Code downloaded from http://www.ecsl.cs.sunysb.edu/TBBT/.
--- /dev/null
+1. TBBT Directory Structure
+
+# README: this file. Introduce the TBBT directory and how to use TBBT.
+# trace_init: source code for extract file system hierarchy from the trace
+# trace_play: source code for trace player
+# test: example of play the EECS Oct 21 1 hour trace. It contains the
+# original trace file: anon-lair62-011021-0000.txt
+# file system hierarchy map: fh-path-map-play
+# file system hierarchy: RFSFS
+#
+
+2. How to use TBBT?
+
+#STEP1: compile the TBBT player, change the executable "sfs3" to be owned
+# by root
+
+cd ~nzhu/TBBT/trace_play
+make sfs3
+su - root; chown root:root tplay; exit
+
+#STEP2: copy or link trace file in a directory for trace play
+
+cd TESTDIR
+ln -s ../test/anon-lair62-011021-0000.txt anon-lair62-011021-0000.txt
+
+#STEP3: extract the file system hierarchy from the trace file
+# There are two outputs: fh-path-map-play and RFSFS
+# fh-path-map-play is the file system hierarchy map
+# RFSFS is the actually file system hierarchy, instead of writing
+# each file to the full length, -S option creates a file system
+# hierarchy where all files are of 0 length. This is useful when
+# for experimental test run or debug run because writing all files
+# to the full length could be time consuming.
+
+~nzhu/TBBT/trace_init/extract-hierarchy anon-lair62-011021-0000.txt [-S]
+
+#STEP4: copy RFSFS to an exported directory on NFS file server
+
+scp -r RFSFS server:/export_dir/
+
+#STEP5: pair-up the request and reply packets in the trace.
+# The output file name is based on input file with suffix ".pair"
+# in this example, anon-lair62-011021-0000.txt.pair
+
+sfs3 -pair_trace anon-lair62-011021-0000.txt
+
+
+#STEP6: play the trace against initial file system hierarchy (RFSFS) on server
+
+sfs3 hostname:/export_dir/RFSFS anon-lair62-011021-0000.txt.pair fh-path-map-play 1 0
--- /dev/null
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# $Id: EXAMPLES,v 1.3 2003/07/28 14:27:16 ellard Exp $\r
+#\r
+# For nfsscan version 0.10a (dated 7/25/2003).\r
+\r
+INTRODUCTION\r
+\r
+The usual procedure for analyzing a trace is the following:\r
+\r
+ 1. Use nfsscan to produce a tabularized summary of each\r
+ 300-second segment of the trace. For these examples,\r
+ we'll call this DEFAULT_TABLE.\r
+\r
+ Depending on what you're looking for, the default\r
+ settings of nfsscan might not provide all the info\r
+ you're going to want in the next step. The default is\r
+ to omit per-client, per-user, per-group, and per-file\r
+ stats and only tally total operation counts. [THE\r
+ DEFAULTS MAY CHANGE IN A FUTURE RELEASE.]\r
+\r
+ 2. Use ns_timeagg to create a summary of activity in the\r
+ entire trace named SUMMARY_TABLE from DEFAULT_TABLE.\r
+\r
+ Note that almost anything ns_timeagg and ns_split can\r
+ do can also be done directly with nfsscan. However,\r
+ the implicit goal of ns_timeagg and ns_split is to\r
+ AVOID re-running nfsscan. It is much faster to\r
+ re-process a table created by nfsscan than it is to\r
+ re-create the table -- the input to nfsscan is\r
+ typically several million (or billion) lines of trace\r
+ data, while the output is usually only a few thousand\r
+ table rows.\r
+\r
+ 3. Use ns_quickview to plot interesting aspects of the\r
+ DEFAULT_TABLE and/or SUMMARY_TABLE.\r
+\r
+ 4. [optional] Use ns_split and/or ns_tsplit to isolate\r
+ interesting parts of DEFAULT_TABLE (such as per-client\r
+ or per-user counts). Repeat steps 2 and 3 with the\r
+ results.\r
+\r
+ 5. [optional] If steps 2-4 found anything interesting, re-run\r
+ nfsscan with new parameters to take a closer look at\r
+ the trace.\r
+\r
+Examples and discussion of these steps and related topics is given\r
+below.\r
+\r
+For these examples, TRACE is a trace file gathered by nfsdump (or\r
+another tool that creates traces files in the same format), and TABLE.ns\r
+is a file created by nfsscan from TRACE. The suffix ".ns" is also\r
+used to denote files that contain tables created by nfsscan,\r
+ns_timeagg, ns_split, and ns_tsplit. Example commandlines always\r
+begin with "%".\r
+\r
+1. RUNNING NFSSCAN\r
+\r
+2. CREATING A SUMMARY TABLE\r
+\r
+ To compute a table contsisting of a single row with counts for\r
+ each operation tallied by the nfsscan run, aggregate over time\r
+ with a time length of zero. (Zero is treated as a special\r
+ time length that includes the entire input table.)\r
+\r
+ % ns_timeagg -t0 TABLE.ns > SUMMARY.ns\r
+\r
+ Note that timeagg will always aggregate over every (except\r
+ time) attribute, so it does not matter whether or not the\r
+ TABLE.ns contains per-client, per-user, per-group, or per-file\r
+ data. The sum will always be the same.\r
+\r
+ On the other hand, if you want to prevent ns_timeagg from\r
+ aggregating over a particular attribute, specify that\r
+ attribute in the same manner as with nfsscan. For example, to\r
+ create a table with a single row containing the operation\r
+ count per user:\r
+\r
+ % ns_timeagg -t0 -BU TABLE.ns > SUMMARY.ns\r
+\r
+ Of course, ns_timeagg cannot create data out of thin air. If\r
+ TABLE.ns does not contain per-user information then -BU will\r
+ have no effect.\r
+\r
+3. PLOTTING THE DATA\r
+\r
+ To simply plot the total operation count:\r
+\r
+ % ns_quickview TABLE.ns\r
+ % gv qv.ps\r
+\r
+WHICH CLIENT REQUESTS THE MOST OPERATIONS?\r
+\r
+Method: use nfsscan to tally the per-client operation counts for the\r
+ entire trace file (by using -t0), and then sort by the TOTAL\r
+ op count fields:\r
+\r
+ If TABLE contains per-client information, then this is easy:\r
+\r
+ % ns_timeagg -t0 -BC TABLE | grep -v '^#' \\r
+ | awk '{print $7, $3}' | sort -nr\r
+\r
+ If TABLE does not contain per-client info, then it's necessary\r
+ to re-run nfsscan:\r
+\r
+ % nfsscan -t0 -BC TRACE | grep -v '^#' \\r
+ | awk '{print $7, $3}' | sort -nr\r
+\r
+ The output from either command is a two-column table. The\r
+ first column is the total operation count of each client, and\r
+ the second column is the ID of each client.\r
+\r
+WHICH CLIENT DOES THE MOST READING?\r
+\r
+ If we've already got TABLE, and it contains per-client info,\r
+ then the easiest way is to simply use extract the read count\r
+ column (instead of the TOTAL column) from TABLE:\r
+\r
+ % ns_timeagg -t0 -BC TABLE | grep -v '^#' \\r
+ | awk '{print $9, $3}' | sort -nr\r
+\r
+ Or, we can nfsscan. Because we're not interested in anything\r
+ except the read count, we can change the list of operations\r
+ that nfsscan tabulates so that it only counts reads. (Of course,\r
+ the resulting table is useless for anything except answering\r
+ this particular question, and since nfsscan is expensive to run\r
+ this is probably wasteful.)\r
+\r
+ % nfsscan -t0 -BC -Oread -i TRACE | grep -v '^#' \\r
+ | awk '{print $7, $3}' | sort -nr\r
+\r
+WHICH CLIENT DOES THE MOST FSSTATS?\r
+\r
+ fsstat is not ordinarily tabulated by nfsscan. To tell nfsscan\r
+ to keep track of it, we can change the list of operations to consist\r
+ only of fsstat:\r
+\r
+ % nfsscan -t0 -BC -Ofsstat -i TRACE | ...\r
+\r
+ As mentioned in the previous example, it is often wasteful to\r
+ run nfsscan just to get one number. Another approach is to\r
+ add fsstat to the default list of "interesting" operations, by\r
+ using "+" at the start of the operation list. This tells nfsscan\r
+ to append the given list of operations to the default list:\r
+\r
+ % nfsscan -t0 -BC -O+fsstat -i TRACE | ...\r
+\r
+ An implication of this is that it's impossible to know what\r
+ each column in the table represents unless you know what\r
+ operations were considered "interesting" for each run of\r
+ nfsscan. To help with this, nfsscan includes the commandline\r
+ and column titles at the start of each file it creates.\r
+\r
+WHICH USER DOES THE MOST READING?\r
+\r
+ This is exactly like the previous example, except that we use\r
+ -BU instead -BC, to do everything per-user instead of\r
+ per-client.\r
+\r
+WHAT DIRECTORIES ARE HOTTEST?\r
+\r
+ Use the -d option to find the cummulative number of operations\r
+ per directory, then sort the results by operation count. In\r
+ order to avoid drowning in data you might choose to print\r
+ print only the top 100:\r
+\r
+ % nfsscan -i TRACE -t0 -d | grep '^D' \\r
+ | awk '{print $7, $5}' | sort -nr | head -100\r
+\r
--- /dev/null
+# $Id: INSTALL,v 1.4 2003/07/28 14:27:16 ellard Exp $\r
+\r
+NFSSCAN INSTALLATION INSTRUCTIONS\r
+\r
+This is version 0.10a of nfsscan, dated 7/25/2003.\r
+\r
+THIS IS A PRELIMINARY RELEASE OF NEW SOFTWARE:\r
+\r
+- THE COMMANDLINE FORMATS MAY EVOLVE RAPIDLY OVER THE NEXT SEVERAL\r
+ RELEASES.\r
+\r
+- DEBUGGING MESSAGES AND WARNINGS MAY APPEAR ON STDERR. MOST OF THESE\r
+ ARE MEANINGLESS, BUT IF YOU ENCOUNTER A PROBLEM WITH THE\r
+ PROGRAM PLEASE INCLUDE THEM IN YOUR BUG REPORT.\r
+ \r
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\r
+\r
+System Requirements:\r
+\r
+ - You must have PERL 5 (5.005 or later) installed in order to\r
+ use any of these tools. (None of these tools have\r
+ been tested with PERL 6 or later.)\r
+\r
+ - If you plan to use ns_quickview, make sure that gnuplot is\r
+ installed on your system.\r
+ \r
+ ns_quickview has been tested with gnuplot 3.7 and\r
+ should work with later versions. If you have problems\r
+ with ns_quickview, please include any diagnostics\r
+ messages from gnuplot and the version of gnuplot with\r
+ your bug report.\r
+\r
+ If gnuplot is not in your path, you must edit\r
+ ns_quickview to set GNUPLOT_PATH to the appropriate\r
+ path after step 2 in the installation instructions.\r
+\r
+Installation:\r
+\r
+1. Un-tar the distribution into an empty directory.\r
+\r
+2. Edit the first line of nfsscan and ns_* to point to wherever your\r
+ favorite version of PERL 5 is installed. If you don't like\r
+ looking at debugging messages, remove the -w from the\r
+ invocation of PERL.\r
+\r
+3. Make sure that nfsscan and ns_* are executable and that\r
+ all the files are publically readable:\r
+\r
+ chmod 444 *\r
+ chmod 555 nfsscan ns_*\r
+\r
+4. Read NOTES.TXT for last-minute info or errata for this release,\r
+ and changes from previous releases.\r
+\r
+5. Read nfsscan.txt and EXAMPLES for more information about how to\r
+ use the tools.\r
+\r
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\r
+\r
+PLEASE report bugs, problems or suggestions for improvements to\r
+ellard@eecs.harvard.edu.\r
--- /dev/null
+# $Id: NOTES.TXT,v 1.2 2003/07/28 14:27:16 ellard Exp $
+
+NFSSCAN BUGS
+
+Known bugs for version 0.10a:
+
+- Debugging messages and warnings may appear on stderr. Most of these
+ are meaningless, but if you encounter a problem with any of
+ the programs program please include them in your bug report.
+
+ Remove the -w from the invocation of PERL (on the first line
+ of each script) if you want to get rid of the PERL warnings.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+PLEASE report bugs, problems or suggestions for improvements to
+ellard@eecs.harvard.edu.
--- /dev/null
+#!/bin/bash -x
+
+# to locate a program
+GUNZIP=`whereis gunzip | gawk '{print $2}'`
+echo $GUNZIP
+
+# to run nfsscan
+gunzip -c traces/* | ./nfsscan -f -t0 -o result-total/test > result-total/output 2>&1
+gunzip -c ~traces/anon-lair62-011130-1100.txt.gz | ../nfsscan.pl > output 2>&1
+
+
+
+# post processing of nfsscan
+gawk '{if ($1=="F") print $2, $3, $5, $6 }' output > output-filename
+gawk '{if ($3!=".") print $0 }' output-filename > output-filename-known
+
+echo file#; gawk '{if ($1=="F") print $0}' output-filename-known | wc
+echo dir#; gawk '{if ($1=="D") print $0}' output-filename-known | wc
+echo active#; gawk '{if ($2=="A") print $0}' output-filename-known | wc
+echo dead#; gawk '{if ($2=="D") print $0}' output-filename-known | wc
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_timeagg,v 1.4 2003/07/10 20:13:31 ellard Exp $\r
+\r
+# Allows rounding of the start time, so that minor clock drift doesn't\r
+# make trace periods appear to begin at funny times (i.e. one or two\r
+# seconds after midnight, instead of midnight).\r
+#\r
+# This is only meant to help with SMALL amounts of drift. Trying to\r
+# use this to correct for anything more than a small fraction of the\r
+# sample interval is asking for trouble.\r
+\r
+require 'timelocal.pl';\r
+\r
+sub findStartTime {\r
+ my ($time, $rounding) = @_;\r
+\r
+ my $new_time = int ($time);\r
+\r
+ if (defined $rounding && $rounding != 0) {\r
+ $rounding = int ($rounding);\r
+\r
+ my $leftover = $new_time % $rounding;\r
+\r
+ if ($leftover > $rounding / 2) {\r
+ $new_time += $rounding;\r
+ }\r
+\r
+ $new_time -= ($new_time % $rounding);\r
+ }\r
+\r
+ return $new_time;\r
+}\r
+\r
+1;\r
--- /dev/null
+#\r
+# Copyright (c) 2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: counts.pl,v 1.9 2003/07/28 14:27:16 ellard Exp $\r
+\r
+package counts;\r
+\r
+@OpList = ();\r
+%KeysSeen = ();\r
+\r
+%OpCounts = ();\r
+\r
+sub init {\r
+ my (@oplist) = @_;\r
+\r
+ @OpList = @oplist;\r
+}\r
+\r
+sub printTitle {\r
+ my ($out) = @_;\r
+\r
+ my $str = "#C time client fh euid egid";\r
+\r
+ foreach my $op ( @OpList ) {\r
+ $str .= " $op";\r
+ }\r
+ $str .= "\n";\r
+\r
+ print $out $str;\r
+} \r
+\r
+sub printOps {\r
+ my ($start_time, $out) = @_;\r
+ my ($k, $str, $op, $nk);\r
+\r
+ my @allkeys = sort keys %KeysSeen;\r
+\r
+ foreach $k ( @allkeys ) {\r
+ my $tot = "$k,TOTAL";\r
+\r
+ if ($main::OMIT_ZEROS &&\r
+ (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) {\r
+ next;\r
+ }\r
+\r
+ $str = sprintf ("C %s %s", $start_time, &key::key2str ($k));\r
+\r
+ foreach $op ( @OpList ) {\r
+ $nk = "$k,$op";\r
+ if (exists $OpCounts{$nk}) {\r
+ if ($op eq 'readM' || $op eq 'writeM') {\r
+ $str .= sprintf (" %.3f",\r
+ $OpCounts{$nk} / (1024 * 1024));\r
+ }\r
+ else {\r
+ $str .= " $OpCounts{$nk}"\r
+ }\r
+ }\r
+ else {\r
+ $str .= " 0";\r
+ }\r
+ }\r
+ $str .= "\n";\r
+ print $out $str;\r
+ }\r
+}\r
+\r
+sub resetOpCounts {\r
+\r
+ # Clear the counts on everything we've seen.\r
+\r
+ foreach my $op ( keys %OpCounts ) {\r
+ $OpCounts{$op} = 0;\r
+ }\r
+}\r
+\r
+1;\r
+\r
--- /dev/null
+#Usage extract-hierarchy trace_file [-S]
+#nfsscan $1
+echo nfsscan $1
+rfs.pl $2
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $\r
+#\r
+# hier.pl - Tools to map out the file system hierarchy. This is\r
+# accomplished by snooping out the lookup calls.\r
+#\r
+# This is expensive because the hierarchy can require a LOT of space\r
+# to store for a large system with lots of files (especially if files\r
+# come and go). Don't construct the hierarchy unless you want it --\r
+# and be prepared to prune it from time to time.\r
+\r
+package hier;\r
+\r
+# Tables used by the outside world:\r
+\r
+%fh2Parent = ();\r
+%fh2Name = ();\r
+%fh2Attr = ();\r
+%fh2AttrOrig = ();\r
+%parent2fh = ();\r
+\r
+#RFS: init FS\r
+%rootsName = ();\r
+%discardFHs = ();\r
+%rootsFHs = ();\r
+#RFS: dependency table\r
+%fhCreate = ();\r
+\r
+%rfsAllFHs = ();\r
+%fhType = (); # we use %fhIsDir instead\r
+$rfsLineNum = 0;\r
+\r
+\r
+\r
+\r
+\r
+\r
+# Library-private tables and variables.\r
+\r
+%pendingCallsXIDnow = ();\r
+%pendingCallsXIDfh = ();\r
+%pendingCallsXIDname = ();\r
+\r
+$nextPruneTime = -1;\r
+$PRUNE_INTERVAL = 5 * 60; # Five minutes.\r
+\r
+sub processLine {\r
+ my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_;\r
+\r
+ &addRfsAllFHs($line, $proto, $op, $uxid,\r
+ $now, $response, $fh_type);\r
+\r
+ if ($now > $nextPruneTime) {\r
+ &prunePending ($now - $PRUNE_INTERVAL);\r
+ $nextPruneTime = $now + $PRUNE_INTERVAL;\r
+ }\r
+\r
+ my $uxid = "$client-$xid";\r
+\r
+ # 'lookup', 'create', 'rename', 'delete',\r
+ # 'getattr', 'setattr'\r
+\r
+ #RFS: add mkdir/rmdir/symlink\r
+ if ( $op eq 'lookup' || $op eq 'create' || $op eq 'mkdir' || \r
+ ($op eq 'symlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) {\r
+ return (&doLookup ($line, $proto, $op, $uxid,\r
+ $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'rename') {\r
+ }\r
+ elsif ($op eq 'remove' || $op eq 'rmdir') {\r
+ # RFS: why remove these entries? Just let them exist since \r
+ # there is generation number available to distinguish btw removed dir/file \r
+ # and new dir/file with the same inode number.\r
+ #return (&doRemove ($line, $proto, $op, $uxid,\r
+ # $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' ||\r
+ ($op eq 'readlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) {\r
+ return (&doGetAttr ($line, $proto, $op, $uxid,\r
+ $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'setattr') {\r
+ }\r
+}\r
+\r
+\r
+# get time stamp\r
+\r
+sub getTimeStamp{\r
+ my ($line) = @_;\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ print "getTimeStamp return undef\n";\r
+ return undef;\r
+ }\r
+ else {\r
+ my @l = split (' ', $line, 2);\r
+ return $l[0];\r
+ }\r
+}\r
+\r
+sub addRfsAllFHs {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ my $fh = undef;\r
+ \r
+ my $checkfh = undef;\r
+ $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh');\r
+ if (defined $checkfh) {\r
+ $fh = nfsd::nfsDumpCompressFH ($fh_type, $checkfh);\r
+ }\r
+ \r
+ my $fh2 = undef;\r
+\r
+ if ($op eq 'rename' || $op eq 'link') {\r
+ $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh2');\r
+ if (defined $checkfh) {\r
+ $fh2 = nfsd::nfsDumpCompressFH ($fh_type, $checkfh);\r
+ } \r
+ }\r
+\r
+ if (defined $fh) {\r
+ # record the first appearance of the fh\r
+ if ( !exists $rfsAllFHs{$fh} ) {\r
+ $rfsAllFHs{$fh} = $rfsLineNum ;\r
+ }\r
+ }\r
+\r
+ if (defined $fh2) {\r
+ if ( !exists $rfsAllFHs{$fh2} ) {\r
+ $rfsAllFHs{$fh2} = $rfsLineNum;\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+\r
+sub doLookup {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+ my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+ # All directories have (at least) three names: the\r
+ # given name, and "." and "..". We're only interested\r
+ # in the given name.\r
+\r
+ if ($name eq '"."' || $name eq '".."') {\r
+ return ;\r
+ }\r
+\r
+ my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $fh;\r
+ $pendingCallsXIDname{$uxid} = $name;\r
+ }\r
+ elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = $pendingCallsXIDfh{$uxid};\r
+ my $name = $pendingCallsXIDname{$uxid};\r
+\r
+ delete $pendingCallsXIDnow{$uxid};\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDname{$uxid};\r
+\r
+ if ($response eq 'OK') {\r
+ my $cfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ my $type = nfsd::nfsDumpParseLineField ($line, 'ftype');\r
+\r
+ #if ($type == 2) \r
+ {\r
+ $fhIsDir{$cfh} = $type;\r
+ }\r
+\r
+ # Original code\r
+ # $fh2Parent{$cfh} = $pfh;\r
+ # $fh2Name{$cfh} = $name;\r
+ # $parent2fh{"$pfh,$name"} = $cfh;\r
+ # RFS code: in case of the rename, we will record the name of the old name\r
+ $fh2Parent{$cfh} = $pfh;\r
+ if (! exists $fh2Name{$cfh}) {\r
+ $fh2Name{$cfh} = $name;\r
+ $parent2fh{"$pfh,$name"} = $cfh;\r
+ } else {\r
+ # keep the old name in the fh2Name{$cfh}\r
+ # and we also add the newname and pfh mapping\r
+ #$fh2Name{$cfh} = $name;\r
+ $parent2fh{"$pfh,$name"} = $cfh;\r
+ }\r
+\r
+ my ($size, $mode, $atime, $mtime, $ctime, $nlink) =\r
+ nfsd::nfsDumpParseLineFields ($line,\r
+ 'size', 'mode',\r
+ 'atime', 'mtime', 'ctime', 'nlink');\r
+ my $ts = getTimeStamp($line);\r
+\r
+ # RFS: modify here to get/maintain more file attributes\r
+ # we can just check the ctime and compare it with trace-start-time\r
+ # to decide whether to create a file/diretory.\r
+ # atime - last access time of the file\r
+ # mtime - last modification time of the file\r
+ # ctime - last file status change time\r
+ \r
+ #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime";\r
+ if (! exists $fh2AttrOrig{$cfh} ) {\r
+ $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+ }\r
+ $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+ }\r
+\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub doRemove {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+ my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+ # All directories have (at least) three names: the\r
+ # given name, and "." and "..". We're only interested\r
+ # in the given name.\r
+\r
+ if ($name eq '"."' || $name eq '".."') {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ if (! exists $parent2fh{"$pfh,$name"}) {\r
+ return ;\r
+ }\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $pfh;\r
+ $pendingCallsXIDname{$uxid} = $name;\r
+ }\r
+ elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = $pendingCallsXIDfh{$uxid};\r
+ my $name = $pendingCallsXIDname{$uxid};\r
+\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDname{$uxid};\r
+ delete $pendingCallsXIDnow{$uxid};\r
+\r
+ if (! exists $parent2fh{"$pfh,$name"}) {\r
+ return ;\r
+ }\r
+\r
+ my $cfh = $parent2fh{"$pfh,$name"};\r
+\r
+ if ($response eq 'OK') {\r
+ if ($op eq 'remove') {\r
+ printFileInfo ($cfh, 'D');\r
+\r
+ delete $fh2Parent{$cfh};\r
+ delete $fh2Name{$cfh};\r
+ delete $fh2Attr{$cfh};\r
+ delete $fhs2AttrOrig{$cfg};\r
+ delete $parent2fh{"$pfh,$name"};\r
+ }\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub doGetAttr {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ #if (nfsd::nfsDumpParseLineField ($line, 'fh')\r
+ # eq '00018961-57570100-d2440000-61890100') {\r
+ # printf STDERR "Seen it ($op)\n";\r
+ #}\r
+\r
+ if (! defined $fh) {\r
+ return ;\r
+ }\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $fh;\r
+# RFS debug code\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($fh eq $wantfh) {\r
+# print "JIAWU: doGetAttr call $wantfh\n";\r
+#}\r
+ }\r
+ else {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $fh = $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDnow{$uxid};\r
+# RFS debug code\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($fh eq $wantfh) {\r
+# print "JIAWU: doGetAttr response $wantfh\n";\r
+#}\r
+\r
+ if ($response ne 'OK') {\r
+ return ;\r
+ }\r
+\r
+ my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype');\r
+ if (!defined $ftype) {\r
+ print STDERR "BAD $line";\r
+ return ;\r
+ }\r
+\r
+ #if ($ftype == 2) \r
+ {\r
+ $fhIsDir{$fh} = $ftype;\r
+ }\r
+\r
+ #RFS comment: here if fh is a directory, then it will not be add \r
+ # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name\r
+ # if ($ftype != 1) {\r
+ # return ;\r
+ #}\r
+ if ($ftype != 1) {\r
+ #return ;\r
+ }\r
+\r
+\r
+ my ($mode, $size, $atime, $mtime, $ctime, $nlink) =\r
+ nfsd::nfsDumpParseLineFields ($line,\r
+ 'mode', 'size', 'atime', 'mtime', 'ctime', 'nlink');\r
+ my $ts = getTimeStamp($line);\r
+\r
+ # RFS: modify here to get/maintain more file attributes\r
+ # we can just check the ctime and compare it with trace-start-time\r
+ # to decide whether to create a file/diretory.\r
+ # atime - last access time of the file\r
+ # mtime - last modification time of the file\r
+ # ctime - last file status change time\r
+\r
+ # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime";\r
+\r
+ if (! exists $fh2AttrOrig{$fh} ) {\r
+ $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+ }\r
+ $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts";\r
+ }\r
+}\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago). This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+ \r
+sub prunePending {\r
+ my ($when) = @_;\r
+\r
+ foreach my $uxid ( keys %pendingCallsXIDnow ) {\r
+ if ($pendingCallsXIDnow{$uxid} < $when) {\r
+# RFS debug code\r
+my $fh = $pendingCallsXIDfh{$uxid};\r
+my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+if ($fh eq $wantfh) {\r
+ print "JIAWU: prunePending $wantfh\n";\r
+}\r
+#enf RFS\r
+ delete $pendingCallsXIDnow{$uxid};\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+# Return as much of the path for the given fh as possible. It may or\r
+# may not reach the root (or the mount point of the file system), but\r
+# right now we don't check. Usually on busy systems the data is\r
+# complete enough so that most paths are complete back to the mount\r
+# point.\r
+\r
+sub findPath {\r
+ my ($fh) = @_;\r
+ my $isdir = 0;\r
+ my $cnt = 0;\r
+ my $MaxPathLen = 40;\r
+\r
+ if (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) {\r
+ $isdir = 1;\r
+ }\r
+\r
+ my @path = ();\r
+ while ($fh && exists $fh2Name{$fh}) {\r
+ unshift (@path, $fh2Name{$fh});\r
+\r
+ if ( ($fh2Name{$fh} ne '"RFSNN0"' ) ) {\r
+ if (! exists $fh2Parent{$fh}) {\r
+ print STDERR "$fh2Name{$fh} ";\r
+ if ( ($fh2Name{$fh} eq '"RFSNN0"' ) ) {\r
+ print STDERR "eq RFSNN0\n";\r
+ } else {\r
+ print STDERR "NOT eq RFSNN0\n";\r
+ }\r
+ }\r
+ if ($fh eq $fh2Parent{$fh}) {\r
+ unshift (@path, '(LOOP)');\r
+ last;\r
+ }\r
+ }\r
+\r
+ if ($cnt++ > $MaxPathLen) {\r
+ print STDERR "findPath: path too long (> $MaxPathLen)\n";\r
+ unshift (@path, '(TOO-LONG)');\r
+ last;\r
+ }\r
+\r
+ $fh = $fh2Parent{$fh};\r
+ }\r
+\r
+ # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory)\r
+ if ($fh && !exists $fh2Name{$fh} && (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ) {\r
+ if (exists $rootsName{$fh}) {\r
+ #print "JIAWU: $rootsName{$fh}\n";\r
+ unshift(@path, $rootsName{$fh});\r
+ } else {\r
+ print "JIAWU: WARNING! No rootsName for this fh: $fh\n";\r
+ unshift(@path, $fh);\r
+ }\r
+ } else {\r
+ if ($fh && !exists $fh2Name{$fh} && (!exists $fhIsDir{$fh} || (exists $fhIsDir{$fh} && $fhIsDir{$fh}!=2)) ) {\r
+ if (exists $discardFHs{$fh}) {\r
+ open NOATTRDIR, ">>noattrdirdiscard" || die "open noattrdirdiscard failed\n";\r
+ print NOATTRDIR "$fh DISCARD\n";\r
+ close NOATTRDIR;\r
+ } else {\r
+ # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0\r
+ unshift(@path, '"RFSNN0"');\r
+ $fhIsDir{$fh}=2;\r
+ $fh2Name{$fh} = '"RFSNN0"';\r
+ $rootsName{$fh} = '"RFSNN0"';\r
+ open NOATTRDIR, ">>noattrdir-root";\r
+ print NOATTRDIR "$fh /RFSNN0/\n";\r
+ close NOATTRDIR;\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ my $str = '';\r
+ $cnt = 0;\r
+ foreach my $p ( @path ) {\r
+ $p =~ s/^.//;\r
+ $p =~ s/.$//;\r
+ $str .= "/$p";\r
+ $cnt++;\r
+ }\r
+\r
+ if ($isdir) {\r
+ $str .= '/';\r
+ }\r
+\r
+ if ($cnt == 0) {\r
+ $str = '.';\r
+ }\r
+\r
+ return ($str, $cnt);\r
+}\r
+\r
+\r
+$total_unknown_fh = 0;\r
+$total_known_fh = 0;\r
+\r
+sub printAll {\r
+ my ($start_time, $out) = @_;\r
+\r
+ my %allfh = ();\r
+ my $fh;\r
+ my $u = 0;\r
+ my $k = 0;\r
+\r
+ # RFS print more information here\r
+ open (OUT_RFS, ">rfsinfo") ||\r
+ die "Can't create $OutFileBaseName.rfs.";\r
+ \r
+ foreach $fh ( keys %fh2Attr ) {\r
+ $allfh{$fh} = 1; \r
+ }\r
+ foreach $fh ( keys %fh2Name ) {\r
+ $allfh{$fh} = 1; \r
+ }\r
+\r
+ #RFS: before printFileInfo, name those roots' name\r
+\r
+ #RFS there are three kind of fh\r
+ # 1. fh/name paired (fh/attr must)\r
+ # 2. fh/attr but no fh/name: type file (discard related operations)\r
+ # 3. fh/attr but no fh/name: type dir (keep as persuedo root)\r
+ $u = $k = 0;\r
+ my $sn=1;\r
+ foreach $fh ( keys %allfh ) {\r
+ if (exists $fh2Parent{$fh} ) {\r
+ $k++;\r
+ }\r
+ else {\r
+ $u++;\r
+ my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+ if ($type eq 'D') {\r
+ $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++);\r
+ $rootsFHs{$fh} = 2;\r
+ }\r
+ else {\r
+ $discardFHs{$fh} = 1;\r
+ }\r
+ }\r
+ }\r
+ print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n";\r
+ $u = keys %rootsFHs;\r
+ print OUT_RFS "#RFS: root fh list($u)\n";\r
+ foreach $fh (keys %rootsName) {\r
+ print OUT_RFS "#RFS: $rootsName{$fh} $fh\n";\r
+ }\r
+ $u = keys %discardFHs;\r
+ print OUT_RFS "#RFS: discard fh list($u)\n";\r
+ print OUT_RFS join("\n", keys %discardFHs, "");\r
+ \r
+\r
+ print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n";\r
+\r
+ print $out "#T starttime = $start_time\n";\r
+ foreach $fh ( keys %allfh ) {\r
+ printFileInfoOutputFile ($fh, 'A', $out);\r
+ }\r
+ \r
+ my $numfh2Name = keys %fh2Name;\r
+ my $numfh2Attr = keys %fh2Attr;\r
+ print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n";\r
+\r
+ \r
+ $u = $k = 0;\r
+ foreach $fh ( keys %allfh ) {\r
+ if ( exists $fh2Name{$fh} ) {$k++;}\r
+ else {$u++;}\r
+ }\r
+ print OUT_RFS "#stat: total fh with name = $k, without name = $u\n";\r
+\r
+ print OUT_RFS "#stat: finally, total known fh = $total_known_fh, unknown = $total_unknown_fh\n";\r
+\r
+# Note: fh with name (8303), fh without name (103)\r
+# root fh list: 18\r
+# discard fh list: 85\r
+# known fh (8321): ( fh with name(8303) + root fh list (18) = 8321)\r
+# unknown fh (85)\r
+#\r
+# All fh from the those data structures: 8321 + 85 = 8303+103\r
+# Or, in keys %allfh\r
+#\r
+# \r
+ print OUT_RFS "#RFS\n";\r
+ close OUT_RFS; \r
+\r
+ open (MISSED, ">missdiscardfh") ||\r
+ die "Can't create missdiscardfh.";\r
+ foreach $fh (keys %rfsAllFHs) {\r
+ if ( !exists $allfh{$fh} && \r
+ ( (defined $fh2Name{$fh}) && ($fh2Name{$fh} ne '"RFSNN0"')) ) {\r
+ print MISSED "$fh LN: $rfsAllFHs{$fh}\n"\r
+ }\r
+ }\r
+ close MISSED;\r
+\r
+# check for a special fh\r
+#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+#if ($allfh{$wantfh} == 1) {\r
+# print OUT_RFS "JIAWU: found $wantfh\n";\r
+#} else {\r
+# print OUT_RFS "JIAWU: NOT found $wantfh\n";\r
+#}\r
+#foreach $fh ( keys %allfh ) {\r
+# if ( $fh eq $wantfh ) {\r
+# print OUT_RFS "JIAWU: found $wantfh\n";\r
+# printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS);\r
+# last;\r
+# }\r
+#}\r
+#print OUT_RFS "JIAWU: after \n";\r
+\r
+\r
+}\r
+\r
+sub printFileInfoOutputFile {\r
+ my ($fh, $state, $out) = @_;\r
+\r
+ my ($p, $c) = findPath ($fh);\r
+ \r
+ if ($c == 0) {$total_unknown_fh++;}\r
+ else {$total_known_fh++;}\r
+ \r
+ #my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+ my $type = $fhIsDir{$fh};\r
+ if (!defined $type) \r
+ {\r
+ print STDERR "unknown ftype(U) for fh: $fh\n"; \r
+ $type = 'U';\r
+ }\r
+ my $attr = (exists $fh2Attr{$fh}) ?\r
+ $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+ my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+ $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+\r
+ print $out "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+sub printFileInfo {\r
+ my ($fh, $state) = @_;\r
+\r
+ my ($p, $c) = findPath ($fh);\r
+ \r
+ if ($c == 0) {$total_unknown_fh++;}\r
+ else {$total_known_fh++;}\r
+ \r
+ my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F';\r
+ my $attr = (exists $fh2Attr{$fh}) ?\r
+ $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+ my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+ $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1";\r
+\r
+ print "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+#\r
+# The flow to create the dependency table\r
+# \r
+# create(dirfh, name, attr) -->newfh, new attr\r
+# mkdir(dirfh, name, attr) -> newfh, new attr\r
+#\r
+# remove(dirfh, name) --> status\r
+# rmdir(dirfh, name) --> status\r
+# rename(dirfh, name, todirfh, toname) --> status\r
+#\r
+# link(newdirfh, newname, dirfh, name) --> status (newdir/newname=>dir/name)\r
+# syslink(newdirfh, newname, string) --> status (newdir/newname=>"string")\r
+# readlink(fh) --> string\r
+# lookup(dirfh, name) --> fh, attr\r
+# getattr(fh) --> attr\r
+# setattr(fh, attr) --> attr\r
+# read(fh, offset, count) -> attr, data\r
+# write(fh, offset, count, data) --> attr\r
+# readdir(dirfh, cookie, count) --> entries\r
+# statfs(fh) --> status\r
+#\r
+#\r
+#\r
+#\r
+# for each line the trace file: \r
+# if (op == R2 or R3) continue; #skip the response line\r
+# switch (the op)\r
+# {\r
+# # CREATION OPs:\r
+# case create:\r
+# case remove:\r
+# # DELETE OPs:\r
+# case mkdir:\r
+# case rmdir:\r
+# # other OPs\r
+#\r
+#\r
+#\r
+#\r
+#\r
+1;\r
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $\r
+#\r
+# hier.pl - Tools to map out the file system hierarchy. This is\r
+# accomplished by snooping out the lookup calls.\r
+#\r
+# This is expensive because the hierarchy can require a LOT of space\r
+# to store for a large system with lots of files (especially if files\r
+# come and go). Don't construct the hierarchy unless you want it --\r
+# and be prepared to prune it from time to time.\r
+\r
+package hier;\r
+\r
+# Tables used by the outside world:\r
+\r
+%fh2Parent = ();\r
+%fh2Name = ();\r
+%fh2Attr = ();\r
+%fh2AttrOrig = ();\r
+%parent2fh = ();\r
+#RFS\r
+%rootsName = ();\r
+%discardFHs = ();\r
+%rootsFHs = ();\r
+\r
+\r
+# Library-private tables and variables.\r
+\r
+%pendingCallsXIDnow = ();\r
+%pendingCallsXIDfh = ();\r
+%pendingCallsXIDname = ();\r
+\r
+$nextPruneTime = -1;\r
+$PRUNE_INTERVAL = 5 * 60; # Five minutes.\r
+\r
+sub processLine {\r
+ my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_;\r
+\r
+ if ($now > $nextPruneTime) {\r
+ &prunePending ($now - $PRUNE_INTERVAL);\r
+ $nextPruneTime = $now + $PRUNE_INTERVAL;\r
+ }\r
+\r
+ my $uxid = "$client-$xid";\r
+\r
+ # 'lookup', 'create', 'rename', 'delete',\r
+ # 'getattr', 'setattr'\r
+\r
+ #RFS: add mkdir/rmdir\r
+ if ($op eq 'lookup' || $op eq 'create' || $op eq 'mkdir') {\r
+ return (&doLookup ($line, $proto, $op, $uxid,\r
+ $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'rename') {\r
+ }\r
+ elsif ($op eq 'remove' || $op eq 'rmdir') {\r
+ # RFS: why remove these entries? Just let them exist since \r
+ # there is generation number available to distinguish btw removed dir/file \r
+ # and new dir/file with the same inode number.\r
+ #return (&doRemove ($line, $proto, $op, $uxid,\r
+ # $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' ) {\r
+ return (&doGetAttr ($line, $proto, $op, $uxid,\r
+ $now, $response, $fh_type));\r
+ }\r
+ elsif ($op eq 'setattr') {\r
+ }\r
+}\r
+\r
+sub doLookup {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+ my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+ # All directories have (at least) three names: the\r
+ # given name, and "." and "..". We're only interested\r
+ # in the given name.\r
+\r
+ if ($name eq '"."' || $name eq '".."') {\r
+ return ;\r
+ }\r
+\r
+ my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $fh;\r
+ $pendingCallsXIDname{$uxid} = $name;\r
+ }\r
+ elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = $pendingCallsXIDfh{$uxid};\r
+ my $name = $pendingCallsXIDname{$uxid};\r
+\r
+ delete $pendingCallsXIDnow{$uxid};\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDname{$uxid};\r
+\r
+ if ($response eq 'OK') {\r
+ my $cfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ my $type = nfsd::nfsDumpParseLineField ($line, 'ftype');\r
+\r
+ if ($type == 2) {\r
+ $fhIsDir{$cfh} = 1;\r
+ }\r
+\r
+ $fh2Parent{$cfh} = $pfh;\r
+ $fh2Name{$cfh} = $name;\r
+ $parent2fh{"$pfh,$name"} = $cfh;\r
+\r
+ my ($size, $mode, $atime, $mtime, $ctime) =\r
+ nfsd::nfsDumpParseLineFields ($line,\r
+ 'size', 'mode',\r
+ 'atime', 'mtime', 'ctime');\r
+\r
+ # RFS: modify here to get/maintain more file attributes\r
+ # we can just check the ctime and compare it with trace-start-time\r
+ # to decide whether to create a file/diretory.\r
+ # atime - last access time of the file\r
+ # mtime - last modification time of the file\r
+ # ctime - last file status change time\r
+ \r
+ #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime";\r
+ if (! exists $fh2AttrOrig{$cfh} ) {\r
+ $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime";\r
+ }\r
+ $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime";\r
+ }\r
+\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub doRemove {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $tag = ($proto eq 'C3') ? 'name' : 'fn';\r
+ my $name = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+ # All directories have (at least) three names: the\r
+ # given name, and "." and "..". We're only interested\r
+ # in the given name.\r
+\r
+ if ($name eq '"."' || $name eq '".."') {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ if (! exists $parent2fh{"$pfh,$name"}) {\r
+ return ;\r
+ }\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $pfh;\r
+ $pendingCallsXIDname{$uxid} = $name;\r
+ }\r
+ elsif ($proto eq 'R3' || $proto eq 'R2') {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $pfh = $pendingCallsXIDfh{$uxid};\r
+ my $name = $pendingCallsXIDname{$uxid};\r
+\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDname{$uxid};\r
+ delete $pendingCallsXIDnow{$uxid};\r
+\r
+ if (! exists $parent2fh{"$pfh,$name"}) {\r
+ return ;\r
+ }\r
+\r
+ my $cfh = $parent2fh{"$pfh,$name"};\r
+\r
+ if ($response eq 'OK') {\r
+ if ($op eq 'remove') {\r
+ printFileInfo ($cfh, 'D');\r
+\r
+ delete $fh2Parent{$cfh};\r
+ delete $fh2Name{$cfh};\r
+ delete $fh2Attr{$cfh};\r
+ delete $fhs2AttrOrig{$cfg};\r
+ delete $parent2fh{"$pfh,$name"};\r
+ }\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub doGetAttr {\r
+ my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ my $fh = nfsd::nfsDumpCompressFH ($fh_type,\r
+ nfsd::nfsDumpParseLineField ($line, 'fh'));\r
+\r
+ #if (nfsd::nfsDumpParseLineField ($line, 'fh')\r
+ # eq '00018961-57570100-d2440000-61890100') {\r
+ # printf STDERR "Seen it ($op)\n";\r
+ #}\r
+\r
+ if (! defined $fh) {\r
+ return ;\r
+ }\r
+\r
+ $pendingCallsXIDnow{$uxid} = $now;\r
+ $pendingCallsXIDfh{$uxid} = $fh;\r
+# RFS debug code\r
+my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+if ($fh eq $wantfh) {\r
+ print "JIAWU: doGetAttr call $wantfh\n";\r
+}\r
+ }\r
+ else {\r
+ if (! exists $pendingCallsXIDnow{$uxid}) {\r
+ return ;\r
+ }\r
+\r
+ my $fh = $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDfh{$uxid};\r
+ delete $pendingCallsXIDnow{$uxid};\r
+# RFS debug code\r
+my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+if ($fh eq $wantfh) {\r
+ print "JIAWU: doGetAttr response $wantfh\n";\r
+}\r
+\r
+ if ($response ne 'OK') {\r
+ return ;\r
+ }\r
+\r
+ my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype');\r
+ if (!defined $ftype) {\r
+ print STDERR "BAD $line";\r
+ return ;\r
+ }\r
+\r
+ if ($ftype == 2) {\r
+ $fhIsDir{$fh} = 1;\r
+ }\r
+\r
+ #RFS comment: here if fh is a directory, then it will not be add \r
+ # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name\r
+ # if ($ftype != 1) {\r
+ # return ;\r
+ #}\r
+ if ($ftype != 1) {\r
+ #return ;\r
+ }\r
+\r
+\r
+ my ($mode, $size, $atime, $mtime, $ctime) =\r
+ nfsd::nfsDumpParseLineFields ($line,\r
+ 'mode', 'size', 'atime', 'mtime', 'ctime');\r
+\r
+ # RFS: modify here to get/maintain more file attributes\r
+ # we can just check the ctime and compare it with trace-start-time\r
+ # to decide whether to create a file/diretory.\r
+ # atime - last access time of the file\r
+ # mtime - last modification time of the file\r
+ # ctime - last file status change time\r
+\r
+ # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime";\r
+\r
+ if (! exists $fh2AttrOrig{$fh} ) {\r
+ $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime";\r
+ }\r
+ $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime";\r
+ }\r
+}\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago). This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+ \r
+sub prunePending {\r
+ my ($when) = @_;\r
+\r
+ foreach my $uxid ( keys %pendingCallsXIDnow ) {\r
+ if ($pendingCallsXIDnow{$uxid} < $when) {\r
+# RFS debug code\r
+my $fh = $pendingCallsXIDfh{$uxid};\r
+my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+if ($fh eq $wantfh) {\r
+ print "JIAWU: prunePending $wantfh\n";\r
+}\r
+#enf RFS\r
+ delete $pendingCallsXIDnow{$uxid};\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+# Return as much of the path for the given fh as possible. It may or\r
+# may not reach the root (or the mount point of the file system), but\r
+# right now we don't check. Usually on busy systems the data is\r
+# complete enough so that most paths are complete back to the mount\r
+# point.\r
+\r
+sub findPath {\r
+ my ($fh) = @_;\r
+ my $isdir = 0;\r
+ my $cnt = 0;\r
+ my $MaxPathLen = 40;\r
+\r
+ if (exists $fhIsDir{$fh}) {\r
+ $isdir = 1;\r
+ }\r
+\r
+ my @path = ();\r
+ while ($fh && exists $fh2Name{$fh}) {\r
+ unshift (@path, $fh2Name{$fh});\r
+ if ($fh eq $fh2Parent{$fh}) {\r
+ unshift (@path, '(LOOP)');\r
+ last;\r
+ }\r
+\r
+ if ($cnt++ > $MaxPathLen) {\r
+ print STDERR "findPath: path too long (> $MaxPathLen)\n";\r
+ unshift (@path, '(TOO-LONG)');\r
+ last;\r
+ }\r
+\r
+ $fh = $fh2Parent{$fh};\r
+ }\r
+\r
+ # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory)\r
+ if ($fh && !exists $fh2Name{$fh} && exists $fhIsDir{$fh}) {\r
+ if (exists $rootsName{$fh}) {\r
+ print "JIAWU: $rootsName{$fh}\n";\r
+ unshift(@path, $rootsName{$fh});\r
+ } else {\r
+ print "JIAWU: WARNING! No rootsName for this fh: $fh\n";\r
+ unshift(@path, $fh);\r
+ }\r
+ } else {\r
+ if ($fh && !exists $fh2Name{$fh} && !exists $fhIsDir{$fh}) {\r
+ if (exists $discardFHs{$fh}) {\r
+ open NOATTRDIR, ">>noattrdirdiscard";\r
+ print NOATTRDIR "$fh DISCARD\n";\r
+ close NOATTRDIR;\r
+ } else {\r
+ # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0\r
+ unshift(@path, '"RFSNN0"');\r
+ $fhIsDir{$fh}=1;\r
+ $fh2Name{$fh} = '"RFSNN0"';\r
+ $rootsName{$fh} = '"RFSNN0"';\r
+ open NOATTRDIR, ">>noattrdir-root";\r
+ print NOATTRDIR "$fh RFSNN0\n";\r
+ close NOATTRDIR;\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ my $str = '';\r
+ $cnt = 0;\r
+ foreach my $p ( @path ) {\r
+ $p =~ s/^.//;\r
+ $p =~ s/.$//;\r
+ $str .= "/$p";\r
+ $cnt++;\r
+ }\r
+\r
+ if ($isdir) {\r
+ $str .= '/';\r
+ }\r
+\r
+ if ($cnt == 0) {\r
+ $str = '.';\r
+ }\r
+\r
+ return ($str, $cnt);\r
+}\r
+\r
+\r
+$total_unknown_fh = 0;\r
+$total_known_fh = 0;\r
+\r
+sub printAll {\r
+ my ($start_time, $out) = @_;\r
+\r
+ my %allfh = ();\r
+ my $fh;\r
+ my $u = 0;\r
+ my $k = 0;\r
+\r
+ # RFS print more information here\r
+ open (OUT_RFS, ">rfsinfo") ||\r
+ die "Can't create $OutFileBaseName.rfs.";\r
+ \r
+ foreach $fh ( keys %fh2Attr ) {\r
+ $allfh{$fh} = 1; \r
+ }\r
+ foreach $fh ( keys %fh2Name ) {\r
+ $allfh{$fh} = 1; \r
+ }\r
+\r
+ #RFS: before printFileInfo, name those roots' name\r
+\r
+ #RFS there are three kind of fh\r
+ # 1. fh/name paired (fh/attr must)\r
+ # 2. fh/attr but no fh/name: type file (discard related operations)\r
+ # 3. fh/attr but no fh/name: type dir (keep as persuedo root)\r
+ $u = $k = 0;\r
+ my $sn=1;\r
+ foreach $fh ( keys %allfh ) {\r
+ if (exists $fh2Parent{$fh} ) {\r
+ $k++;\r
+ }\r
+ else {\r
+ $u++;\r
+ my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
+ if ($type eq 'D') {\r
+ $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++);\r
+ $rootsFHs{$fh} = 1;\r
+ }\r
+ else {\r
+ $discardFHs{$fh} = 1;\r
+ }\r
+ }\r
+ }\r
+ print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n";\r
+ $u = keys %rootsFHs;\r
+ print OUT_RFS "#RFS: root fh list($u)\n";\r
+ foreach $fh (keys %rootsName) {\r
+ print OUT_RFS "#RFS: $rootsName{$fh} $fh\n";\r
+ }\r
+ $u = keys %discardFHs;\r
+ print OUT_RFS "#RFS: discard fh list($u)\n";\r
+ print OUT_RFS join("\n", keys %discardFHs, "");\r
+ \r
+\r
+ print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n";\r
+\r
+ print $out "#T starttime = $start_time\n";\r
+ foreach $fh ( keys %allfh ) {\r
+ printFileInfoOutputFile ($fh, 'A', $out);\r
+ }\r
+ \r
+ \r
+ my $numfh2Name = keys %fh2Name;\r
+ my $numfh2Attr = keys %fh2Attr;\r
+ print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n";\r
+ my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100";\r
+ if ($allfh{$wantfh} == 1) {\r
+ print OUT_RFS "JIAWU: found $wantfh\n";\r
+ } else {\r
+ print OUT_RFS "JIAWU: NOT found $wantfh\n";\r
+ }\r
+ foreach $fh ( keys %allfh ) {\r
+ if ( $fh eq $wantfh ) {\r
+ print OUT_RFS "JIAWU: found $wantfh\n";\r
+ printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS);\r
+ last;\r
+ }\r
+ }\r
+ print OUT_RFS "JIAWU: after \n";\r
+\r
+ \r
+ $u = $k = 0;\r
+ foreach $fh ( keys %allfh ) {\r
+ if ( exists $fh2Name{$fh} ) {$k++;}\r
+ else {$u++;}\r
+ }\r
+ print OUT_RFS "#stat: total known fh = $total_known_fh, unknown = $total_unknown_fh\n";\r
+ print OUT_RFS "#stat: total fh with name = $k, without name = $u\n";\r
+\r
+ print OUT_RFS "#RFS\n";\r
+ close OUT_RFS; \r
+\r
+}\r
+\r
+sub printFileInfoOutputFile {\r
+ my ($fh, $state, $out) = @_;\r
+\r
+ my ($p, $c) = findPath ($fh);\r
+ \r
+ if ($c == 0) {$total_unknown_fh++;}\r
+ else {$total_known_fh++;}\r
+ \r
+ my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
+ my $attr = (exists $fh2Attr{$fh}) ?\r
+ $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
+ my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+ $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1";\r
+\r
+ print $out "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+sub printFileInfo {\r
+ my ($fh, $state) = @_;\r
+\r
+ my ($p, $c) = findPath ($fh);\r
+ \r
+ if ($c == 0) {$total_unknown_fh++;}\r
+ else {$total_known_fh++;}\r
+ \r
+ my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F';\r
+ my $attr = (exists $fh2Attr{$fh}) ?\r
+ $fh2Attr{$fh} : "-1 -1 -1 -1 -1";\r
+ my $attrOrig = (exists $fh2AttrOrig{$fh}) ?\r
+ $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1";\r
+\r
+ print "F $type $state $fh $p $c $attrOrig $attr\n";\r
+}\r
+\r
+1;\r
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: key.pl,v 1.11 2003/07/26 20:52:04 ellard Exp $\r
+\r
+package key;\r
+\r
+sub makeKey {\r
+ my ($line, $proto, $op, $xid, $client, $now) = @_;\r
+ my ($client_id, $fh, $euid, $egid) = ('u', 'u', 'u', 'u');\r
+ my ($uxid) = "$client-$xid";\r
+\r
+ if ($proto eq 'R3' || $proto eq 'R2') {\r
+ if (exists $PendingKeyStr{$uxid}) {\r
+ return ($PendingKeyStr{$uxid});\r
+ }\r
+ else {\r
+ return 'u,u,u,u';\r
+ }\r
+ }\r
+\r
+ if ($main::UseClient) {\r
+ $client_id = $client;\r
+ $client_id =~ s/\..*//g\r
+ }\r
+ if ($main::UseFH && $op ne 'null') {\r
+ my $tag = ($op eq 'commit') ? 'file' : 'fh';\r
+\r
+ $fh = nfsd::nfsDumpParseLineField ($line, $tag);\r
+ if (! defined $fh) {\r
+ print STDERR "undefined fh ($line)\n";\r
+ }\r
+\r
+ $fh = nfsd::nfsDumpCompressFH ($main::FH_TYPE, $fh);\r
+\r
+ }\r
+ if ($main::UseUID && $op ne 'null') {\r
+ $euid = nfsd::nfsDumpParseLineField ($line, 'euid');\r
+ }\r
+ if ($main::UseGID && $op ne 'null') {\r
+ $egid = nfsd::nfsDumpParseLineField ($line, 'egid');\r
+ }\r
+\r
+ my $key = "$client_id,$fh,$euid,$egid";\r
+ $KeysSeen{$key} = 1;\r
+\r
+ $PendingKeyStr{$uxid} = $key;\r
+ $PendingKeyTime{$uxid} = $now;\r
+\r
+ return ($key);\r
+}\r
+\r
+sub key2str {\r
+ my ($key) = @_;\r
+\r
+ my ($client_id, $fh, $euid, $egid) = split (/,/, $key);\r
+\r
+ if ($client_id ne 'u') {\r
+\r
+ # just for aesthetics:\r
+ $client_id = sprintf ("%.8x", hex ($client_id));\r
+ $client_id =~ /^(..)(..)(..)(..)/;\r
+ $client_id = sprintf ("%d.%d.%d.%d",\r
+ hex ($1), hex ($2), \r
+ hex ($3), hex ($4)); \r
+ $client_id = sprintf ("%-15s", $client_id);\r
+ }\r
+\r
+ if ($euid ne 'u') {\r
+ $euid = hex ($euid);\r
+ }\r
+ if ($egid ne 'u') {\r
+ $egid = hex ($egid);\r
+ }\r
+\r
+ return ("$client_id $fh $euid $egid");\r
+}\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago). This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+ \r
+sub prunePending {\r
+ my ($when) = @_;\r
+\r
+ foreach my $uxid ( keys %PendingKeyTime ) {\r
+ if ($PendingKeyTime{$uxid} < $when) {\r
+ delete $PendingKeyTime{$uxid};\r
+ delete $PendingKeyStr{$uxid};\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+1;\r
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: latency.pl,v 1.8 2003/07/28 14:27:16 ellard Exp $\r
+#\r
+# latency.pl -\r
+\r
+package latency;\r
+\r
+%PendingOps = ();\r
+%OpCount = ();\r
+%OpTime = ();\r
+%KeysSeen = ();\r
+\r
+@OpList = ();\r
+\r
+sub init {\r
+ my (@oplist) = @_;\r
+\r
+ @OpList = @oplist;\r
+}\r
+\r
+# Bugs: might not recognize the actual response packets. It's an\r
+# approximation.\r
+\r
+sub update {\r
+ my ($key, $proto, $op, $xid, $client, $now) = @_;\r
+\r
+ my $uxid = "$client-$xid";\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ $PendingOps{$uxid} = $now;\r
+ }\r
+ elsif (exists $PendingOps{$uxid}) {\r
+ my $elapsed = $now - $PendingOps{$uxid};\r
+\r
+ $KeysSeen{$key} = 1;\r
+\r
+ $OpTime{"$key,$op"} += $elapsed;\r
+ $OpCount{"$key,$op"}++;\r
+\r
+ $OpTime{"$key,TOTAL"} += $elapsed;\r
+ $OpCount{"$key,TOTAL"}++;\r
+\r
+ $OpTime{"$key,INTERESTING"} += $elapsed;\r
+ $OpCount{"$key,INTERESTING"}++;\r
+\r
+ delete $PendingOps{$uxid};\r
+ }\r
+}\r
+\r
+sub resetOpCounts {\r
+\r
+ my $k;\r
+\r
+ foreach $k ( keys %OpTime ) {\r
+ $OpTime{$k} = 0.0;\r
+ }\r
+ foreach $k ( keys %OpCount ) {\r
+ $OpCount{$k} = 0;\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub printTitle {\r
+ my $str = "#L time client euid egid fh";\r
+\r
+ foreach my $op ( @OpList ) {\r
+ $str .= " $op-cnt $op-lat";\r
+ }\r
+ $str .= "\n";\r
+\r
+ print $str;\r
+} \r
+\r
+sub printOps {\r
+ my ($start_time, $out) = @_;\r
+ my ($k, $str, $op, $nk, $latms, $cnt);\r
+\r
+ my @allkeys = sort keys %KeysSeen;\r
+\r
+ foreach $k ( @allkeys ) {\r
+ my $tot = "$k,TOTAL";\r
+\r
+ if ($main::OMIT_ZEROS &&\r
+ (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) {\r
+ next;\r
+ }\r
+\r
+ $str = sprintf ("L %s %s", $start_time, &key::key2str ($k));\r
+\r
+ foreach $op ( @OpList ) {\r
+ $nk = "$k,$op";\r
+\r
+ if (exists $OpCount{$nk}) {\r
+ $cnt = $OpCount{"$k,$op"};\r
+ }\r
+ else {\r
+ $cnt = 0;\r
+ }\r
+\r
+ if ($cnt > 0) {\r
+ $latms = 1000 * $OpTime{$nk} / $cnt;\r
+ }\r
+ else {\r
+ $latms = -1;\r
+ }\r
+\r
+ $str .= sprintf (" %d %.4f", $cnt, $latms);\r
+ }\r
+\r
+ print $out "$str\n";\r
+ }\r
+}\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago). This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+ \r
+sub prunePending {\r
+ my ($when) = @_;\r
+\r
+ foreach my $uxid ( keys %PendingOps ) {\r
+ if ($PendingOps{$uxid} < $when) {\r
+ delete $PendingOps{$uxid};\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+1;\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: nfsdump.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $\r
+#\r
+# Utility for dealing with raw nfsdump records.\r
+\r
+package nfsd;\r
+\r
+# If $AllowRisky is set, then allow some optimizations that might be\r
+# "risky" in bizarre situations (but have never been observed to\r
+# actually break anything). By default, no riskiness is permitted.\r
+\r
+$AllowRisky = 0;\r
+\r
+# nfsDumpParseLine -- initializes the global associative array\r
+# %nfsd'nfsDumpLine with information about a record from nfsdump. \r
+# Returns an empty list if anything goes wrong. Otherwise, returns a\r
+# list of the protocol (eg R2, C2, R3, C3), the name of the operation,\r
+# and the xid, the client host ID, the time, and for responses, the\r
+# status (via nfsDumpParseLineHeader). The reason for this particular\r
+# return list is that these are very frequently-accessed values, so it\r
+# can save time to avoid going through the associative array to access\r
+# them.\r
+#\r
+# All records begin with several fixed fields, and then are followed\r
+# by some number of name/value pairs, and finally some diagnostic\r
+# fields (which are mostly ignored by this routine-- the only\r
+# diagnostic this routine cares about is whether the packet as part of\r
+# a jumbo packet or not. If so, then 'truncated' is set.)\r
+\r
+sub nfsDumpParseLine {\r
+ my ($line, $total) = @_;\r
+\r
+ my (@rl) = &nfsDumpParseLineHeader ($line);\r
+\r
+ if (@rl && $total) {\r
+ &nfsDumpParseLineBody ($line);\r
+ }\r
+\r
+ return @rl;\r
+}\r
+\r
+sub nfsDumpParseLineBody {\r
+ my ($line) = @_;\r
+ my $i;\r
+ my $client_id;\r
+ my $reseen;\r
+\r
+ undef %nfsDumpLine;\r
+\r
+ # If the line doesn't start with a digit, then it's certainly\r
+ # not properly formed, so bail out immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return undef;\r
+ }\r
+\r
+ my @l = split (' ', $line);\r
+ my $lineLen = @l;\r
+ if ($l[$lineLen - 1] eq 'LONGPKT') {\r
+ splice (@l, $lineLen - 1);\r
+ $nfsDumpLine{'truncated'} = 1;\r
+ $lineLen--;\r
+ }\r
+\r
+ $nfsDumpLine{'time'} = $l[0];\r
+ $nfsDumpLine{'srchost'} = $l[1];\r
+ $nfsDumpLine{'deshost'} = $l[2];\r
+ $nfsDumpLine{'proto'} = $l[4];\r
+ $nfsDumpLine{'xid'} = $l[5];\r
+ $nfsDumpLine{'opcode'} = $l[6];\r
+ $nfsDumpLine{'opname'} = $l[7];\r
+\r
+ if (($l[4] eq 'R3') || ($l[4] eq 'R2')) {\r
+ $nfsDumpLine{'status'} = $l[8];\r
+\r
+ $client_id = $l[2];\r
+ $reseen = 0;\r
+ for ($i = 9; $i < $lineLen - 10; $i += 2) {\r
+ if (defined $nfsDumpLine{$l[$i]}) {\r
+ $reseen = 1;\r
+ $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1];\r
+ }\r
+ else {\r
+ $nfsDumpLine{$l[$i]} = $l[$i + 1];\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ $client_id = $l[1];\r
+ $reseen = 0;\r
+ for ($i = 8; $i < $lineLen - 6; $i += 2) {\r
+ if (defined $nfsDumpLine{$l[$i]}) {\r
+ $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1];\r
+ $reseen = 1;\r
+ }\r
+ else {\r
+ $nfsDumpLine{$l[$i]} = $l[$i + 1];\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+# Returns an empty list if anything goes wrong. Otherwise, returns a\r
+# list of the protocol (eg R2, C2, R3, C3), the name of the operation,\r
+# and the xid, the client host ID, and time, and the response status. \r
+# (For call messages, the status is 'na'.)\r
+\r
+sub nfsDumpParseLineHeader {\r
+ my ($line) = @_;\r
+\r
+ # If the line doesn't start with a digit, then it's certainly\r
+ # not properly formed, so bail out immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return ();\r
+ }\r
+ else {\r
+ my $client_id;\r
+ my $status;\r
+\r
+ my @l = split (' ', $line, 10);\r
+\r
+\r
+ if (($l[4] eq 'R3') || ($l[4] eq 'R2')) {\r
+ $client_id = $l[2];\r
+ $status = $l[8];\r
+ }\r
+ else {\r
+ $client_id = $l[1];\r
+ $status = 'na';\r
+ }\r
+\r
+ return ($l[4], $l[7], $l[5], $client_id, $l[0], $status);\r
+ }\r
+}\r
+\r
+# nfsDumpParseLineFields -- Just return a subset of the fields,\r
+# without parsing the entire line.\r
+\r
+sub nfsDumpParseLineFields {\r
+ my ($line, @fields) = @_;\r
+ my $i;\r
+\r
+ # If the line doesn't start with a digit, then\r
+ # it's certainly not properly formed, so bail out\r
+ # immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return ();\r
+ }\r
+\r
+ my $rest;\r
+ if ($AllowRisky) {\r
+ $rest = $line;\r
+ }\r
+ else {\r
+ my @foo = split (' ', $line, 9);\r
+ $rest = ' ' . $foo[8];\r
+ }\r
+\r
+ my $fl = @fields;\r
+ my @l = ();\r
+ for ($i = 0; $i < $fl; $i++) {\r
+ my $field = $fields[$i];\r
+\r
+ $rest =~ /\ $field\ +([^\ ]+)/;\r
+ $l[$i] = $1;\r
+ }\r
+\r
+ return (@l);\r
+}\r
+\r
+# nfsDumpParseLineField -- Just return ONE of the fields,\r
+# without parsing the entire line.\r
+\r
+sub nfsDumpParseLineField {\r
+ my ($line, $field) = @_;\r
+\r
+ # If the line doesn't start with a digit, then\r
+ # it's certainly not properly formed, so bail out\r
+ # immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return undef;\r
+ }\r
+\r
+ my $rest;\r
+ if ($AllowRisky) {\r
+ $rest = $line;\r
+ }\r
+ else {\r
+ my @foo = split (' ', $line, 9);\r
+ $rest = ' ' . $foo[8];\r
+ }\r
+\r
+ $rest =~ /\ $field\ +([^\ ]+)/;\r
+ return $1;\r
+}\r
+\r
+# Returns a new file handle that has all the "useful" information as\r
+# the original, but requires less storage space. File handles\r
+# typically contain quite a bit of redundancy or unused bytes.\r
+#\r
+# This routine only knows about the advfs and netapp formats. If\r
+# you're using anything else, just use anything else as the mode, and\r
+# the original file handle will be returned.\r
+#\r
+# If you extend this to handle more file handles, please send the new\r
+# code to me (ellard@eecs.harvard.edu) so I can add it to the\r
+# distribution.\r
+\r
+sub nfsDumpCompressFH {\r
+ my ($mode, $fh) = @_;\r
+\r
+ if ($mode eq 'advfs') {\r
+\r
+ # The fh is a long hex string:\r
+ # 8 chars: file system ID\r
+ # 8 chars: apparently unused.\r
+ # 8 chars: unused.\r
+ # 8 chars: inode\r
+ # 8 chars: generation\r
+ # rest of string: mount point (not interesting).\r
+ # So all we do is pluck out the fsid, inode,\r
+ # and generation number, and throw the rest away.\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$1-$4-$5");\r
+ }\r
+ elsif ($mode eq 'netapp') {\r
+\r
+ # Here's the netapp format (from Shane Owara):\r
+ #\r
+ # 4 bytes mount point file inode number\r
+ # 4 bytes mount point file generation number\r
+ # \r
+ # 2 bytes flags\r
+ # 1 byte snapshot id\r
+ # 1 byte unused\r
+ #\r
+ # 4 bytes file inode number\r
+ # 4 bytes file generation number\r
+ # 4 bytes volume identifier\r
+ #\r
+ # 4 bytes export point fileid\r
+ # 1 byte export point snapshot id\r
+ # 3 bytes export point snapshot generation number\r
+ #\r
+ # The only parts of this that are interesting are\r
+ # inode, generation, and volume identifier (and probably\r
+ # a lot of the bits of the volume identifier could be\r
+ # tossed, since we don't have many volumes...).\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$4-$5-$6-$1");\r
+ }\r
+ elsif ($mode eq 'RFSNN') {\r
+\r
+ # Here's the netapp format (from Shane Owara):\r
+ #\r
+ # 4 bytes mount point file inode number\r
+ # 4 bytes mount point file generation number\r
+ # \r
+ # 2 bytes flags\r
+ # 1 byte snapshot id\r
+ # 1 byte unused\r
+ #\r
+ # 4 bytes file inode number\r
+ # 4 bytes file generation number\r
+ # 4 bytes volume identifier\r
+ #\r
+ # 4 bytes export point fileid\r
+ # 1 byte export point snapshot id\r
+ # 3 bytes export point snapshot generation number\r
+ #\r
+ # The only parts of this that are interesting are\r
+ # inode, generation, and volume identifier (and probably\r
+ # a lot of the bits of the volume identifier could be\r
+ # tossed, since we don't have many volumes...).\r
+ \r
+ # 61890100575701002000000 0009ac710e9ea381 0d24400006189010057570100\r
+ # 61890100575701002000000 0009ac70ed2ea381 0d24400006189010057570100\r
+ # 61890100575701002000000 000479a1e008d782 0d24400006189010057570100\r
+ # Ningning need only 24-39 (or 12-19 bytes)\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$4$5");\r
+ }else {\r
+\r
+ return ($fh);\r
+ }\r
+}\r
+\r
+sub testMain {\r
+ $lineNum = 0;\r
+\r
+ while (<STDIN>) {\r
+ $line = $_;\r
+ $lineNum++;\r
+\r
+ &nfsDumpParseLine ($line);\r
+ }\r
+}\r
+\r
+1;\r
+\r
+# end of nfsdump.pl\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: nfsscan,v 1.18 2003/07/28 14:27:16 ellard Exp $\r
+\r
+$ProgDir = $0;\r
+$ProgDir =~ /(^.*)\//;\r
+$ProgDir = $1;\r
+if (!$ProgDir) {\r
+ $ProgDir = ".";\r
+}\r
+\r
+require "$ProgDir/nfsdump.pl";\r
+require "$ProgDir/userUtils.pl";\r
+require "$ProgDir/hier.pl";\r
+require "$ProgDir/counts.pl";\r
+require "$ProgDir/latency.pl";\r
+require "$ProgDir/key.pl";\r
+require "$ProgDir/common.pl";\r
+\r
+use Getopt::Std;\r
+\r
+$INTERVAL = 5 * 60; # in seconds (5 minutes)\r
+\r
+%KeysSeen = ();\r
+\r
+@ADD_USERS = ();\r
+@DEL_USERS = ();\r
+@ADD_GROUPS = ();\r
+@DEL_GROUPS = ();\r
+@ADD_CLIENTS = ();\r
+@DEL_CLIENTS = ();\r
+\r
+$DO_COUNTS = 1;\r
+$DO_LATENCY = 0;\r
+$DO_FILES = 0;\r
+$DO_PATHS = 0;\r
+$DO_SQUEEZE = 0;\r
+\r
+$FH_TYPE = 'unknown';\r
+\r
+$END_TIME = -1;\r
+$START_TIME = -1;\r
+$NOW = -1;\r
+$UseClient = 0;\r
+$UseFH = 0;\r
+$UseUID = 0;\r
+$UseGID = 0;\r
+$OMIT_ZEROS = 0;\r
+\r
+$OutFileBaseName = undef;\r
+\r
+$nextPruneTime = -1;\r
+$PRUNE_INTERVAL = 1 * 60; # One minute.\r
+\r
+# &&&\r
+# Is this really the right default set of operations?\r
+\r
+$DEF_OPLIST = 'read,write,lookup,getattr,access,create,remove';\r
+@OPLIST = ('TOTAL', 'INTERESTING', \r
+ split (/,/, $DEF_OPLIST));\r
+%OPARRAY = ();\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] [trace1 [trace2 ...]]\r
+\r
+If no trace files are specified, then the trace is read from stdin.\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+-B [CFUG] Compute per-Client, per-File, per-User, or per-Group info.\r
+\r
+-c c1[,c2]* Include only activity performed by the specified clients.\r
+\r
+-C c1[,c2]* Exclude activity performed by the specified clients.\r
+\r
+-d Compute per-directory statistics. This implicitly\r
+ enables -BF so that per-file info is computed.\r
+\r
+-f Do file info tracking. This implicitly enables -BF so\r
+ that per-File info is computed.\r
+\r
+-F fhtype Specify the file handle type used by the server.\r
+ (advfs or netapp)\r
+\r
+-g g1[,g2]* Include only activity performed by the specified groups.\r
+\r
+-G g1[,g2]* Exclude activity performed by the specified groups.\r
+\r
+-l Record average operation latency.\r
+\r
+-o basename Write output to files starting with the specified\r
+ basename. The "Count" table goes to basename.cnt,\r
+ "Latency" to basename.lat, and "File" to basename.fil.\r
+ The default is to write all output to stdout.\r
+\r
+-O op[,op]* Specify the list of "interesting" operations.\r
+ The default list is:\r
+\r
+ read,write,lookup,getattr,access,create,remove\r
+\r
+ If the first op starts with +, then the specified list\r
+ of ops is appended to the default list. The special\r
+ pseudo-ops readM and writeM represent the number of\r
+ bytes read and written, expressed in MB.\r
+\r
+-t interval Time interval for cummulative statistics (such as\r
+ operation count). The default is $INTERVAL seconds. \r
+ If set to 0, then the entire trace is processed. By\r
+ default, time is specified in seconds, but if the last\r
+ character of the interval is any of s, m, h, or d,\r
+ then the interval is interpreted as seconds, minutes,\r
+ hours, or days.\r
+\r
+-u u1[,u2]* Include only activity performed by the specified users.\r
+\r
+-U u1[,u2]* Exclude activity performed by the specified users.\r
+\r
+-Z Omit count and latency lines that have a zero total\r
+ operation count.\r
+.\r
+\r
+\r
+main ();\r
+\r
+sub main {\r
+\r
+ parseArgs ();\r
+\r
+ if ($DO_COUNTS) {\r
+ counts::printTitle (*OUT_COUNTS);\r
+ }\r
+\r
+ if ($DO_LATENCY) {\r
+ latency::printTitle (*OUT_LATENCY);\r
+ }\r
+\r
+ counts::resetOpCounts ();\r
+\r
+ my $cmdbuf = 'rm -f noattrdirdiscard noattrdir-root';\r
+ system($cmdbuf);\r
+\r
+ readTrace ();\r
+}\r
+\r
+sub parseArgs {\r
+\r
+ my $cmdline = "$0 " . join (' ', @ARGV);\r
+\r
+ my $Options = "B:dfF:g:G:hlO:o:t:u:U:SR:Z";\r
+ if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+ }\r
+ if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+ }\r
+\r
+ #RFS: neednot input arguments\r
+ $opt_o = "test";\r
+ $opt_f = 1;\r
+ $opt_t = 0;\r
+ #$opt_F = 'RFSNN'; # advfs or netapp\r
+\r
+ if (defined $opt_B) {\r
+ $UseClient = ($opt_B =~ /C/);\r
+ $UseFH = ($opt_B =~ /F/);\r
+ $UseUID = ($opt_B =~ /U/);\r
+ $UseGID = ($opt_B =~ /G/);\r
+ }\r
+\r
+ if (defined $opt_o) {\r
+ $OutFileBaseName = $opt_o;\r
+ }\r
+\r
+ if (defined $opt_O) {\r
+ if ($opt_O =~ /^\+(.*)/) {\r
+ @OPLIST = (@OPLIST, split (/,/, $1));\r
+ }\r
+ else {\r
+ @OPLIST = ('TOTAL', 'INTERESTING', split (/,/, $opt_O));\r
+ }\r
+ # Error checking?\r
+ }\r
+\r
+ if (defined $opt_l) {\r
+ $DO_LATENCY = 1;\r
+ }\r
+\r
+ if (defined $opt_t) {\r
+ if ($INTERVAL =~ /([0-9]*)([smhd])/) {\r
+ my $n = $1;\r
+ my $unit = $2;\r
+\r
+ if ($unit eq 's') {\r
+ $INTERVAL = $opt_t;\r
+ }\r
+ elsif ($unit eq 'm') {\r
+ $INTERVAL = $opt_t * 60;\r
+ }\r
+ elsif ($unit eq 'h') {\r
+ $INTERVAL = $opt_t * 60 * 60;\r
+ }\r
+ elsif ($unit eq 'd') {\r
+ $INTERVAL = $opt_t * 24 * 60 * 60;\r
+ }\r
+ }\r
+ else {\r
+ $INTERVAL = $opt_t;\r
+ }\r
+ }\r
+\r
+ $DO_PATHS = (defined $opt_d);\r
+ $DO_FILES = (defined $opt_f);\r
+ $DO_SQUEEZE = (defined $opt_S);\r
+ $OMIT_ZEROS = (defined $opt_Z);\r
+\r
+ $TIME_ROUNDING = (defined $opt_R) ? $opt_R : 0;\r
+\r
+ if (defined $opt_F) {\r
+ $FH_TYPE = $opt_F;\r
+ }\r
+\r
+ if (defined $opt_c) {\r
+ @ADD_CLIENTS = split (/,/, $opt_c);\r
+ }\r
+ if (defined $opt_C) {\r
+ @DEL_CLIENTS = split (/,/, $opt_c);\r
+ }\r
+\r
+ if (defined $opt_g) {\r
+ @ADD_GROUPS = groups2gids (split (/,/, $opt_g));\r
+ }\r
+ if (defined $opt_G) {\r
+ @DEL_GROUPS = groups2gids (split (/,/, $opt_G));\r
+ }\r
+\r
+ if (defined $opt_u) {\r
+ @ADD_USERS = logins2uids (split (/,/, $opt_u));\r
+ }\r
+ if (defined $opt_U) {\r
+ @DEL_USERS = logins2uids (split (/,/, $opt_U));\r
+ }\r
+\r
+\r
+ # Now that we know what options the user asked for, initialize\r
+ # things accordingly.\r
+\r
+ if ($DO_PATHS || $DO_FILES) {\r
+ $UseFH = 1;\r
+ }\r
+\r
+ if ($DO_LATENCY) {\r
+ latency::init (@OPLIST);\r
+ }\r
+\r
+ if ($DO_COUNTS) {\r
+ counts::init (@OPLIST);\r
+ }\r
+\r
+ if (defined $OutFileBaseName) {\r
+ if ($DO_COUNTS) {\r
+ open (OUT_COUNTS, ">$OutFileBaseName.cnt") ||\r
+ die "Can't create $OutFileBaseName.cnt.";\r
+ print OUT_COUNTS "#cmdline $cmdline\n";\r
+ }\r
+ if ($DO_LATENCY) {\r
+ open (OUT_LATENCY, ">$OutFileBaseName.lat") ||\r
+ die "Can't create $OutFileBaseName.lat.";\r
+ print OUT_LATENCY "#cmdline $cmdline\n";\r
+ }\r
+ if ($DO_FILES) {\r
+ open (OUT_FILES, ">$OutFileBaseName.fil") ||\r
+ die "Can't create $OutFileBaseName.fil.";\r
+ print OUT_FILES "#cmdline $cmdline\n";\r
+ }\r
+ if ($DO_PATHS) {\r
+ open (OUT_PATHS, ">$OutFileBaseName.pat") ||\r
+ die "Can't create $OutFileBaseName.pat.";\r
+ print OUT_PATHS "#cmdline $cmdline\n";\r
+ }\r
+ }\r
+ else {\r
+ *OUT_COUNTS = STDOUT;\r
+ *OUT_LATENCY = STDOUT;\r
+ *OUT_FILES = STDOUT;\r
+ *OUT_PATHS = STDOUT;\r
+\r
+ print STDOUT "#cmdline $cmdline\n";\r
+ }\r
+\r
+ foreach my $op ( @OPLIST ) {\r
+ $OPARRAY{$op} = 1;\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub readTrace {\r
+ my (@args) = @_;\r
+\r
+ while (my $line = <>) {\r
+\r
+ $hier::rfsLineNum++;\r
+ if ( ($hier::rfsLineNum % 1000) eq 0) {\r
+ print STDERR "$hier::rfsLineNum\n";\r
+ }\r
+\r
+\r
+ if ($line =~ /SHORT\ PACKET/) {\r
+ next;\r
+ }\r
+\r
+ my ($proto, $op, $xid, $client, $now, $response) =\r
+ nfsd::nfsDumpParseLineHeader ($line);\r
+ $NOW = $now;\r
+\r
+ # NOTE: This next bit of logic requires a little\r
+ # extra attention. We want to discard lines as\r
+ # quickly as we can if they're not "interesting". \r
+ # However, different lines are interesting in\r
+ # different contexts, so the order of the tests and\r
+ # the manner in which they are interspersed with\r
+ # subroutine calls to pluck info from the lines is\r
+ # very important.\r
+\r
+ # Check whether it is a line that we should prune and\r
+ # ignore, because of the filters.\r
+ \r
+ next if (($op eq 'C3' || $op eq 'C2') &&\r
+ ! pruneCall ($line, $client));\r
+\r
+ if ($DO_PATHS || $DO_FILES) {\r
+ hier::processLine ($line,\r
+ $proto, $op, $xid, $client,\r
+ $now, $response, $FH_TYPE);\r
+ }\r
+\r
+ my $key = key::makeKey ($line, $proto, $op,\r
+ $xid, $client, $now,\r
+ $UseClient, $UseFH, $UseUID, $UseGID,\r
+ $FH_TYPE);\r
+ if (! defined $key) {\r
+ next ;\r
+ }\r
+ $KeysSeen{$key} = 1;\r
+\r
+ # Count everything towards the total, but only\r
+ # do the rest of the processing for things\r
+ # that are "interesting".\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ $counts::OpCounts{"$key,TOTAL"}++;\r
+ $counts::KeysSeen{$key} = 1;\r
+\r
+ next if (! exists $OPARRAY{$op});\r
+\r
+ $counts::OpCounts{"$key,$op"}++;\r
+ $counts::OpCounts{"$key,INTERESTING"}++;\r
+ }\r
+\r
+ if ($op eq 'read' && exists $OPARRAY{'readM'}) {\r
+ doReadSize ($line, $proto, $op, $key, $client, $xid, $response, $now);\r
+ }\r
+\r
+ if ($op eq 'write' && exists $OPARRAY{'writeM'}) {\r
+ doWriteSize ($line, $proto, $op, $key, $client, $xid, $response, $now);\r
+ }\r
+\r
+ if ($DO_LATENCY) {\r
+ latency::update ($key, $proto, $op,\r
+ $xid, $client, $now);\r
+ }\r
+\r
+ if ($END_TIME < 0) {\r
+ $START_TIME = findStartTime ($NOW, $TIME_ROUNDING);\r
+ $END_TIME = $START_TIME + $INTERVAL;\r
+ }\r
+\r
+ # Note that this is a loop, because if the interval is\r
+ # short enough, or the system is very idle (or there's\r
+ # a filter in place that makes it look idle), entire\r
+ # intervals can go by without anything happening at\r
+ # all. Some tools can get confused if intervals are\r
+ # missing from the table, so we emit them anyway.\r
+\r
+ while (($INTERVAL > 0) && ($NOW >= $END_TIME)) {\r
+ printAll ($START_TIME);\r
+\r
+ counts::resetOpCounts ();\r
+ latency::resetOpCounts ();\r
+\r
+ $START_TIME += $INTERVAL;\r
+ $END_TIME = $START_TIME + $INTERVAL;\r
+ }\r
+\r
+ if ($now > $nextPruneTime) {\r
+ key::prunePending ($now - $PRUNE_INTERVAL);\r
+ latency::prunePending ($now - $PRUNE_INTERVAL);\r
+\r
+ prunePending ($now - $PRUNE_INTERVAL);\r
+\r
+ $nextPruneTime = $now + $PRUNE_INTERVAL;\r
+ }\r
+ }\r
+\r
+ # Squeeze out the last little bit, if there's anything that we\r
+ # counted but did not emit. If DO_SQUEEZE is true, then\r
+ # always do this. Otherwise, only squeeze out the results of\r
+ # the last interval if the interval is "almost" complete (ie\r
+ # within 10 seconds of the end).\r
+\r
+ if (($NOW > $START_TIME) && ($DO_SQUEEZE || (($END_TIME - $NOW) < 10))) {\r
+ printAll ($START_TIME);\r
+ counts::resetOpCounts ();\r
+ }\r
+\r
+ print "#T endtime = $NOW\n";\r
+\r
+}\r
+\r
+sub printAll {\r
+ my ($start_time) = @_;\r
+\r
+ if ($DO_COUNTS) {\r
+ counts::printOps ($start_time, *OUT_COUNTS);\r
+ }\r
+\r
+ if ($DO_LATENCY) {\r
+ latency::printOps ($start_time, *OUT_LATENCY);\r
+ }\r
+\r
+ if ($DO_FILES) {\r
+ hier::printAll ($start_time, *OUT_FILES);\r
+ }\r
+\r
+ if ($DO_PATHS) {\r
+ printPaths ($start_time, *OUT_PATHS);\r
+ }\r
+}\r
+\r
+sub pruneCall {\r
+ my ($line, $client) = @_;\r
+\r
+ if (@ADD_USERS > 0 || @DEL_USERS > 0) {\r
+ my $c_uid = nfsd::nfsDumpParseLineField ($line, 'euid');\r
+ if (! defined ($c_uid)) {\r
+ return 0;\r
+ }\r
+ $c_uid = hex ($c_uid);\r
+\r
+ if (@ADD_USERS && !grep (/^$c_uid$/, @ADD_USERS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_USERS && grep (/^$c_uid$/, @DEL_USERS)) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ if (@ADD_GROUPS > 0 || @DEL_GROUPS > 0) {\r
+ my $c_gid = nfsd::nfsDumpParseLineField ($line, 'egid');\r
+ if (! defined ($c_gid)) {\r
+ return 0;\r
+ }\r
+ $c_gid = hex ($c_gid);\r
+\r
+ if (@ADD_GROUPS && !grep (/^$c_gid$/, @ADD_GROUPS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_GROUPS && grep (/^$c_gid$/, @DEL_GROUPS)) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ if (@ADD_CLIENTS > 0 || @DEL_CLIENTS > 0) {\r
+ if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+%PathOpCounts = ();\r
+%PathsSeen = ();\r
+\r
+sub buildDirPath {\r
+ my ($fh, $key) = @_;\r
+ my $pfh;\r
+ my $cnt;\r
+\r
+ foreach my $op ( @OPLIST ) {\r
+ if (exists $counts::OpCounts{"$key,$op"}) {\r
+ $cnt = $counts::OpCounts{"$key,$op"};\r
+ }\r
+ else {\r
+ $cnt = 0;\r
+ }\r
+ $PathOpCounts{"$fh,$op"} = $cnt;\r
+\r
+ $PathsSeen{$fh} = 1;\r
+\r
+ $pfh = $fh;\r
+ my $len = 0;\r
+ while (defined ($pfh = $hier::fh2Parent{$pfh})) {\r
+\r
+ if ($len++ > 20) {\r
+ print "Really long path ($fh)\n";\r
+ last;\r
+ }\r
+\r
+ if (exists $PathOpCounts{"$pfh,$op"}) {\r
+ $PathOpCounts{"$pfh,$op"} += $cnt;\r
+ }\r
+ else {\r
+ $PathOpCounts{"$pfh,$op"} = $cnt;\r
+ }\r
+ $PathsSeen{$pfh} = 1;\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+sub printPaths {\r
+ my ($start_time, $out) = @_;\r
+\r
+ my $str = "#D time Dir/File dircnt path fh";\r
+ foreach my $op ( @OPLIST ) {\r
+ $str .= " $op";\r
+ }\r
+ $str .= "\n";\r
+\r
+ print $out $str;\r
+\r
+ undef %PathsSeen;\r
+\r
+ foreach my $key ( keys %KeysSeen ) {\r
+ my ($client_id, $fh, $euid, $egid) = split (/,/, $key);\r
+\r
+ buildDirPath ($fh, $key);\r
+ }\r
+\r
+ foreach my $fh ( keys %PathsSeen ) {\r
+ my ($path, $cnt) = hier::findPath ($fh);\r
+\r
+ if ($cnt == 0) {\r
+ $path = ".";\r
+ }\r
+\r
+ my $type = (exists $hier::fhIsDir{$fh} && $hier::fhIsDir{$fh}==2) ? 'D' : 'F';\r
+\r
+ my $str = "$cnt $type $path $fh ";\r
+\r
+ foreach my $op ( @OPLIST ) {\r
+ my $cnt;\r
+\r
+ if (exists $PathOpCounts{"$fh,$op"}) {\r
+ $cnt = $PathOpCounts{"$fh,$op"};\r
+ }\r
+ else {\r
+ print "Missing $fh $op\n";\r
+ $cnt = 0;\r
+ }\r
+\r
+ $str .= " $cnt";\r
+\r
+ $PathOpCounts{"$fh,$op"} = 0; # &&& reset\r
+ }\r
+\r
+ print $out "D $start_time $str\n";\r
+ }\r
+}\r
+\r
+%uxid2key = ();\r
+%uxid2time = ();\r
+\r
+sub doReadSize {\r
+ my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_;\r
+\r
+ my $uxid = "$client-$xid";\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+ $uxid2time{$uxid} = $time;\r
+ $uxid2key{$uxid} = $key;\r
+ }\r
+ else {\r
+ if (! exists $uxid2key{$uxid}) {\r
+ return ;\r
+ }\r
+ if ($response ne 'OK') {\r
+ return ;\r
+ }\r
+\r
+ $key = $uxid2key{$uxid};\r
+ my $count = nfsd::nfsDumpParseLineField ($line, 'count');\r
+ $count = hex ($count);\r
+\r
+ delete $uxid2key{$uxid};\r
+ delete $uxid2time{$uxid};\r
+\r
+ $counts::OpCounts{"$key,readM"} += $count;\r
+ }\r
+}\r
+\r
+# Note that we always just assume that writes succeed, because on most\r
+# systems they virtually always do. If you're tracing a system where\r
+# your users are constantly filling up the disk or exceeding their\r
+# quotas, then you will need to fix this.\r
+\r
+sub doWriteSize {\r
+ my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_;\r
+\r
+ if ($proto eq 'C3' || $proto eq 'C2') {\r
+\r
+ my $tag = ($proto eq 'C3') ? 'count' : 'tcount';\r
+\r
+ my $count = nfsd::nfsDumpParseLineField ($line, $tag);\r
+\r
+ if (! $count) {\r
+ printf "WEIRD count $line\n";\r
+ }\r
+\r
+ $count = hex ($count);\r
+\r
+ $counts::OpCounts{"$key,writeM"} += $count;\r
+ }\r
+}\r
+\r
+\r
+# Purge all the pending XID records dated earlier than $when (which is\r
+# typically at least $PRUNE_INTERVAL seconds ago). This is important\r
+# because otherwise missing XID records can pile up, eating a lot of\r
+# memory. \r
+ \r
+sub prunePending {\r
+ my ($when) = @_;\r
+\r
+ foreach my $uxid ( keys %uxid2time ) {\r
+ if ($uxid2time{$uxid} < $when) {\r
+ delete $uxid2key{$uxid};\r
+ delete $uxid2time{$uxid};\r
+ }\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
--- /dev/null
+# Copyright (c) 2002-2003
+# The President and Fellows of Harvard College.
+#
+# $Id: nfsscan.txt,v 1.5 2003/07/28 14:27:16 ellard Exp $
+
+NFSSCAN DOCUMENTATION
+
+This is version 0.10a of nfsscan, dated 7/25/2003.
+
+THIS IS A PRELIMINARY RELEASE OF A NEW UTILITY. YOU SHOULD ASSUME
+THAT THE COMMANDLINE FORMATS WILL EVOLVE RAPIDLY OVER THE NEXT SEVERAL
+WEEKS.
+
+Please report bugs, problems or suggestions for improvements to
+ellard@eecs.harvard.edu.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+COMMANDLINE OPTIONS
+
+Usage: nfsscan [options] [trace1 [trace2 ...]]
+
+If no trace files are specified, then the trace is read from stdin.
+
+Command line options:
+
+-h Print usage message and exit.
+
+-B [CFUG] Compute per-Client, per-File, per-User, or per-Group info.
+
+-c c1[,c2]* Include only activity performed by the specified clients.
+
+-C c1[,c2]* Exclude activity performed by the specified clients.
+
+-d Compute per-directory statistics. This implicitly
+ enables -BF so that per-file info is computed.
+
+-f Do file info tracking. This implicitly enables -BF so
+ that per-File info is computed.
+
+-F fhtype Specify the file handle type used by the server.
+ (advfs or netapp)
+
+-g g1[,g2]* Include only activity performed by the specified groups.
+
+-G g1[,g2]* Exclude activity performed by the specified groups.
+
+-l Record average operation latency.
+
+-o basename Write output to files starting with the specified
+ basename. The "Count" table goes to basename.cnt,
+ "Latency" to basename.lat, and "File" to basename.fil.
+ The default is to write all output to stdout.
+
+-O op[,op]* Specify the list of "interesting" operations.
+ The default list is:
+
+ read,write,lookup,getattr,access,create,remove
+
+ If the first op starts with +, then the specified list
+ of ops is appended to the default list. The special
+ pseudo-ops readM and writeM represent the number of
+ bytes read and written, expressed in MB.
+
+-t interval Time interval for cummulative statistics (such as
+ operation count). The default is 300 seconds.
+ If set to 0, then the entire trace is processed. By
+ default, time is specified in seconds, but if the last
+ character of the interval is any of s, m, h, or d,
+ then the interval is interpreted as seconds, minutes,
+ hours, or days.
+
+-u u1[,u2]* Include only activity performed by the specified users.
+
+-U u1[,u2]* Exclude activity performed by the specified users.
+
+-Z Omit count and latency lines that have a zero total
+ operation count.
+
+
+OUTPUT FORMATS
+
+Each line generated by nfsscan begins with a token that indicates the
+table to which it belongs.
+
+COUNT LINES
+
+These lines all begin with 'C' (or #C, for comments),
+and have the following format:
+
+C time client euid egid fh TOTAL INTERESTING <OPS>
+
+time - The time of the start of the measurement interval.
+
+client - The IP number of the client. If the user has not
+ requested per-client statistics, this field is 'u'.
+
+euid - The effective uid of the caller, in decimal. If the user has
+ not requested per-user statistics, this field is 'u'.
+
+egid - The effective gid of the caller, in decimal. If the user has
+ not requested per-group statistics, this field is 'u'.
+
+fh - The file handle used by the operation. If the user has not
+ requested per-file statistics, this field is 'u'.
+
+TOTAL - The total number of operations for the given client, euid,
+ egid, and fh, for all the operations in <OPS...>. Note that
+ this is NOT the same as the total of all operations! For
+ example, if you set <OPS> to the empty list, the TOTAL will
+ always be zero.
+
+INTERESTING - The total number of "interesting" operations. These are
+ either the default operations (listed below) or the whatever
+ set of operations the user specifies.
+
+
+<OPS> - Following INTERESTING, the count for each "interesting"
+ operation observed during the measurement interval is printed.
+ The list of operations can be specified or extended on the
+ commandline. By default, the list of operations is:
+
+ read, write, lookup, getattr, access, create, remove
+
+LATENCY LINES
+
+The format for the "latency" lines is like that of the "count" lines,
+except the lines all begin with "L" (or "#L") and each count
+(including the TOTAL and INTERESTING counts) is followed the average
+latency for that operation. If the count for a particular operation
+is 0, then the average latency is shown as -1.
+
+Note that the counts for the latency lines may be (and often are)
+different than the counts for count lines. This is because the count
+lines show the number of calls that were observed, and the latency
+lines require observing both calls and reponses.
+
+FILE LINES
+
+These lines show information about files, rather than information
+about calls. These lines all begin with "F" (or "#F"). The file
+information (size, mode, etc) are currently only printed for files,
+not directories.
+
+The format is:
+
+F type state fh path dirs mode size atime mtime ctime
+
+type - Either "F" for file, or "D" for directory.
+
+state - "A" if the file is still alive at the end of the
+ trace, or "D" if the file has been deleted.
+
+fh - The file handle.
+
+path - as much of the file path as can be reconstructed from the
+ observed traffic.
+
+dirs - the number of directories in the path.
+
+mode - the most recently observed permissions on the file, in hex.
+
+size - the most recently observed size of the file, in hex.
+
+atime - last access time of the file
+
+mtime - last modification time of the file
+
+ctime - last file status change time
+
+DIRECTORY LINES
+
+These lines show operation count information for files and
+directories. These lines all begin with "D" (or "#D"). Each line
+begins with the following fields:
+
+time - The time of the start of the measurement interval.
+
+Dir/File - D for directory, F for file.
+
+dircnt - The length of the path to that directory, or zero for files.
+
+path - The path from the mount point to the directory or file.
+
+fh - The file handle of the directory or file.
+
+TOTAL - The total number of operations for the given client, euid,
+ egid, and fh, for all the operations in <OPS...>. Note that
+ this is NOT the same as the total of all operations! For
+ example, if you set <OPS> to the empty list, the TOTAL will
+ always be zero.
+
+Following the TOTAL, the count for each "interesting" operation
+observed during the measurement interval is printed. The list of
+operations can be specified on the commandline. By default, the list
+of operations is:
+
+ read, write, lookup, getattr, access, create, remove
+
+The information for files is redundant because this information is
+also reflected in the Count lines (if -BF is used), but it is
+sometimes useful to have it in the same format as the directory info.
+
+The TOTAL and op counts for directories represent the total number of
+ops for the files in that directory AND all of its subdirectories.
+
+NOTE: there are several potential problems with the directory information:
+
+ 1. It is possible (and not uncommon) for some part of a path
+ to be missing from the traces. The path is
+ reconstructed as far back to the root as possible, but
+ this is not always successful.
+
+ If no information about the name of a file is known,
+ then it is given the name ".".
+
+ 2. If files are renamed during the trace, then the name shown
+ is the most recent name for the file.
+
+ 3. If files are removed during the trace, they are still
+ reported in the summary.
+
+ This can lead to an error: if a directory is deleted,
+ and then another directory is created elsewhere with a
+ new name but the same inode number, this new directory
+ can "inherit" all the info about the files in the old
+ directory, including parentage. In the worst case,
+ this can cause a loop. The program will detect such
+ loops, and therefore not get caught forever, but it
+ doesn't do anything clever about them.
--- /dev/null
+#!/usr/bin/perl\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_loc2gmt,v 1.6 2003/07/28 14:27:16 ellard Exp $\r
+#\r
+# A helper application used by ns_quickview to convert from local time\r
+# to GMT. nfsdump records time as localtime, but gnuplot only know\r
+# how to deal with dates expressed as Greenwich Mean Time (which it\r
+# then displays using the local time, for some reason).\r
+#\r
+# This is a fragile tool -- it must be run in the same time zone in\r
+# which the data was collected via nfsdump, or else it will not do the\r
+# proper conversion. Improvements welcomed!\r
+#\r
+# The 'C' (count) and 'L' (latency) records use the second column for\r
+# dates, expressed as seconds.microseconds in localtime. The seconds\r
+# portion is the only part of the data modified by this program. \r
+# Comment lines are passed through unaltered.\r
+#\r
+# There is no error checking. Garbage in, garbage out.\r
+#\r
+# Note - we're throwing the microseconds away.\r
+\r
+\r
+use Getopt::Std;\r
+require 'timelocal.pl';\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] [table1.ns [table2.ns ... ]]\r
+\r
+If no table files are specified, then the input is read from stdin.\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+-d secs Time offset, in seconds, to subtract from each time\r
+ in the input tables. Note that all times are rounded\r
+ down to the nearest second.\r
+\r
+IMPORTANT NOTE: this must be run in the same time zone in which the\r
+data was collected via nfsdump, or else it will not do the proper\r
+conversion unless the -d option is used.\r
+\r
+.\r
+\r
+$Options = "d:";\r
+if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+}\r
+if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+}\r
+\r
+$TimeOffset = 0;\r
+if (defined $opt_d) {\r
+ $TimeOffset = int ($opt_d);\r
+}\r
+\r
+while ($line = <>) {\r
+ if ($line =~ /^#/) {\r
+ print $line;\r
+ next;\r
+ }\r
+\r
+ @arr = split (' ', $line);\r
+\r
+ ($secs, $usec) = split (/\./, $arr[1]);\r
+ $secs -= $TimeOffset;\r
+\r
+ $arr[1] = &timegm (localtime ($secs));\r
+\r
+ print join (' ', @arr);\r
+ print "\n";\r
+}\r
+\r
+exit (0);\r
+\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_quickview,v 1.9 2003/07/28 14:27:17 ellard Exp $\r
+\r
+use Getopt::Std;\r
+\r
+$ProgDir = $0;\r
+$ProgDir =~ /(^.*)\//;\r
+$ProgDir = $1;\r
+if (!$ProgDir) {\r
+ $ProgDir = ".";\r
+}\r
+\r
+$GNUPLOT_PATH = "gnuplot";\r
+$GNUPLOT_OPTS = "-persist";\r
+$LOC2GMT_PATH = "$ProgDir/ns_loc2gmt";\r
+\r
+$OUTPUT = "qv.ps";\r
+$GP_OUTPUT = "qv.gp";\r
+$TERMINAL = "postscript";\r
+$SIZE = undef;\r
+$EXTRA_CMD = undef;\r
+$TITLE_TEXT = undef;\r
+$PLOT_OPTS = '';\r
+@OPS = ( 7 );\r
+@SCALES = ();\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] table1.ns [table2.ns ...]\r
+\r
+At least one table file must be specified.\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+-c cmd Pass an extra command directly to gnuplot.\r
+\r
+-g Save the gnuplot commands to $GP_OUTPUT instead\r
+ of executing the immediately.\r
+\r
+-k Show the key for each line on the plot. The default\r
+ is not to show the key.\r
+\r
+-l If more than one input file is specified, superimpose\r
+ the plot for each so that they all appear to begin at\r
+ the same time as the first row in the first file.\r
+\r
+-o filename Save the plot to the specified file. The default\r
+ output file is \"$OUTPUT\".\r
+\r
+-O op1,...,opN Plot the specified columns from the table. The\r
+ default is $OPS[0], the total operation count.\r
+ Note that the operations must be specified by column\r
+ number, not by name.\r
+\r
+-t terminal Create a plot for the given gnuplot terminal specification.\r
+ The default is \"$TERMINAL\". See the gnuplot\r
+ documentation for more information.\r
+\r
+-T string Use the specified string as the title for the plot.\r
+ The default is no title.\r
+\r
+-s sizespec Set the output size specifier. See the gnuplot\r
+ documentation for more information.\r
+\r
+-S s1,..,sN Set the scaling factor for each file. This is useful\r
+ for plotting files aggregated across different time\r
+ intervals on the same scale.\r
+\r
+.\r
+\r
+$Options = "c:ghklo:O:P:t:T:s:S:";\r
+if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+}\r
+if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+}\r
+\r
+$EXTRA_CMD = $opt_c if (defined $opt_c);\r
+$OUTPUT = $opt_o if (defined $opt_o);\r
+$TERMINAL = $opt_t if (defined $opt_t);\r
+$SIZE = $opt_s if (defined $opt_s);\r
+$TITLE_TEXT = $opt_T if (defined $opt_T);\r
+$PLOT_OPTS = $opt_P if (defined $opt_P);\r
+@OPS = split (/,/, $opt_O) if (defined $opt_O);\r
+@SCALES = split (/,/, $opt_S) if (defined $opt_S);\r
+\r
+$SAVE_GNUPLOT = defined $opt_g;\r
+$SHOW_KEY = defined $opt_k;\r
+$LAYER = defined $opt_l;\r
+\r
+@FILES = @ARGV;\r
+\r
+if (@FILES == 0) {\r
+ print STDERR "No tables to plot?\n";\r
+ exit (1);\r
+}\r
+\r
+&makePlot;\r
+exit (0);\r
+\r
+sub makePlot {\r
+ my @starts = ();\r
+ my @ends = ();\r
+\r
+ my $nfiles = @FILES;\r
+ my $nops = @OPS;\r
+\r
+ if ($SAVE_GNUPLOT) {\r
+ die unless open (PLOT, ">$GP_OUTPUT");\r
+ }\r
+ else {\r
+ die unless open (PLOT, "|$GNUPLOT_PATH $GNUPLOT_OPTS");\r
+ }\r
+\r
+ $preamble = `cat $ProgDir/template.gp`;\r
+ print PLOT $preamble;\r
+\r
+ print PLOT "set terminal $TERMINAL\n";\r
+ print PLOT "set output \'$OUTPUT\'\n";\r
+ if (defined $SIZE) {\r
+ print PLOT "set size $SIZE\n";\r
+ }\r
+\r
+ if (defined $TITLE_TEXT) {\r
+ print PLOT "set title \"$TITLE_TEXT\"\n";\r
+ }\r
+\r
+ if (defined $EXTRA_CMD) {\r
+ print PLOT "$EXTRA_CMD\n";\r
+ }\r
+\r
+ print PLOT "set key below\n";\r
+\r
+ if ($LAYER) {\r
+ for ($i = 0; $i < $nfiles; $i++) {\r
+ ($starts [$i], $ends [$i]) =\r
+ findFileTimeSpan ($FILES [$i]);\r
+ }\r
+ }\r
+\r
+ print PLOT "plot $PLOT_OPTS \\\n";\r
+ for ($i = 0; $i < $nfiles; $i++) {\r
+ my $offset = 0;\r
+ my $scale = 1;\r
+ if (defined $SCALES[$i]) {\r
+ $scale = $SCALES[$i];\r
+ }\r
+\r
+ my $file = $FILES [$i];\r
+\r
+ if ($LAYER) {\r
+ $offset = $starts [$i] - $starts [0];\r
+ }\r
+\r
+ for ($j = 0; $j < @OPS; $j++) {\r
+ my $op = $OPS[$j];\r
+\r
+ print PLOT "\t\"< $LOC2GMT_PATH -d $offset $file\" \\\n";\r
+ print PLOT "\t\tusing 2:(\$$op*$scale) \\\n";\r
+ if ($SHOW_KEY) {\r
+ print PLOT "\t\ttitle \'$file:$op\'\\\n";\r
+ }\r
+ print PLOT "\t\twith lines lw 2";\r
+ if ($i != $nfiles - 1 || $j != $nops - 1) {\r
+ print PLOT ",\\\n";\r
+ }\r
+ else {\r
+ print PLOT "\n";\r
+ }\r
+ }\r
+ }\r
+ print PLOT "\n";\r
+ close PLOT;\r
+}\r
+\r
+# Find the earliest and latest times in a file created by nfsscan, and\r
+# return them as a list. Returns the empty list if anything goes\r
+# wrong.\r
+\r
+sub findFileTimeSpan {\r
+ my ($fname) = @_;\r
+ my ($smallest, $largest) = (-1, -1);\r
+\r
+ if (! open (FF, "<$fname")) {\r
+ return ();\r
+ }\r
+\r
+ while (my $l = <FF>) {\r
+ if ($l =~ /^#/) {\r
+ next;\r
+ }\r
+\r
+ @a = split (' ', $l);\r
+\r
+ if ($smallest < 0 || $smallest > $a[1]) {\r
+ $smallest = $a[1];\r
+ }\r
+ if ($largest < 0 || $largest < $a[1]) {\r
+ $largest = $a[1];\r
+ }\r
+ }\r
+\r
+ close FF;\r
+\r
+ if ((! defined $smallest) || (! defined $largest)) {\r
+ return ();\r
+ }\r
+\r
+ return ($smallest, $largest);\r
+}\r
+\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_split,v 1.4 2003/07/28 14:27:17 ellard Exp $\r
+\r
+use Getopt::Std;\r
+\r
+$ProgDir = $0;\r
+$ProgDir =~ /(^.*)\//;\r
+$ProgDir = $1;\r
+if (!$ProgDir) {\r
+ $ProgDir = ".";\r
+}\r
+\r
+require "$ProgDir/userUtils.pl";\r
+\r
+@ADD_USERS = ();\r
+@DEL_USERS = ();\r
+@ADD_GROUPS = ();\r
+@DEL_GROUPS = ();\r
+@ADD_CLIENTS = ();\r
+@DEL_CLIENTS = ();\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] [table1.ns [table2.ns ... ]]\r
+\r
+If no table files are specified, then the input is read from stdin.\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+-c c1[,c2]* Include only activity performed by the specified clients.\r
+\r
+-C c1[,c2]* Exclude activity performed by the specified clients.\r
+\r
+-g g1[,g2]* Include only activity performed by the specified groups.\r
+\r
+-G g1[,g2]* Exclude activity performed by the specified groups.\r
+\r
+-u u1[,u2]* Include only activity performed by the specified users.\r
+\r
+-U u1[,u2]* Exclude activity performed by the specified users.\r
+\r
+.\r
+\r
+$cmdline = "$0 " . join (' ', @ARGV);\r
+$Options = "c:C:g:G:u:U::";\r
+if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+exit (1);\r
+}\r
+if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+}\r
+\r
+if (defined $opt_u) {\r
+ @ADD_USERS = &logins2uids ($opt_u);\r
+}\r
+if (defined $opt_U) {\r
+ @DEL_USERS = &logins2uids ($opt_U);\r
+}\r
+if (defined $opt_g) {\r
+ @ADD_GROUPS = &groups2gids ($opt_g);\r
+}\r
+if (defined $opt_G) {\r
+ @DEL_GROUPS = &groups2gids ($opt_G);\r
+}\r
+if (defined $opt_c) {\r
+ @ADD_CLIENTS = split (/,/, $opt_c);\r
+}\r
+if (defined $opt_C) {\r
+ @DEL_CLIENTS = split (/,/, $opt_C);\r
+}\r
+\r
+print "#cmdline $cmdline\n";\r
+\r
+while ($l = <>) {\r
+ if ($l =~ /^#/) {\r
+ print $l;\r
+ next;\r
+ }\r
+\r
+ my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l);\r
+\r
+ next unless ($type eq 'C');\r
+\r
+ # Something wrong with the input?\r
+\r
+ next if (@vals == 0);\r
+\r
+ if (saveRecord ($client, $fh, $euid, $egid)) {\r
+ print $l;\r
+ }\r
+}\r
+\r
+sub saveRecord {\r
+ my ($client, $fh, $euid, $egid) = @_;\r
+\r
+ if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) {\r
+ return 0;\r
+ }\r
+\r
+ if (@ADD_USERS && !grep (/^$euid$/, @ADD_USERS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_USERS && grep (/^$euid$/, @DEL_USERS)) {\r
+ return 0;\r
+ }\r
+\r
+ if (@ADD_GROUPS && !grep (/^$egid$/, @ADD_GROUPS)) {\r
+ return 0;\r
+ }\r
+ if (@DEL_GROUPS && grep (/^$egid$/, @DEL_GROUPS)) {\r
+ return 0;\r
+ }\r
+\r
+ return (1);\r
+}\r
+\r
+exit 0;\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_timeagg,v 1.7 2003/07/28 14:27:17 ellard Exp $\r
+\r
+use Getopt::Std;\r
+\r
+$ProgDir = $0;\r
+$ProgDir =~ /(^.*)\//;\r
+$ProgDir = $1;\r
+if (!$ProgDir) {\r
+ $ProgDir = ".";\r
+}\r
+\r
+require "$ProgDir/common.pl";\r
+\r
+$UseClient = 0;\r
+$UseFH = 0;\r
+$UseUID = 0;\r
+$UseGID = 0;\r
+$NEW_INTERVAL = 300;\r
+$OMIT_ZEROS = 0;\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] [table1.ns [table2.ns ... ]]\r
+\r
+If no table files are specified, then the input is read from stdin.\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+-B flags Choose fields to NOT aggregate. The fields are:\r
+ C - client host ID\r
+ U - Effective user ID\r
+ G - Effective group ID\r
+ F - File handle\r
+\r
+-R secs Round the start time to the closest multiple of\r
+ the given number of seconds. This is useful for\r
+ dealing with small amounts of clock drift. For example,\r
+ if the trace starts at 12:00:01 instead of 12:00:00,\r
+ it is probably convenient to pretend that it started\r
+ at 12:00:00.\r
+\r
+-t secs Aggregate over the given number of seconds. The default\r
+ is $NEW_INTERVAL. If the current interval does not\r
+ divide the new interval, warning messages are printed\r
+ and the output might not be meaningful.\r
+\r
+ If secs is 0, then the total for the entire table is\r
+ computed.\r
+\r
+-Z Omit count lines that have a zero total operation count.\r
+.\r
+\r
+$cmdline = "$0 " . join (' ', @ARGV);\r
+$Options = "B:hR:t:Z";\r
+if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+}\r
+if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+}\r
+\r
+if (defined $opt_t) {\r
+ if ($opt_t <= 0) {\r
+ $EndTime = 0;\r
+ }\r
+ $NEW_INTERVAL = $opt_t;\r
+}\r
+\r
+$TIME_ROUNDING = defined $opt_R ? $opt_R : 0;\r
+\r
+if (defined $opt_B) {\r
+ $UseClient = ($opt_B =~ /C/);\r
+ $UseUID = ($opt_B =~ /U/);\r
+ $UseGID = ($opt_B =~ /G/);\r
+ $UseFH = ($opt_B =~ /F/);\r
+}\r
+\r
+# We need to let the measurement of time be a little bit inexact, so\r
+# that a small rounding error or truncation doesn't throw everything\r
+# off.\r
+\r
+$TimerInacc = 0.05;\r
+\r
+# Print out the commandline as a comment in the output.\r
+\r
+print "#cmdline $cmdline\n";\r
+\r
+# It would be nice to make this do the right thing with Latency lines,\r
+# and perhaps file op counts.\r
+\r
+while ($l = <>) {\r
+ if ($l =~ /^#C/) {\r
+ print $l;\r
+ next;\r
+ }\r
+\r
+ next if ($l =~ /^#/);\r
+\r
+ my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l);\r
+\r
+ next unless ($type eq 'C');\r
+\r
+ # Something wrong with the input?\r
+\r
+ next if (@vals == 0);\r
+\r
+ if (!defined $StartTime) {\r
+ $StartTime = findStartTime ($time, $TIME_ROUNDING);\r
+ if ($NEW_INTERVAL > 0) {\r
+ $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc;\r
+ }\r
+ }\r
+\r
+ if ($EndTime > 0 && $time >= $EndTime) {\r
+ my $diff = int ($time - $StartTime);\r
+ if ($diff > 0) {\r
+ if ($NEW_INTERVAL % $diff != 0) {\r
+ print STDERR "$0: time interval mismatch ";\r
+ print STDERR "(from $diff to $NEW_INTERVAL)\n";\r
+ }\r
+ }\r
+\r
+ dumpKeys ($StartTime);\r
+\r
+ $StartTime += $NEW_INTERVAL;\r
+ $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc;\r
+ }\r
+\r
+\r
+ # Aggregate across clients, files, users, and groups unless\r
+ # specifically asked NOT to.\r
+\r
+ $client = $UseClient ? $client : 'u';\r
+ $fh = $UseFH ? $fh : 'u';\r
+ $euid = $UseUID ? $euid : 'u';\r
+ $egid = $UseGID ? $egid : 'u';\r
+\r
+ $key = "$client,$fh,$euid,$egid";\r
+\r
+ if (exists $Totals{$key}) {\r
+ my (@tots) = split (' ', $Totals{$key});\r
+ for (my $i = 0; $i < @vals; $i++) {\r
+ $tots[$i] += $vals[$i];\r
+ }\r
+ $Totals{$key} = join (' ', @tots);\r
+ }\r
+ else {\r
+ $Totals{$key} = join (' ', @vals);\r
+ }\r
+}\r
+\r
+if ($EndTime <= 0) {\r
+ dumpKeys ($StartTime);\r
+}\r
+\r
+sub dumpKeys {\r
+ my ($start) = @_;\r
+ my ($i, $k, $ks);\r
+\r
+ foreach $k ( keys %Totals ) {\r
+ my (@v) = split (' ', $Totals{$k});\r
+\r
+ if ($OMIT_ZEROS && $v[0] == 0) {\r
+ next;\r
+ }\r
+\r
+ $ks = $k;\r
+ $ks =~ s/,/\ /g;\r
+\r
+ print "C $start $ks";\r
+\r
+ for ($i = 0; $i < @v; $i++) {\r
+ print " $v[$i]";\r
+ }\r
+ print "\n";\r
+\r
+ for ($i = 0; $i < @v; $i++) {\r
+ $v[$i] = 0;\r
+ }\r
+ $Totals{$k} = join (' ', @v);\r
+ }\r
+}\r
+\r
+exit 0;\r
--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: ns_tsplit,v 1.4 2003/07/26 20:52:04 ellard Exp $\r
+\r
+use Getopt::Std;\r
+use Time::Local;\r
+\r
+$ProgDir = $0;\r
+$ProgDir =~ /(^.*)\//;\r
+$ProgDir = $1;\r
+if (!$ProgDir) {\r
+ $ProgDir = ".";\r
+}\r
+\r
+# Set the default start and end times.\r
+\r
+$DEF_BEGIN_TIME = "20000101:00:00:00";\r
+$DEF_END_TIME = "20360101:00:00:00";\r
+\r
+# The range includes everything from the start time up to and less\r
+# than the end time (so if you want one day, it is correct to go\r
+# midnight to midnight, instead of going until 23:59:59).\r
+\r
+$Usage =<< ".";\r
+\r
+Usage: $0 [options] rangeSpec... - [table.ns [table.ns...]]\r
+\r
+Command line options:\r
+\r
+-h Print usage message and exit.\r
+\r
+Any number of time range specifier can be provided. The special\r
+symbol "-" is used to separated the time specifiers from the input\r
+file names. If input is taken from stdin, then the "-" may be\r
+omitted.\r
+\r
+The basic time range specification format is: StartTime-EndTime where\r
+StartTime and EndTime have the form YYMMDD[:HH[:MM[:SS]]]. All\r
+records from the input that have a timestamp greater than or equal to\r
+StartTime and less than EndTime are printed to stdout.\r
+\r
+If StartTime is omitted, $DEF_BEGIN_TIME is used.\r
+If EndTime is omitted, $DEF_END_TIME is used.\r
+\r
+Note that omitting both the StartTime and EndTime results in a rangeSpec\r
+of "-", which is the special symbol that marks the end of the rangeSpecs.\r
+.\r
+\r
+$cmdline = "$0 " . join (' ', @ARGV);\r
+$Options = "h";\r
+if (! getopts ($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+}\r
+if (defined $opt_h) {\r
+ print $Usage;\r
+ exit (0);\r
+}\r
+\r
+@StartTimes = ();\r
+@EndTimes = ();\r
+\r
+while ($ts = shift @ARGV) {\r
+ last if ($ts eq '-');\r
+\r
+ my ($ts_s, $ts_e) = split (/-/, $ts);\r
+ my $s;\r
+\r
+ if ($ts_s eq '') {\r
+ $ts_s = $DEF_START_TIME;\r
+ }\r
+ $s = &ts2secs ($ts_s);\r
+ if (! defined $s) {\r
+ print STDERR "Failed to translate ($ts_s)\n";\r
+ exit (1);\r
+ }\r
+ push @StartTimes, $s;\r
+\r
+ if ($ts_e eq '') {\r
+ $ts_e = $DEF_END_TIME;\r
+ }\r
+ $s = &ts2secs ($ts_e);\r
+ if (! defined $s) {\r
+ print STDERR "Failed to translate ($ts_e)\n";\r
+ exit (1);\r
+ }\r
+\r
+ push @EndTimes, $s;\r
+}\r
+\r
+print "#cmdline $cmdline\n";\r
+\r
+while ($l = <>) {\r
+ if ($l =~ /^#/) {\r
+ print $l;\r
+ next;\r
+ }\r
+\r
+ my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l);\r
+\r
+ next unless ($type eq 'C');\r
+\r
+ # Something wrong with the input?\r
+\r
+ next if (@vals == 0);\r
+\r
+ if (saveRecord ($time)) {\r
+ print $l;\r
+ }\r
+}\r
+\r
+exit 0;\r
+\r
+sub saveRecord {\r
+ my ($t) = @_;\r
+\r
+ for (my $i = 0; $i < @StartTimes ; $i++) {\r
+ if (($StartTimes[$i] <= $t) && ($t < $EndTimes[$i])) {\r
+ return (1);\r
+ }\r
+ }\r
+\r
+ return (0);\r
+}\r
+\r
+# This is ugly!\r
+#\r
+# The basic time specification format is: YYMMDD[:HH[:MM[:SS]]].\r
+#\r
+# To make a time range, join two time specs with a -.\r
+#\r
+# The data spec is Y2.01K compliant... I'm assuming that all the\r
+# traces processed by this tool will be gathered before 2010. If\r
+# anyone is still gathering NFSv3 traces in 2010, I'm sorry.\r
+\r
+sub ts2secs {\r
+ my ($ts) = @_;\r
+ my ($hour, $min, $sec) = (0, 0, 0);\r
+\r
+ my (@d) = split (/:/, $ts);\r
+\r
+ if ($d[0] =~ /([0-9][0-9])([0-2][0-9])([0-3][0-9])/) {\r
+ $year = $1 + 100;\r
+ $mon = $2 - 1;\r
+ $day = $3;\r
+ }\r
+ else {\r
+ return undef;\r
+ }\r
+\r
+ if (@d > 1) {\r
+ if ($d[1] =~ /([0-2][0-9])/) {\r
+ $hour = $1;\r
+ }\r
+ else {\r
+ return undef;\r
+ }\r
+ }\r
+\r
+ if (@d > 2) {\r
+ if ($d[2] =~ /([0-5][0-9])/) {\r
+ $min = $1;\r
+ }\r
+ else {\r
+ return undef;\r
+ }\r
+ }\r
+\r
+ if (@d > 3) {\r
+ if ($d[3] =~ /([0-5][0-9])/) {\r
+ $sec = $1;\r
+ }\r
+ else {\r
+ return undef;\r
+ }\r
+ }\r
+\r
+ my $s = timelocal ($sec, $min, $hour, $day, $mon, $year,\r
+ undef, undef, undef);\r
+\r
+ my ($s2, $m2, $h2, $md2, $mon2, $y2, $isdst) = localtime ($s);\r
+ if (($s2 != $sec) || $min != $m2 || $hour != $h2 || $day != $md2) {\r
+ print "failed 1!\n";\r
+ }\r
+ if (($mon != $mon2) || $year != $y2) {\r
+ print "failed 2!\n";\r
+ }\r
+\r
+ return ($s);\r
+}\r
+\r
--- /dev/null
+#!/usr/bin/perl -w\r
+\r
+use Getopt::Std;\r
+$Usage =<< "EOF";\r
+\r
+Usage: $0 [options]\r
+\r
+This program runs after nfsscan.pl. It outputs the file system hierarchy (RFSFS) as well as\r
+the file system hierarchy map (stored in three files: fh-path-map, noattrdirdiscard, noattrdir-root).\r
+\r
+This perl program do post processing based on this file\r
+\r
+Command line options:\r
+\r
+-h Print Usage message and exit.\r
+\r
+-S The file in file system hierarchy are all of size 0. \r
+ Without -S, the files are written to it full length.\r
+\r
+\r
+EOF\r
+\r
+$writebuf = "R" x 8193;\r
+$writeBlockSize = 8192;\r
+\r
+main ();\r
+\r
+sub rfsCreateSymlink()\r
+{\r
+ my ($path, $pathcnt, $sizeHexStr) = @_;\r
+ $sizeHexStr = "0x".$sizeHexStr;\r
+\r
+ my $dir = $path;\r
+ my $name = '';\r
+ my $cmdbuf;\r
+\r
+ $dir =~ /(^.*)\/(.*)/;\r
+ $dir = $1;\r
+ $name = $2;\r
+ if (!$dir) {$dir = ".";}\r
+ die "name is empty\n" if (!$name);\r
+ #print "path($path) dir($dir) name($name)\n";\r
+\r
+ if (! -e $dir) {\r
+ $cmdbuf = "mkdir -p $dir";\r
+ system $cmdbuf;\r
+ print "RFS: Warning: the directory should be created already: $path\n";\r
+ } else {\r
+ die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir));\r
+ }\r
+\r
+ my $size = hex($sizeHexStr);\r
+ #print "size($sizeHexStr) lp($lp) rem($remSize)\n";\r
+\r
+ my $pathnamebuf = $sizeHexStr;\r
+ symlink ($pathnamebuf, $path);\r
+ #if (! symlink ($pathnamebuf, $path) ) {\r
+ # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n";\r
+ #}\r
+}\r
+\r
+\r
+sub rfsCreateFile()\r
+{\r
+ my ($path, $pathcnt, $sizeHexStr) = @_;\r
+ $sizeHexStr = "0x".$sizeHexStr;\r
+\r
+ my $dir = $path;\r
+ my $name = '';\r
+ my $cmdbuf;\r
+\r
+ $dir =~ /(^.*)\/(.*)/;\r
+ $dir = $1;\r
+ $name = $2;\r
+ if (!$dir) {$dir = ".";}\r
+ die "name is empty\n" if (!$name);\r
+ #print "path($path) dir($dir) name($name)\n";\r
+\r
+ if (! -e $dir) {\r
+ $cmdbuf = "mkdir -p $dir";\r
+ system $cmdbuf;\r
+ print "RFS: Warning: the directory should be created already: $path\n";\r
+ } else {\r
+ die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir));\r
+ }\r
+\r
+ my $size = hex($sizeHexStr);\r
+ my $remSize = $size % $writeBlockSize;\r
+ my $lp = ($size - $remSize) / $writeBlockSize;\r
+ #print "size($sizeHexStr) lp($lp) rem($remSize)\n";\r
+\r
+ open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write";\r
+\r
+ # the eight lines below should not be commented out \r
+ if (!defined $opt_S) {\r
+ my $i = 0;\r
+ for ($i = 0; $i < $lp; $i++) {\r
+ syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize);\r
+ }\r
+ if ($remSize) {\r
+ syswrite(RFSTMPWRITE, $writebuf, $remSize);\r
+ #print "write ($remSize) byte\n";\r
+ }\r
+ close RFSTMPWRITE;\r
+ }\r
+}\r
+\r
+# Useful commands:\r
+# sort -n -k a,b -c -u \r
+# \r
+# *** WARNING *** The locale specified by the environment affects sort\r
+# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native\r
+# byte values.\r
+\r
+sub parseArgs {\r
+ my $Options = "S";\r
+ if (! getopts($Options)) {\r
+ print STDERR "$0: Incorrect usage.\n";\r
+ print STDERR $Usage;\r
+ exit (1);\r
+ }\r
+}\r
+\r
+sub main {\r
+\r
+ parseArgs ();\r
+\r
+ my $cmdbuf;\r
+\r
+ # format of lines in test.fil:\r
+ # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr()\r
+ # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14))\r
+ # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22))\r
+\r
+ # skip comment lines\r
+ # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file \r
+\r
+ $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # sort the test.fil according to path name\r
+ $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # keep the interested field only\r
+ # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e)\r
+ $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # skip comment lines\r
+ # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file \r
+\r
+ $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # sort the active.fil according to path name\r
+ $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # keep the interested field only\r
+ # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink)\r
+ $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # format output files\r
+ $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ $cmdbuf = 'export LC_ALL=C; mv noattrdir-root noattrdir-root-tmp; sort -u -k 1,1 noattrdir-root-tmp > noattrdir-root; echo $LC_ALL; rm -f noattrdir-root-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh';\r
+ $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ $cmdbuf = "cut -d ' ' -f '1 2' fh-path-map > fh-path-map-tmp";\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ $cmdbuf = 'cat noattrdirdiscard noattrdir-root fh-path-map-tmp > fh-path-map-play; rm -rf fh-path-map-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ #exit(0);\r
+ \r
+ # so far, you got the following information\r
+ # in active: all files/dirs active \r
+ # in noattrdir-root: a set of fhs pointing to RFSNN0\r
+ # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0)\r
+ # a set of file fhs should be discard due to short of information\r
+\r
+ # create the active fs\r
+ # 1. BASEDIR/RFSNN0\r
+ # 2. BASEDIR/active\r
+ $cmdbuf = "mkdir -p RFSFS/RFSNN0";\r
+ system $cmdbuf;\r
+ open RFS_ACTIVE, "active" || die "open active failed\n";\r
+ while (<RFS_ACTIVE>) {\r
+ chop;\r
+ my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7);\r
+ if ($type==2) {\r
+ $cmdbuf = "mkdir -p RFSFS$path";\r
+ system $cmdbuf;\r
+ }elsif ($type==1){\r
+ &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr);\r
+ }elsif ($type==5){\r
+ #print STDERR "SYMLINK: $type fh: $fh path: $path\n";\r
+ &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr);\r
+ }else {\r
+ print STDERR "special file: $type fh: $fh path: $path\n";\r
+ }\r
+\r
+ my $line_num=0;\r
+ $line_num++;\r
+ if ( ($line_num %100)==0 ) {\r
+ print STDERR "$line_num\n";\r
+ }\r
+ \r
+ }\r
+\r
+ # create map table: key (fh), value (path/fn)\r
+\r
+ # check whether there is fh that is not mapped to any path/fn in the trace\r
+\r
+ # simulate a replay of trace\r
+\r
+ return;\r
+ \r
+}\r
+\r
+1;\r
--- /dev/null
+#!/usr/bin/perl -w\r
+\r
+$Usage =<< ".";\r
+\r
+This is the rfs program\r
+\r
+After run nfsscan, you will get a file-list file in the output\r
+\r
+This perl program do post processing based on this file\r
+\r
+\r
+.\r
+\r
+$writebuf = "R" x 8193;\r
+$writeBlockSize = 8192;\r
+\r
+\r
+main ();\r
+\r
+sub rfsCreateFile()\r
+{\r
+ my ($path, $pathcnt, $sizeHexStr) = @_;\r
+ $sizeHexStr = "0x".$sizeHexStr;\r
+\r
+ my $dir = $path;\r
+ my $name = '';\r
+ my $cmdbuf;\r
+\r
+ $dir =~ /(^.*)\/(.*)/;\r
+ $dir = $1;\r
+ $name = $2;\r
+ if (!$dir) {$dir = ".";}\r
+ die "name is empty\n" if (!$name);\r
+ #print "path($path) dir($dir) name($name)\n";\r
+\r
+ if (! -e $dir) {\r
+ $cmdbuf = "mkdir -p $dir";\r
+ system $cmdbuf;\r
+ print "RFS: Warning: the directory should be created already: $path\n";\r
+ } else {\r
+ die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir));\r
+ }\r
+\r
+ my $size = hex($sizeHexStr);\r
+ my $remSize = $size % $writeBlockSize;\r
+ my $lp = ($size - $remSize) / $writeBlockSize;\r
+ #print "size($sizeHexStr) lp($lp) rem($remSize)\n";\r
+\r
+ open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write";\r
+\r
+ my $i = 0;\r
+ for ($i = 0; $i < $lp; $i++) {\r
+ syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize);\r
+ }\r
+ if ($remSize) {\r
+ syswrite(RFSTMPWRITE, $writebuf, $remSize);\r
+ #print "write ($remSize) byte\n";\r
+ }\r
+ close RFSTMPWRITE;\r
+}\r
+\r
+# Useful commands:\r
+# sort -n -k a,b -c -u \r
+# \r
+#\r
+\r
+sub main {\r
+\r
+ my $cmdbuf;\r
+ \r
+ # skip comment lines\r
+ # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir") skip the file \r
+\r
+ $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir") print $0 }\' test.fil > active.fil';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # sort the active.fil according to path_count\r
+ $cmdbuf = 'sort -k 5,5 active.fil > active.fil.order';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'sort -n -k 6,6 active.fil > active.fil.order-pathcnt';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # keep the interested field only\r
+ # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode)\r
+ $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order > fh-path-map';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order-pathcnt > active-pathcnt';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order-pathcnt > fh-path-map-pathcnt';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ #exit(0);\r
+ \r
+ # so far, you got the following information\r
+ # in active: all files/dirs active \r
+ # in noattrdir-root: a set of fhs pointing to RFSNN0\r
+ # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0)\r
+ # a set of file fhs should be discard due to short of information\r
+\r
+ # create the active fs\r
+ # 1. BASEDIR/RFSNN0\r
+ # 2. BASEDIR/active\r
+ $cmdbuf = "mkdir -p RFSFS/RFSNN0";\r
+ system $cmdbuf;\r
+ open RFS_ACTIVE, "active";\r
+ while (<RFS_ACTIVE>) {\r
+ chop;\r
+ my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7);\r
+ if ($type eq "D") {\r
+ $cmdbuf = "mkdir -p RFSFS$path";\r
+ system $cmdbuf;\r
+ }\r
+ else {\r
+ &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr);\r
+ }\r
+ \r
+ }\r
+\r
+ # create map table: key (fh), value (path/fn)\r
+\r
+ # check whether there is fh that is not mapped to any path/fn in the trace\r
+\r
+ # simulate a replay of trace\r
+\r
+ return;\r
+ \r
+}\r
+\r
+1;\r
--- /dev/null
+#!/usr/bin/perl -w\r
+\r
+$Usage =<< ".";\r
+\r
+This is the rfs program\r
+\r
+After run nfsscan, you will get a file-list file in the output\r
+\r
+This perl program do post processing based on this file\r
+\r
+\r
+.\r
+\r
+$writebuf = "R" x 8193;\r
+$writeBlockSize = 8192;\r
+\r
+main ();\r
+\r
+sub rfsCreateSymlink()\r
+{\r
+ my ($path, $pathcnt, $sizeHexStr) = @_;\r
+ $sizeHexStr = "0x".$sizeHexStr;\r
+\r
+ my $dir = $path;\r
+ my $name = '';\r
+ my $cmdbuf;\r
+\r
+ $dir =~ /(^.*)\/(.*)/;\r
+ $dir = $1;\r
+ $name = $2;\r
+ if (!$dir) {$dir = ".";}\r
+ die "name is empty\n" if (!$name);\r
+ #print "path($path) dir($dir) name($name)\n";\r
+\r
+ if (! -e $dir) {\r
+ $cmdbuf = "mkdir -p $dir";\r
+ system $cmdbuf;\r
+ print "RFS: Warning: the directory should be created already: $path\n";\r
+ } else {\r
+ die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir));\r
+ }\r
+\r
+ my $size = hex($sizeHexStr);\r
+ #print "size($sizeHexStr) lp($lp) rem($remSize)\n";\r
+\r
+ my $pathnamebuf = $sizeHexStr;\r
+ symlink ($pathnamebuf, $path);\r
+ #if (! symlink ($pathnamebuf, $path) ) {\r
+ # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n";\r
+ #}\r
+}\r
+\r
+\r
+sub rfsCreateFile()\r
+{\r
+ my ($path, $pathcnt, $sizeHexStr) = @_;\r
+ $sizeHexStr = "0x".$sizeHexStr;\r
+\r
+ my $dir = $path;\r
+ my $name = '';\r
+ my $cmdbuf;\r
+\r
+ $dir =~ /(^.*)\/(.*)/;\r
+ $dir = $1;\r
+ $name = $2;\r
+ if (!$dir) {$dir = ".";}\r
+ die "name is empty\n" if (!$name);\r
+ #print "path($path) dir($dir) name($name)\n";\r
+\r
+ if (! -e $dir) {\r
+ $cmdbuf = "mkdir -p $dir";\r
+ system $cmdbuf;\r
+ print "RFS: Warning: the directory should be created already: $path\n";\r
+ } else {\r
+ die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir));\r
+ }\r
+\r
+ my $size = hex($sizeHexStr);\r
+ my $remSize = $size % $writeBlockSize;\r
+ my $lp = ($size - $remSize) / $writeBlockSize;\r
+ #print "size($sizeHexStr) lp($lp) rem($remSize)\n";\r
+\r
+ open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write";\r
+\r
+ # the eight lines below should not be commented out \r
+ #my $i = 0;\r
+ #for ($i = 0; $i < $lp; $i++) {\r
+ # syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize);\r
+ #}\r
+ #if ($remSize) {\r
+ # syswrite(RFSTMPWRITE, $writebuf, $remSize);\r
+ # #print "write ($remSize) byte\n";\r
+ #}\r
+ close RFSTMPWRITE;\r
+}\r
+\r
+# Useful commands:\r
+# sort -n -k a,b -c -u \r
+# \r
+# *** WARNING *** The locale specified by the environment affects sort\r
+# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native\r
+# byte values.\r
+\r
+sub main {\r
+\r
+ my $cmdbuf;\r
+\r
+ # format of lines in test.fil:\r
+ # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr()\r
+ # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14))\r
+ # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22))\r
+\r
+ # skip comment lines\r
+ # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file \r
+\r
+ $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # sort the test.fil according to path name\r
+ $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # keep the interested field only\r
+ # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e)\r
+ $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # skip comment lines\r
+ # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file \r
+\r
+ $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ # sort the active.fil according to path name\r
+ $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # keep the interested field only\r
+ # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink)\r
+ $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ # format output files\r
+ $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ $cmdbuf = 'export LC_ALL=C; mv noattrdir-root noattrdir-root-tmp; sort -u -k 1,1 noattrdir-root-tmp > noattrdir-root; echo $LC_ALL; rm -f noattrdir-root-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+\r
+ #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh';\r
+ $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp';\r
+ print "$cmdbuf\n";\r
+ system $cmdbuf;\r
+ \r
+ #exit(0);\r
+ \r
+ # so far, you got the following information\r
+ # in active: all files/dirs active \r
+ # in noattrdir-root: a set of fhs pointing to RFSNN0\r
+ # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0)\r
+ # a set of file fhs should be discard due to short of information\r
+\r
+ # create the active fs\r
+ # 1. BASEDIR/RFSNN0\r
+ # 2. BASEDIR/active\r
+ $cmdbuf = "mkdir -p RFSFS/RFSNN0";\r
+ system $cmdbuf;\r
+ open RFS_ACTIVE, "active" || die "open active failed\n";\r
+ while (<RFS_ACTIVE>) {\r
+ chop;\r
+ my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7);\r
+ if ($type==2) {\r
+ $cmdbuf = "mkdir -p RFSFS$path";\r
+ system $cmdbuf;\r
+ }elsif ($type==1){\r
+ &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr);\r
+ }elsif ($type==5){\r
+ #print STDERR "SYMLINK: $type fh: $fh path: $path\n";\r
+ &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr);\r
+ }else {\r
+ print STDERR "special file: $type fh: $fh path: $path\n";\r
+ }\r
+\r
+ my $line_num=0;\r
+ $line_num++;\r
+ if ( ($line_num %100)==0 ) {\r
+ print STDERR "$line_num\n";\r
+ }\r
+ \r
+ }\r
+\r
+ # create map table: key (fh), value (path/fn)\r
+\r
+ # check whether there is fh that is not mapped to any path/fn in the trace\r
+\r
+ # simulate a replay of trace\r
+\r
+ return;\r
+ \r
+}\r
+\r
+1;\r
--- /dev/null
+#cmdline nfsscan.pl
+#C time client fh euid egid TOTAL INTERESTING read write lookup getattr access create remove
--- /dev/null
+#cmdline nfsscan.pl
--- /dev/null
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: userUtils.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $\r
+#\r
+# utils.pl - deal with details like turning login names into uids.\r
+#\r
+\r
+sub logins2uids {\r
+ my ($str) = @_;\r
+\r
+ my @uids = ();\r
+ my @logins = split (/,/, $str);\r
+\r
+ my $j = 0;\r
+ for (my $i = 0; $i < @logins; $i++) {\r
+ if ($logins[$i] =~ /^[0-9]/) {\r
+ $uids[$j++] = $logins[$i];\r
+ }\r
+ else {\r
+ my ($login, $pw, $uid, $gid) = getpwnam ($logins[$i]);\r
+ if (defined $uid) {\r
+ $uids[$j++] = $uid;\r
+ }\r
+ else {\r
+ # &&& Warning or error?\r
+ print STDERR "WARNING: Unrecognized login ($logins[$i])\n";\r
+ $uids[$j++] = $logins[$i];\r
+ }\r
+ }\r
+ }\r
+\r
+ return (@uids);\r
+}\r
+\r
+sub groups2gids {\r
+ my ($str) = @_;\r
+\r
+ my @gids = ();\r
+ my @groups = split (/,/, $str);\r
+\r
+ for (my $i = 0; $i < @groups; $i++) {\r
+ if ($groups[$i] =~ /^[0-9]/) {\r
+ $gids[$j++] = $groups[$i];\r
+ }\r
+ else {\r
+ my $gid = getgrnam ($groups[$i]);\r
+ if (defined $gid) {\r
+ $gids[$j++] = $gid;\r
+ }\r
+ else {\r
+ # &&& Warning or error?\r
+ print STDERR "WARNING: Unrecognized group ($logins[$i])\n";\r
+ $gids[$j++] = $groups[$i];\r
+ }\r
+ }\r
+ }\r
+\r
+ return (@gids);\r
+}\r
+\r
+1;\r
--- /dev/null
+#
+# @(#)Makefile 2.1 97/10/23
+#
+# Makefile to build SFS benchmark suite
+#
+#
+# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+# All rights reserved.
+# Standard Performance Evaluation Corporation (SPEC)
+# 6585 Merchant Place, Suite 100
+# Warrenton, VA 20187
+#
+# This tape contains benchmarks acquired from several sources who
+# understand and agree with SPEC's goal of creating fair and objective
+# benchmarks to measure computer performance.
+#
+# This copyright notice is placed here only to protect SPEC in the
+# event the source is misused in any manner that is contrary to the
+# spirit, the goals and the intent of SPEC.
+#
+# The source code is provided to the user or company under the license
+# agreement for the SPEC Benchmark Suite for this tape.
+#
+#
+# *****************************************************************
+# * *
+# * Copyright 1991,1992 Legato Systems, Inc. *
+# * Copyright 1991,1992 Auspex Systems, Inc. *
+# * Copyright 1991,1992 Data General Corporation *
+# * Copyright 1991,1992 Digital Equipment Corporation *
+# * Copyright 1991,1992 Interphase Corporation *
+# * Copyright 1991,1992 Sun Microsystems, Inc. *
+# * *
+# *****************************************************************
+
+#
+# ----------------------- How to use this makefile --------------------
+#
+# The makefile is divided into 10 sections:
+# 1) basic information
+# 2) default compiler definitions
+# 3) vendor specific definitions
+# 4) OS feature specific definitions
+# 5) final compiler definitions (includes specific vendor and feature defs)
+# 6) source file definitions
+# 7) object file definitions
+# 8) rules and targets for building sfs
+# 9) lint, install, clean, misc rules
+
+
+# ---------------------- default compiler definitions -----------------
+#
+# For Solaris 2.X (SunOS 5.X) or later
+MACHID = solaris2
+CC=cc
+OPT = -O
+CFLAGS = -v -Xc -D__sparc -D_POSIX_SOURCE
+LDFLAGS =
+EXTRA_CFLAGS = -DPORTMAP
+EXTRA_LDFLAGS =
+EXTRA_LINTFLAGS =
+EXTRA_LIBS =
+RESVPORT_MOUNT=
+OSTYPE = -DSVR4 -DSOLARIS2
+INCDIR = -I.
+RPCLIB = rpc/librpclib.a
+LIBS = -lm -lsocket -lnsl
+
+#
+# ---------------------- OS feature specific definitions ------------------
+#
+
+#
+# A UNIX derivative that only supports the getfsent(3) family of calls
+# should have the USEFSTAB variable set.
+# A UNIX derivative that supports the getmntent(3) family of calls
+# should have the line commented out.
+#FSTAB = -DUSEFSTAB
+
+# SFS uses the setsid(2) system call to form a new proces group
+# This prevents 'synchronization' signals from causing parent shell (sh)
+# to exit. SETPGRP_CALL allows you to specify the alternative use
+# of the setpgrp(2) and which variation you are using.
+
+# If you are linking with BSD libraries then uncomment the next line.
+# SETPGRP_CALL = -DSETPGRP_BSD
+
+# If you are linking with SYSV type libraries then uncomment the next line.
+#SETPGRP_CALL = -DSETPGRP_SYSV
+
+# if your client is failing the mount even when run as root then you should
+# uncomment the RESVPORT_MOUNT to explicitly bind to a reserved port.
+# RESVPORT_MOUNT = -DRESVPORT
+
+#
+# ---------------------- final compiler definitions -----------------------
+#
+
+# DEBUG = -g
+CFLAGS =
+EXTRA_CFLAGS =
+
+LDFLAGS = $(OSTYPE)
+
+
+#
+# ---------------------- source file definitions ------------------
+#
+
+MAKEFILE = Makefile
+
+DOC = sfs.1 README sfs.1
+
+SHELLFILES = sfs_mgr sfs_mcr sfs_rc
+
+V2_HDR = sfs_c_def.h sfs_c_nfs.h
+
+V3_HDR = sfs_c_def.h sfs_c_nfs.h
+
+V2_SRC = sfs_2_ops.c sfs_2_vld.c
+
+V3_SRC = sfs_3_ops.c sfs_3_vld.c
+
+C_SRC = sfs_c_clk.c sfs_c_dat.c sfs_c_dmp.c sfs_c_man.c \
+ sfs_c_pnt.c sfs_c_rnd.c sfs_c_sig.c sfs_c_bio.c \
+ sfs_2_xdr.c sfs_3_xdr.c sfs_c_mnt.c sfs_c_chd.c \
+ sfs_c_clnt.c
+
+C_COMMON_SRC = sfs_c_sub.c
+
+M_HDR = sfs_m_def.h
+
+M_PRIME_SRC = sfs_m_prm.c
+
+M_SYNCD_SRC = sfs_m_snc.c
+
+M_MSG_SRC = sfs_m_msg.c
+
+M_COMMON_SRC = sfs_m_xdr.c
+
+#
+# ---------------------- object file definitions -------------------
+#
+
+V2_OBJ = sfs_2_ops.o sfs_2_vld.o
+
+V3_OBJ = sfs_3_ops.o sfs_3_vld.o
+
+C_OBJ = sfs_c_clk.o sfs_c_dat.o sfs_c_dmp.o sfs_c_man.o \
+ sfs_c_pnt.o sfs_c_rnd.o sfs_c_sig.o sfs_c_bio.o \
+ sfs_2_xdr.o sfs_3_xdr.o sfs_c_mnt.o sfs_c_chd.o \
+ sfs_c_clnt.o
+
+C_COMMON_OBJ = sfs_c_sub.o
+
+
+M_PRIME_OBJ = sfs_m_prm.o
+
+M_SYNCD_OBJ = sfs_m_snc.o
+
+M_MSG_OBJ = sfs_m_msg.o
+
+M_COMMON_OBJ = sfs_m_xdr.o
+
+#
+# ---------------------- rules and targets for building sfs ---------
+#
+
+BENCH = /tmp/spec
+all: fix_perm install
+
+compile: fix_perm install
+
+fix_perm:
+ chmod 755 $(BENCH)/src
+ chmod 755 $(BENCH)/src/rpc
+ chmod 755 $(BENCH)/bin
+
+sfs: $(V2_HDR) $(M_HDR) $(V2_OBJ) $(C_OBJ) \
+ $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB)
+ $(CC) -o $@ $(LDFLAGS) $(V2_OBJ) $(C_OBJ) $(C_COMMON_OBJ) \
+ $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \
+ $(EXTRA_LIBS)
+
+sfs3: $(V3_HDR) $(M_HDR) $(V3_OBJ) $(C_OBJ) \
+ $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB)
+ $(CC) -o $@ $(LDFLAGS) $(V3_OBJ) $(C_OBJ) $(C_COMMON_OBJ) \
+ $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \
+ $(EXTRA_LIBS)
+
+sfs_prime: $(V2_HDR) $(M_HDR) $(M_PRIME_OBJ) \
+ $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB)
+ $(CC) -o $@ $(LDFLAGS) $(M_PRIME_OBJ) $(C_COMMON_OBJ) \
+ $(M_COMMON_OBJ) $(M_MSG_OBJ) $(RPCLIB) $(LIBS) \
+ $(EXTRA_LIBS)
+
+sfs_syncd: $(V2_HDR) $(M_HDR) $(M_SYNCD_OBJ) \
+ $(C_COMMON_OBJ) $(M_COMMON_OBJ) $(RPCLIB)
+ $(CC) -o $@ $(LDFLAGS) $(M_SYNCD_OBJ) $(C_COMMON_OBJ) \
+ $(M_COMMON_OBJ) $(RPCLIB) $(LIBS) $(EXTRA_LIBS)
+
+.c.o:
+ $(CC) -c $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \
+ $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) $(OPT) \
+ $(RESVPORT_MOUNT) $*.c
+
+${RPCLIB}: FRC
+ @cd rpc; make MACHID="${MACHID}"\
+ CC="${CC}" CFLAGS="${CFLAGS}" \
+ LDFLAGS="${LDFLAGS}" EXTRA_CFLAGS="${EXTRA_CFLAGS}" \
+ EXTRA_LDFLAGS="${EXTRA_LDFLAGS}" EXTRA_LIBS="${EXTRA_LIBS}" \
+ LIBS="${LIBS}" OSTYPE="${OSTYPE}" OPT="${OPT}" \
+ EXTRA_LINTFLAGS="${EXTRA_LINTFLAGS}" \
+ RESVPORT_MOUNT="${RESVPORT_MOUNT}"\
+ librpclib.a
+
+sfs_3_ops.o: sfs_c_nfs.h sfs_c_def.h sfs_3_ops.c
+
+sfs_3_vld.o: sfs_c_nfs.h sfs_c_def.h sfs_3_vld.c
+
+sfs_3_xdr.o: sfs_c_nfs.h sfs_c_def.h sfs_3_xdr.c
+
+sfs_c_bio.o: sfs_c_nfs.h sfs_c_def.h sfs_c_bio.c
+
+sfs_c_chd.o: sfs_c_nfs.h sfs_c_def.h sfs_m_def.h sfs_c_chd.c
+
+sfs_c_clk.o: sfs_c_nfs.h sfs_c_def.h sfs_c_clk.c
+
+sfs_c_clnt.o: sfs_c_nfs.h sfs_c_def.h sfs_c_clnt.c
+
+sfs_c_dat.o: sfs_c_def.h sfs_m_def.h sfs_c_nfs.h sfs_c_dat.c
+
+sfs_c_dmp.o: sfs_c_nfs.h sfs_c_def.h sfs_c_dmp.c
+
+sfs_c_man.o: sfs_c_nfs.h sfs_c_def.h \
+ sfs_m_def.h sfs_c_man.c
+
+sfs_c_mnt.o: sfs_c_nfs.h sfs_c_def.h sfs_c_mnt.c
+
+sfs_2_ops.o: sfs_c_nfs.h sfs_c_def.h sfs_2_ops.c
+
+sfs_c_pnt.o: sfs_c_nfs.h sfs_c_def.h \
+ sfs_m_def.h sfs_c_pnt.c
+
+sfs_c_rnd.o: sfs_c_rnd.c
+
+sfs_c_sig.o: sfs_c_nfs.h sfs_c_def.h sfs_c_sig.c
+
+sfs_c_sub.o: sfs_c_nfs.h sfs_c_def.h sfs_c_sub.c
+
+sfs_2_vld.o: sfs_c_nfs.h sfs_c_def.h sfs_2_vld.c
+
+sfs_2_xdr.o: sfs_c_nfs.h sfs_c_def.h sfs_2_xdr.c
+
+sfs_m_msg.o: sfs_c_nfs.h sfs_c_def.h \
+ sfs_m_def.h sfs_m_msg.c
+
+sfs_m_prm.o: sfs_c_nfs.h sfs_c_def.h \
+ sfs_m_def.h sfs_m_prm.c
+
+sfs_m_snc.o: sfs_c_nfs.h sfs_c_def.h \
+ sfs_m_def.h sfs_m_snc.c
+
+sfs_m_xdr.o: sfs_m_def.h sfs_m_xdr.c
+
+#
+# ---------------------- lint, install, clean, misc rules -----------------
+#
+
+lint: lint_sfs lint_sfs3 lint_prime lint_syncd
+
+lint_sfs: $(V2_HDR) $(M_HDR) $(V2_SRC) $(C_SRC) \
+ $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC)
+ lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \
+ $(EXTRA_LINTFLAGS) \
+ $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \
+ $(RESVPORT_MOUNT) \
+ $(V2_SRC) $(C_SRC) $(C_COMMON_SRC) \
+ $(M_COMMON_SRC) $(M_MSG_SRC) \
+ $(LIBS) $(EXTRA_LIBS) \
+ > sfs.lint
+
+lint_prime: $(V2_HDR) $(M_HDR) $(M_PRIME_SRC) \
+ $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC)
+ lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \
+ $(EXTRA_LINTFLAGS) \
+ $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \
+ $(RESVPORT_MOUNT) \
+ $(M_PRIME_SRC) $(C_COMMON_SRC) \
+ $(M_COMMON_SRC) $(M_MSG_SRC) \
+ $(LIBS) $(EXTRA_LIBS) \
+ > sfs_prime.lint
+
+lint_syncd: $(V2_HDR) $(M_HDR) $(M_SYNCD_SRC) \
+ $(C_COMMON_SRC) $(M_COMMON_SRC)
+ lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \
+ $(EXTRA_LINTFLAGS) \
+ $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \
+ $(RESVPORT_MOUNT) \
+ $(M_SYNCD_SRC) $(C_COMMON_SRC) $(M_COMMON_SRC) \
+ $(LIBS) $(EXTRA_LIBS) \
+ > sfs_syncd.lint
+
+lint_sfs3: $(V3_HDR) $(M_HDR) $(V3_SRC) $(C_SRC) \
+ $(C_COMMON_SRC) $(M_COMMON_SRC) $(M_MSG_SRC)
+ lint -Dlint $(INCDIR) $(CFLAGS) $(EXTRA_CFLAGS) $(DEBUG) \
+ $(EXTRA_LINTFLAGS) \
+ $(SETPGRP_CALL) $(FSTAB) $(OSTYPE) \
+ $(RESVPORT_MOUNT) \
+ $(V3_SRC) $(C_SRC) $(C_COMMON_SRC) \
+ $(M_COMMON_SRC) $(M_MSG_SRC) \
+ $(LIBS) $(EXTRA_LIBS) \
+ > sfs3.lint
+
+install: sfs sfs3 sfs_syncd sfs_prime
+ chmod +x sfs_syncd sfs_prime \
+ sfs_mgr sfs_mcr sfs_ext_mon \
+ sfs_suchown sfs sfs3
+ ./sfs_suchown $(RESVPORT_MOUNT) sfs sfs3
+
+clean clobber:
+ rm -f *.o sfs sfs_syncd sfs_prime \
+ sfs3 sfs_prime \
+ lint.out sfs_*.shr1 core *.lint
+ cd rpc; make clean
+ ./sfs_suchown clobber
+
+FRC:
--- /dev/null
+
+The trace player executable is sfs3. For trace player to run, the owner of the
+executable has to be root.
+
+To make the executable:
+ make sfs3
+
+The change owner:
+ su -
+ chown root:root sfs3
--- /dev/null
+A few notes about the redesign of the file selection algorithm for
+SFS 3.0, which may be incorporated into the inline comments in the
+next maintenance release.
+
+
+Q1. How is the Poisson Distribution used in SFS?
+
+A1. The probabilities from a Poisson-derived distribution are used
+to select files from the I/O working-set in a non-uniform fashion.
+
+The load-generators are not Poisson Processes. In particular, their
+inter-request times are not exponentially-distributed; they are programmed
+to have uniformly distributed think-times, which are only approximately
+achieved, due to limitations of the load-generating client and its
+operating system.
+
+Q2. Why is a Poisson Distribution used in SFS, rather than, say a
+Zipf distribution?
+
+A2. The reasoning behind the use of Poisson probabilities in SFS 2.0
+may be lost in the mists of time. Because SFS 2.0 was withdrawn by SPEC
+before SFS 3.0 development had proceeded very far, there was considerable
+time-pressure to release SFS 3.0 quickly. So we chose to patch the
+existing Poisson probability calculations instead of implementing a new
+distribution for file accesses.
+
+There is experimental data showing that a Zipf Distribution is a good
+model for file access probabilities. The question of what distribution
+to use in SFS 4.0 is open, so various distributions can be considered.
+
+Q3. Is it true that SFS 3.0 combines multiple Poisson cycles into a
+single cycle that is also a Poisson Distribution?
+
+A3. The combination of the distributions follows the curve of a Poisson
+Distribution only if you treat each of the twelve generations of
+access-groups as a single outcome.
+
+Q4. Why did the calculation of "Io_working_set.access_group_cnt" change
+between SFS 2.0 and SFS 3.0?
+
+A4. This calculation determines the number of access-groups in the I/O
+working-set. The number of access-groups grows *roughly* in linear
+proportion to the number of files, so that no access-group contains more
+than 125 files.
+
+In SFS 2.0, 4 groups are added for every 500 files, but in SFS 3.0
+12 groups are added for every 1500 files, so the number of files per
+access-group remains roughly the same.
+
+The main effect of the change is how the number of groups is rounded.
+In SFS 2.0, the number of groups was rounded to a multiple of 4,
+with a minimum of 8 groups. In SFS 3.0, the number of groups is
+rounded to a multiple of 12 (the value of "generations") so that the
+cycle of Poisson probabilities (for all 12 generations of access-groups)
+will be completely repeated group_cnt/12 times with no groups left over.
+
+Q5. Why was the minimum number of access-groups changed from 8 to 12?
+Why not, say, 24?
+
+A5. The choice was somewhat arbitrary. Here is some of the rationale:
+
+In order to get a complete set of Poisson probabilities, there must be
+at least as many access-groups as generations. If we had generations=24
+instead of 12, then some of the group-probabilities would be zero due to
+defect #2. (Recall that with 24 groups in SFS 2.0, 13% were inaccessible
+due to rounding of the scaled probabilities.) In SFS 2.0, 12 was the
+largest number of groups which did not trigger defect #2. Of course,
+since we now know how to fix that defect, that wasn't the complete reason.
+
+Another consideration was that the number of access groups must be rounded
+to a multiple of the number of generations. If we had used generations=24
+then the rounding would introduce a more radical change in the number
+of groups, especially for large numbers of procs.
+
+On the other hand, if we had used generations=8 instead of 12, we would
+have gotten very little variation in access probabilities, which seemed
+against the intent of the benchmark design.
+
+Q6. Why is lambda being calculated differently in SFS 3.0?
+
+A6. Lambda is the parameter of the Poisson Distribution which determines
+the shape of its probability density function, its mean, and other
+characteristics.
+
+SFS 2.0 set lambda to half the number of groups, which in turn was a
+function of the requested ops/sec/proc. That was defect #4, which caused
+the shape of the access-distribution to change depending on how many
+processes a vendor chose to use -- it would get narrower as the number
+of processes was reduced. SFS 3.0 sets lambda based on "generations",
+which is a constant. Thus the new distribution has the same shape no
+matter how many load-generating processes are used.
+
+The key to defect #4 was that we *don't* want lambda to vary
+with the number of access-groups. We want it to be a constant so that
+for a given size fileset the number of files that fit into cache
+depends very little on the number of load-generation processes used.
+
+Q7. Why was lambda set to half the number of groups in SFS 2.0
+instead of something else?
+
+A7. The SFS 2.0 rationale is not documented, as far as we know. The nice
+thing about group_count/2 is that it puts the characteristic "hump" of the
+probability density function (the mode) near group_count/2, in the middle
+of the array of groups. Probably that was the reason.
+
+In SFS 3.0, each cycle of probabilities has 12 groups, so lambda=6
+to preserve this feature of the distribution.
+
+Q8. How is the SFS 3.0 approach differ from always using 12 access-groups
+and allowing the number of files per access-group to be more than 125?
+
+A8. The end-result would have been roughly the same. The reason this
+was not done was that SFS sometimes searches within an access-group, for
+instance to find the best fit for an operation. The search is a linear
+search. We were concerned that if the number of files in a group got
+too large, the client could get bogged down searching for the best fit.
+The search for the appropriate access-group is a binary search, which
+scales better.
+
+Q9. Why not just limit the benchmark to 25 requested ops/second/process,
+so that the number of groups would always be 12 or less?
+
+A9. Historically SPEC has given server vendors lots of leeway to
+have as few processes as they wish, modulo the Uniform Access Rule and
+the run-rule requirement for at least 8 processes per network. In the
+interest of avoiding a long, drawn-out debate about adding a new run-rule
+to arbitrarily limit ops/process, we decided to simply fix the code so
+that it would work reasonably well for larger numbers of ops/process.
+
+Q10. Where did the new variable "cumulative_ratio" come from?
+
+A10. This variable is used in the loop that calculates Poisson distribution
+for I/O working-set accesses. In 2.0, lambda^x and x! were calculated
+separately. Both variables got very large and would overflow when 'x' got
+into the hundreds. This was defect #3. The Poisson distribution really
+only needs the ratio (lambda^x/x!), which is numerically more stable.
+So that is what the SFS 3.0 code calculates.
+
+Since SFS 3.0 never uses values of lambda other than 6.0, the change
+has proven to be moot.
--- /dev/null
+
+
+agefs Wed Apr 21 18:28:42 EDT 2004
+
+
+agefs Wed Apr 21 18:31:44 EDT 2004
+Wed Apr 21 18:31:44 EDT 2004
+
+
+agefs Wed Apr 21 18:32:06 EDT 2004
+Wed Apr 21 18:32:06 EDT 2004
+
+
+agefs Wed Apr 21 18:32:58 EDT 2004
+Wed Apr 21 18:32:58 EDT 2004
+
+
+agefs Wed Apr 21 18:33:15 EDT 2004
+Wed Apr 21 18:33:15 EDT 2004
+Wed Apr 21 23:18:15 EDT 2004
+Wed Apr 21 23:18:15 EDT 2004
+Thu Apr 22 00:08:24 EDT 2004
+Thu Apr 22 00:08:24 EDT 2004
+Thu Apr 22 00:33:15 EDT 2004
+Thu Apr 22 00:33:15 EDT 2004
+Thu Apr 22 00:33:33 EDT 2004
+Thu Apr 22 00:33:33 EDT 2004
+Thu Apr 22 00:35:06 EDT 2004
+Thu Apr 22 00:35:06 EDT 2004
+Thu Apr 22 00:35:16 EDT 2004
+Thu Apr 22 00:35:16 EDT 2004
+Thu Apr 22 00:36:25 EDT 2004
+Thu Apr 22 00:38:36 EDT 2004
+Thu Apr 22 00:39:17 EDT 2004
+Thu Apr 22 00:39:19 EDT 2004
+Thu Apr 22 00:47:43 EDT 2004
--- /dev/null
+nfs stop
+umount $1
+mount $1
+nfs start
+cd $1
+echo "find test1 -print >$3.1"
+find test1 -print > $3.1
+echo "sed -d s/^/show_inode_info / $3.1"
+sed -e "s/^/show_inode_info /" $3.1 >$3.2
+echo "open $2" >$3.3
+cat $3.2 >> $3.3
+echo "debugfs -f $3.3"
+debugfs -f $3.3 > $3.4
+#echo "frag_count $3.4 > $3.5"
+#frag_count $3.4 > $3.5
--- /dev/null
+#This is to collect info for something other than test1
+#test1 is hard-coded in frag_collect
+nfs stop
+umount $1
+mount $1
+nfs start
+echo "sed -d s/^/show_inode_info / $3"
+sed -e "s/^/show_inode_info /" $3 >$3.2
+echo "open $2" >$3.3
+cat $3.2 >> $3.3
+echo "debugfs -f $3.3"
+debugfs -f $3.3 > $3.4
+#echo "frag_count $3.4 > $3.5"
+#frag_count $3.4 > $3.5
--- /dev/null
+#include <stdio.h>
+#define RFS_ASSERT(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "Assertion failed: line %d, file \"%s\"\n", \
+ __LINE__, __FILE__); \
+ fflush(stdout); \
+ fflush(stderr); \
+ exit(-1); \
+ }
+
+#define DISK_BLOCK_SIZE 4096
+#define SUB_DISTANCE
+int f()
+{}
+char buf[8024000];
+main(int argc, char ** argv)
+{
+ FILE * fp;
+ int ret;
+ char * p;
+ int i;
+ int frag_num, distance, tfrag_num=0;
+ int td=0, td_MB=0;
+ int max_block_num = -1;
+ int block_num = -1;
+ int last_block_num;
+ int tblock_num =0;
+ int last_block = 0;
+ int block_range_start, block_range_stop;
+ int lineno = 0;
+ int format_version = 1;
+ char * p1=NULL, *p2=NULL, *p3=NULL;
+ int size = -1;
+ int max_block = 0;
+ FILE * fpout;
+ char name[1024];
+ int file_num = 0;
+ unsigned long long distancell = 0;
+ int avg_frag_distance;
+ int avg_block_distance;
+#ifdef SUB_DISTANCE
+#define MAX_SUB_DISTANCE_NUM 50
+ int sub_distance_index;
+ int SUB_DISTANCE_NUM = 1;
+ int SUB_DISTANCE_SIZE = 1000000;
+ unsigned long long sub_distance[MAX_SUB_DISTANCE_NUM];
+ unsigned int sub_frag_num[MAX_SUB_DISTANCE_NUM], sub_block_num[MAX_SUB_DISTANCE_NUM];
+ memset (&sub_distance, 0, sizeof(sub_distance));
+ memset (&sub_frag_num, 0, sizeof(sub_frag_num));
+ memset (&sub_block_num, 0, sizeof(sub_block_num));
+
+ if (argc == 3) {
+ SUB_DISTANCE_NUM = atoi(argv[2]);
+ RFS_ASSERT ((SUB_DISTANCE_NUM >=1) && (SUB_DISTANCE_NUM <= MAX_SUB_DISTANCE_NUM));
+ }
+#endif
+
+ fp = fopen (argv[1], "r");
+ if (!fp) {
+ printf ("can not opern %s\n", argv[1]);
+ perror("open");
+ exit (0);
+ }
+
+
+ strcpy (name, argv[1]);
+ strcat (name, ".disk");
+
+ fpout = fopen(name, "w");
+ if (!fpout) {
+ printf ("can not opern %s\n", name);
+ perror("open");
+ exit (0);
+ }
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ lineno++;
+ if ((lineno%10000)==0) { // || (lineno >630000)) {
+ fprintf(stderr, "%d lines processed\n", lineno);
+ }
+ if (lineno==122165)
+ f();
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ if (buf[0]=='U') {
+ p = strstr (buf, "Size");
+ RFS_ASSERT (p);
+ if (size != -1) {
+ printf ("lineno %d size %d\n", lineno, size);
+ }
+ RFS_ASSERT (size == -1);
+ sscanf(p, "Size: %d", &size);
+ continue;
+ }
+
+
+ /* For now we ignore symbolic links */
+ if (!strncmp(buf, "Fast_link_dest", strlen("Fast_link_dest"))) {
+ f();
+ goto ENDLOOP;
+ }
+
+ if (buf[0]!='B')
+ continue;
+
+ RFS_ASSERT (!strcmp(buf, "BLOCKS:\n"));
+ fgets(buf, sizeof(buf), fp);
+ lineno++;
+ if (!(buf[strlen(buf)-1]=='\n')) {
+ printf ("line[%d] %s\n", lineno, buf);
+ };
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ if (!strcmp(buf, "\n"))
+ goto ENDLOOP;
+
+
+ RFS_ASSERT (size >= 0);
+ RFS_ASSERT (block_num == -1);
+ RFS_ASSERT (max_block_num == -1);
+ block_num = 0; /* the block number of block_range_start of current fragment */
+ last_block_num = 0; /* the block number of block_range_start of last fragment */
+ max_block_num = 0;
+ if (size >0) {
+ char line[1024];
+ int i;
+ fgets(line, sizeof(line), fp);
+ lineno++;
+ RFS_ASSERT (line[strlen(line)-1]=='\n');
+ RFS_ASSERT (strstr(line, "TOTAL: "));
+ max_block_num = atoi (line+strlen("TOTAL: "));
+ i = ((size+DISK_BLOCK_SIZE-1)/DISK_BLOCK_SIZE);
+ RFS_ASSERT ((max_block_num >= i) && ((max_block_num*9/i)<10));
+ }
+ tblock_num += max_block_num;
+
+ p = buf;
+ frag_num = 0;
+ distance = 0;
+ last_block = 0;
+ //printf ("line %d %s", lineno, buf);
+ while (p && (*p!='\n')) {
+ if (format_version == 1) {
+ p1 = strchr (p, ')');
+ if (p1) {
+ p2 = strchr (p, '-');
+ p3 = strchr (p, ':');
+ RFS_ASSERT (p3);
+ p3++;
+ } else {
+ format_version = 2;
+ p3 = p;
+ }
+ } else
+ p3 = p;
+
+#define checkit
+ /* single block range */
+ if ((p2==NULL) || p2>p1) {
+#ifdef checkit
+ char * pt2, a;
+ pt2 = strchr(p3, ' ');
+ if (!pt2) {
+ pt2 = strchr(p3, '\n');
+ a = '\n';
+ } else
+ a = ' ';
+ RFS_ASSERT (pt2);
+ RFS_ASSERT (pt2!=p3);
+ *pt2 = 0;
+ block_range_start = atoi (p3);
+ *pt2 = a ;
+#else
+ sscanf(p3, "%d", &block_range_start);
+#endif
+ block_range_stop = block_range_start;
+ } else {
+#ifdef checkit
+ char * pt, * pt2, a;
+ pt = strchr(p3, '-');
+ RFS_ASSERT (pt);
+ *pt = 0;
+ block_range_start = atoi (p3);
+ *pt = '-';
+ pt2 = strchr(pt+1, ',');
+ if (!pt2) {
+ pt2 = strchr(pt+1, '\n');
+ a = '\n';
+ } else
+ a = ',';
+ RFS_ASSERT (pt2);
+ *pt2 = 0;
+ block_range_stop = atoi (pt+1);
+ *pt2 = a ;
+#else
+ sscanf(p3, "%d-%d", &block_range_start, &block_range_stop);
+#endif
+ }
+
+ RFS_ASSERT (block_range_start >0);
+ RFS_ASSERT (block_range_stop >= block_range_start);
+
+ block_num += (block_range_stop - block_range_start+1);
+
+ if (block_range_start != (last_block+1)) {
+ frag_num ++;
+
+#ifdef SUB_DISTANCE
+ sub_distance_index = (block_num-1) * SUB_DISTANCE_NUM/max_block_num;
+ sub_block_num[sub_distance_index] += block_num - last_block_num;
+ last_block_num = block_num;
+ //printf ("block_num %d SUB_DISTANCE_NUM %d max_block_num %d index %d\n", block_num, SUB_DISTANCE_NUM,
+//max_block_num, sub_distance_index);
+ RFS_ASSERT ((sub_distance_index>=0) && (sub_distance_index<SUB_DISTANCE_NUM));
+#endif
+ if (last_block!=0) {
+ if (block_range_start > last_block+1) {
+ distance += block_range_start - (last_block+1);
+#ifdef SUB_DISTANCE
+ sub_distance[sub_distance_index] += block_range_start - (last_block+1);
+#endif
+ } else {
+ distance += (last_block+1)-block_range_start;
+#ifdef SUB_DISTANCE
+ sub_distance[sub_distance_index] += (last_block+1)-block_range_start ;
+#endif
+ }
+
+ if (distance >= 1000000000) {
+ printf ("line[%d] %s, block_range_start %d last_block %d\n", lineno, buf, block_range_start, last_block);
+ RFS_ASSERT (0);
+ }
+ fprintf(fpout, "%d %d\n", last_block, block_range_start);
+ };
+ //printf ("range_start %d last_block %d distance %d\n",
+ //block_range_start, last_block, distance);
+#ifdef SUB_DISTANCE
+ sub_frag_num[sub_distance_index] ++;
+#endif
+ }
+
+ last_block = block_range_stop;
+ if (last_block > max_block)
+ max_block = last_block;
+ if (p1)
+ p = strchr (p3, '(');
+ else {
+ p = strchr (p3, ' ');
+ p++;
+ }
+ }
+ //printf ("FRAG_NUM %d DISTANCE %d\n", frag_num, distance);
+ tfrag_num += frag_num;
+ distancell += distance;
+ENDLOOP:
+ file_num ++;
+ size = -1;
+ block_num = -1;
+ max_block_num = -1;
+/*
+ td += distance;
+ if (td > 1000000) {
+ td_MB += td/1000000;
+ td = td%1000000;
+ RFS_ASSERT (td_MB < 1000000000);
+ }
+*/
+ }
+ fclose (fpout);
+ fclose (fp);
+
+ if (tfrag_num != file_num) {
+ RFS_ASSERT ((distancell /(tfrag_num-file_num)) < 1000000000);
+ RFS_ASSERT ((distancell /(tblock_num-file_num)) < 1000000000);
+ avg_frag_distance = distancell/(tfrag_num-file_num);
+ avg_block_distance = distancell/(tblock_num-file_num);
+ } else {
+ avg_frag_distance =0;
+ avg_block_distance =0;
+ }
+ RFS_ASSERT ((distancell /1000000) < 1000000000);
+ td_MB = distancell/1000000;
+ td = distancell%1000000;
+
+#ifdef SUB_DISTANCE
+ for (i=0; i<SUB_DISTANCE_NUM; i++) {
+ printf("sub[%d] block_num %d frag_num %d distance %d\n", i, sub_block_num[i], sub_frag_num[i], sub_distance[i]/1000000);
+ }
+#endif
+/*
+ distancell = td_MB;
+ distancell *=1000000;
+ distancell +=td;
+ distancell /= (tfrag_num-file_num);
+ if (distancell > 1000000000) {
+ printf ("error 4\n");
+ exit(-1);
+ }
+ avg_frag_distance = distancell;
+
+ distancell = td_MB;
+ distancell *=1000000;
+ distancell +=td;
+ distancell /= (tblock_num-file_num);
+ if (distancell > 1000000000) {
+ printf ("error 4\n");
+ exit(-1);
+ }
+ avg_block_distance = distancell;
+*/
+
+ printf("****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d\n", tfrag_num, td_MB, td, tblock_num, max_block, file_num, avg_frag_distance, avg_block_distance);
+}
--- /dev/null
+# Usage: fragment_collect dev mount_dir test_path output_file
+#
+# On linux system, this script collects the fragmentation status of a
+# particular directory. The objects under this directory SHOULD NOT be
+# used as mount point. If they do, please umount them first, but there
+# is no danger even if you forget. The "output_file" is the one that I
+# need, please gzip it and send it back to nzhu@cs.sunysb.edu
+#
+# The program needs to be run as ROOT, but it is SAFE and KEEPS PRIVACY.
+# The pathname information on your system is completely filtered out
+# from the final output. This program ONLY READ and DOES NOT WRITE any
+# files EXCEPT "output_file*"
+#
+# Parameters:
+# dev :the device of the directory to be measured
+# mount_dir :the the mount point of the device
+# test_path :the relative path of the directory under mount_dir
+# output_file :where the result is stored.
+#
+# Example: fragment_collect /dev/hda7 /home ningning /tmp/home-ningning.frag
+#
+# This gives the fragmentation information (saved in "/tmp/home-ningning.frag")
+# of the file system objects under the directory of "/home/ningning" on the
+# disk partition of "/dev/hda7" mounted to "/home".
+#
+#nfs stop
+#umount $2
+#mount $2
+#nfs start
+cd $2
+echo "find $3 -print >$4.1"
+find $3 -print > $4.1
+echo "sed -d s/^/show_inode_info / $4.1"
+sed -e "s/^/show_inode_info /" $4.1 >$4.2
+echo "open $1" >$4.3
+cat $4.2 >> $4.3
+echo "debugfs -f $4.3"
+debugfs -f $4.3 > $4.4
+grep -v debugfs $4.4 > $4
+#rm $4.1
+#rm $4.2
+#rm $4.3
+#rm $4.4
--- /dev/null
+#include <stdio.h>
+#include "rfs_assert.h"
+
+#define EVEN_CHUNK_SIZE_MAX 2
+#define CHUNK_NUM_MAX 17
+#define FS_SIZE_MB_MAX 81
+#define STAGE_NUM_MAX 65
+typedef struct {
+ int flag;
+ int frag_num;
+ int avg_frag_distance;
+ int avg_block_distance;
+} result_t;
+result_t result[EVEN_CHUNK_SIZE_MAX][CHUNK_NUM_MAX][FS_SIZE_MB_MAX][STAGE_NUM_MAX];
+
+int range_check (int i, int min, int max)
+{
+ if (i<min)
+ return 0;
+ if (i>max)
+ return 0;
+ return 1;
+}
+
+int file_ratio, active_ratio, chunk_num, fs_size_MB, even_chunk_size, stage_num;
+#define SEQ_NUM 11
+int frag_num[SEQ_NUM], td_MB[SEQ_NUM], td[SEQ_NUM], tblock_num[SEQ_NUM], file_num;
+main(int argc, char ** argv)
+{
+ char line[1024];
+ FILE * fp;
+ unsigned long long distance;
+ unsigned long long tll;
+ int avg_frag_distance = 0;
+ int max_blockno=0, avg_block_distance=0;
+ char * p;
+ int sequence[SEQ_NUM]={1,2,5,10,20,30,50,100,200,250,500};
+ int i;
+ char fhmap[1024];
+ result_t * resultp= NULL;
+
+
+ memset (result, 0, sizeof(result));
+
+ fp = fopen (argv[1], "r");
+ if (!fp) {
+ perror("open");
+ exit(-1);
+ }
+ i=-1;
+ while (fgets(line, sizeof(line), fp)) {
+ if (feof(fp))
+ break;
+ if (strstr(line, "==>")) {
+ RFS_ASSERT (resultp == NULL);
+
+ p = strrchr (line, '/');
+ if (p==NULL)
+ p=line;
+ p = strchr (p, '_');
+ if (p==NULL) {
+ printf("error 2\n");
+ exit(-1);
+ }
+ sscanf (p, "_%d_%d_%d_%d_%d_%d_%s.5 <==\n", &even_chunk_size, &file_ratio, &active_ratio, &chunk_num, &fs_size_MB, &stage_num, fhmap);
+ i = chunk_num;
+ RFS_ASSERT (range_check (even_chunk_size, 0, EVEN_CHUNK_SIZE_MAX-1));
+ RFS_ASSERT (range_check (chunk_num, 0, CHUNK_NUM_MAX-1));
+ RFS_ASSERT (range_check (fs_size_MB/100, 0, FS_SIZE_MB_MAX-1));
+ RFS_ASSERT (range_check (stage_num, 0, STAGE_NUM_MAX-1));
+ resultp = & (result[even_chunk_size][chunk_num][fs_size_MB/100][stage_num]);
+ RFS_ASSERT (resultp->flag == 0);
+ }
+ if (strstr(line, "****")) {
+
+ RFS_ASSERT (resultp);
+
+ sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &frag_num[0], &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &avg_frag_distance, &avg_block_distance);
+ sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &(resultp->frag_num), &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &(resultp->avg_frag_distance), &(resultp->avg_block_distance));
+ resultp ->flag = 1;
+
+ //printf("%d %d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0], avg_block_distance);
+
+ resultp = NULL;
+ }
+ }
+
+ print_xmgr ("avg_block_distance");
+ print_xmgr ("frag_num");
+ print_xmgr ("frag_size");
+ print_xmgr ("avg_frag_distance");
+ print_xmgr_chunk_stage ("avg_block_distance");
+}
+
+int print_y(FILE * fp, result_t * resultp, char * y_axis)
+{
+ if (!strcmp (y_axis, "avg_block_distance")) {
+ fprintf (fp, "%d ", resultp->avg_block_distance);
+ } else if (!strcmp (y_axis, "frag_num")) {
+ fprintf (fp, "%d ", resultp->frag_num);
+ } else if (!strcmp (y_axis, "frag_size")) {
+ fprintf (fp, "%f ", (float)tblock_num[0]/(float)resultp->frag_num);
+ } else if (!strcmp (y_axis, "avg_frag_distance")) {
+ fprintf (fp, "%d ", resultp->avg_frag_distance);
+ } else {
+ RFS_ASSERT (0);
+ }
+}
+ /* statistics for avg_block_distance */
+int print_xmgr (char * y_axis)
+{
+ int flag1, flag2;
+ result_t * resultp;
+ char name[1024];
+
+ FILE * fp = NULL;
+
+ for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) {
+ for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) {
+
+ sprintf (name, "xmgr.%d_%d.stage.%s", even_chunk_size, fs_size_MB*100, y_axis);
+ fp = NULL;
+ flag1 = 1;
+ for (stage_num = 0; stage_num < STAGE_NUM_MAX; stage_num++) {
+ flag2 = 1;
+ for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) {
+ resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]);
+ if (resultp->flag) {
+ if (flag1) {
+ printf ("*** even %d fs_size_MB %d X: stage_num Y: %s Lines: different chunk_num ***\n", even_chunk_size, fs_size_MB*100, y_axis);
+ flag1 = 0;
+ if (!fp) {
+ fp = fopen (name, "w");
+ if (!fp)
+ RFS_ASSERT (0);
+ }
+ }
+ if (flag2) {
+ //fprintf (fp, "%d ", file_num/stage_num);
+ fprintf (fp, "%f ", 1/((float)stage_num));
+ flag2 = 0;
+ }
+ print_y (fp, resultp, y_axis);
+ }
+ }
+ if (!flag2)
+ fprintf (fp, "\n");
+ }
+ if (fp)
+ fclose (fp);
+ }
+ }
+
+ for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) {
+ for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) {
+ sprintf (name, "xmgr.%d_%d.chunk.%s", even_chunk_size, fs_size_MB*100, y_axis);
+ fp = NULL;
+ flag1 = 1;
+ for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) {
+ flag2 = 1;
+ for (stage_num = 0; stage_num < STAGE_NUM_MAX; stage_num++) {
+ resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]);
+ if (resultp->flag) {
+ if (flag1) {
+ printf ("*** even %d fs_size_MB %d X: chunk_num Y: %s Lines: different stage_num ***\n", even_chunk_size, fs_size_MB*100, y_axis);
+ flag1 = 0;
+ if (!fp) {
+ fp = fopen (name, "w");
+ if (!fp)
+ RFS_ASSERT (0);
+ }
+ }
+ if (flag2) {
+ fprintf (fp, "%d ", chunk_num);
+ flag2 = 0;
+ }
+ print_y (fp, resultp, y_axis);
+ }
+ }
+ if (!flag2)
+ fprintf (fp, "\n");
+ }
+ if (fp)
+ fclose (fp);
+ }
+ }
+}
+int print_xmgr_chunk_stage (char * y_axis)
+{
+ int flag1, flag2;
+ result_t * resultp;
+ char name[1024];
+
+ FILE * fp = NULL;
+
+ for (even_chunk_size = 0; even_chunk_size < EVEN_CHUNK_SIZE_MAX; even_chunk_size ++) {
+ for (fs_size_MB = 0; fs_size_MB < FS_SIZE_MB_MAX; fs_size_MB ++) {
+
+ sprintf (name, "xmgr.%d_%d.stage_chunk.%s", even_chunk_size, fs_size_MB*100, y_axis);
+ fp = NULL;
+ flag1 = 1;
+ for (stage_num = STAGE_NUM_MAX-1; stage_num>=0; stage_num--) {
+ flag2 = 1;
+ for (chunk_num = 0; chunk_num < CHUNK_NUM_MAX; chunk_num++) {
+ resultp = &(result[even_chunk_size][chunk_num][fs_size_MB][stage_num]);
+ if (resultp->flag) {
+ if (flag1) {
+ printf ("*** even %d fs_size_MB %d X: stage_num Y: %s Lines: different chunk_num ***\n", even_chunk_size, fs_size_MB*100, y_axis);
+ flag1 = 0;
+ if (!fp) {
+ fp = fopen (name, "w");
+ if (!fp)
+ RFS_ASSERT (0);
+ }
+ }
+ fprintf (fp, "%f ", chunk_num/((float)stage_num));
+ print_y (fp, resultp, y_axis);
+ fprintf (fp, "\n");
+ }
+ }
+ }
+ if (fp)
+ fclose (fp);
+ }
+ }
+}
--- /dev/null
+#include <stdio.h>
+main(int argc, char ** argv)
+{
+ char line[1024];
+ FILE * fp;
+ unsigned long long distance;
+ unsigned long long tll;
+ int avg_frag_distance = 0;
+#define SEQ_NUM 11
+ int frag_num[SEQ_NUM], td_MB[SEQ_NUM], td[SEQ_NUM], tblock_num[SEQ_NUM], file_num;
+ int max_blockno=0, avg_block_distance=0;
+ char * p;
+ int sequence[SEQ_NUM]={1,2,5,10,20,30,50,100,200,250,500};
+ int i;
+ int file_ratio, active_ratio, chunk_num, fs_size_MB;
+ char fhmap[1024];
+
+ fp = fopen (argv[1], "r");
+ if (!fp) {
+ perror("open");
+ exit(-1);
+ }
+ i=-1;
+ while (fgets(line, sizeof(line), fp)) {
+ if (feof(fp))
+ break;
+ if (strstr(line, "==>")) {
+ if (i!=-1) {
+ printf("error 1\n");
+ exit(-1);
+ }
+ p = strrchr (line, '/');
+ if (p==NULL)
+ p=line;
+ p = strchr (p, '_');
+ if (p==NULL) {
+ printf("error 2\n");
+ exit(-1);
+ }
+ sscanf (p, "_%d_%d_%d_%d_%s.5 <==\n", &file_ratio, &active_ratio, &chunk_num, &fs_size_MB, fhmap);
+ i = chunk_num;
+ }
+ if (strstr(line, "****")) {
+ if (i==-1) {
+ printf("error 3\n");
+ exit(-1);
+ }
+ sscanf(line, "****total FRAG_NUM %d td_MB %d td %d tblock_num %d max_blockno %d file_num %d avg_frag_distance %d avg_block_distance %d", &frag_num[0], &td_MB[0], &td[0], &tblock_num[0], &max_blockno, &file_num, &avg_frag_distance, &avg_block_distance);
+
+#ifdef checkit
+ distance = td_MB[0];
+ distance *=1000000;
+ distance +=td[0];
+ tll = frag_num[0];
+ distance /= frag_num[0];
+
+ if (distance > 1000000000) {
+ printf ("error 4\n");
+ exit(-1);
+ }
+ avg_frag_distance = distance;
+ printf("%d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0]);
+#else
+ printf("%d %d %d %d %d %d\n", chunk_num, frag_num[0], avg_frag_distance, td_MB[0], tblock_num[0], avg_block_distance);
+#endif
+ i = -1;
+ }
+ }
+}
+
--- /dev/null
+/* generic_hash.c: Generic hash function that hashes on a maximum of three keys
+ * $Id: generic_hash.c,v 1.5 2002/08/21 22:08:01 ningning Exp $
+ * Changes:
+ *
+ * $Log: generic_hash.c,v $
+ * Revision 1.5 2002/08/21 22:08:01 ningning
+ * *** empty log message ***
+ *
+ */
+
+#include <stdio.h>
+#include "generic_hash.h"
+
+/* create an entry for a block */
+void generic_insert(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize)
+{
+unsigned int index;
+struct generic_entry *p,*q;
+
+unsigned int * tmp =(unsigned int *)key1;
+unsigned int i;
+index = 0;
+for (i=0; i<(key1_size/4); i++)
+ index += *(tmp+i);
+index = index%hsize;
+{
+//printf ("generic_insert index %d key %s\n", index, key1);
+}
+p = q = htable[index];
+while (p) {
+ if ((!memcmp(p->key1, key1, key1_size))
+ && (p->key3==key3)) {
+ return;
+ }
+ q = p;
+ p = p->next;
+}
+if (!p) {
+ p = (struct generic_entry *)malloc(sizeof(struct generic_entry));
+ memcpy(p->key1, key1, key1_size);
+ p->key3 = key3;
+ p->next = NULL;
+}
+if (!q)
+ htable[index] = p;
+else
+ q->next = p;
+return;
+}
+
+void generic_delete(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize)
+{
+unsigned int index;
+int found;
+struct generic_entry *p,*q;
+
+unsigned int * tmp =(unsigned int *)key1;
+unsigned int i;
+index = 0;
+for (i=0; i<(key1_size/4); i++)
+ index += *(tmp+i);
+index = index%hsize;
+
+p = q = htable[index];
+found = 0;
+while (p) {
+ if ((!memcmp(p->key1, key1, key1_size))
+ && (p->key3==key3)) {
+ found = 1;
+ break;
+ }
+ q = p;
+ p = p->next;
+}
+RFS_ASSERT(found==1);
+if (p==htable[index])
+ htable[index] = p->next;
+else
+ q->next = p->next;
+
+/* free hash entry */
+
+free(p);
+
+return;
+}
+
+struct generic_entry *generic_lookup(char * key1, unsigned int key1_size, unsigned int key3, struct generic_entry **htable, int hsize)
+{
+unsigned int index;
+struct generic_entry *p;
+
+unsigned int * tmp =(unsigned int *)key1;
+unsigned int i;
+index = 0;
+for (i=0; i<(key1_size/4); i++)
+ index += *(tmp+i);
+index = index%hsize;
+{
+//printf ("generic_lookup index %d key %s\n", index, key1);
+}
+
+p = htable[index];
+while (p) {
+ if (!memcmp(p->key1, key1, key1_size))
+ {
+ return p;
+ }
+ p = p->next;
+}
+/*RFS_ASSERT(0);*/
+return NULL;
+}
+
+struct generic_entry *generic_lookup1(unsigned int key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize)
+{
+unsigned int index;
+struct generic_entry *p;
+
+index = ((unsigned int)key1)%hsize;
+p = htable[index];
+while (p) {
+ if (p->key1==key1)
+ return p;
+ p = p->next;
+}
+/*RFS_ASSERT(0);*/
+return NULL;
+}
+
+struct generic_entry *generic_lookup2(unsigned int key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize)
+{
+unsigned int index;
+struct generic_entry *p;
+
+index = (key1 + key2)%hsize;
+p = htable[index];
+while (p) {
+ if ( (p->key1==key1)
+ && (p->key2==key2)
+ )
+ return p;
+ p = p->next;
+}
+/*RFS_ASSERT(0);*/
+return NULL;
+}
+
+void generic_display(struct generic_entry **htable, int hsize, int numkeys)
+{
+int i;
+struct generic_entry *p;
+int counter = 0;
+
+for (i=0;i<hsize;i++) {
+ p = htable[i];
+ if (p)
+ printf ("htable[%d]", i);
+ while (p) {
+ if (numkeys==1)
+ printf("%d, ", p->key1);
+ else if (numkeys==2)
+ printf("(%d,%x), ", p->key1,p->key2);
+ else
+ printf("(%d,%d,%d), ", p->key1,p->key2,p->key3);
+ p = p->next;
+ counter++;
+ if ((counter%4)==3)
+ printf("\n");
+ }
+}
+printf("\ncounter was %d\n",counter);
+}
+
+/* create an entry for a block */
+void generic_long_insert4(unsigned int key1, unsigned int key2, unsigned int key3, unsigned int key4, unsigned int key5, unsigned int key6, struct generic_long_entry **htable, int hsize)
+{
+int index;
+struct generic_long_entry *p,*q;
+
+index = (key1 + key2 + key3 + key4)%hsize;
+p = q = htable[index];
+while (p) {
+ if ((p->key1==key1)
+ && (p->key2==key2)
+ && (p->key3==key3)
+ && (p->key4==key4)
+ && (p->key5==key5)
+ && (p->key6==key6)) {
+ return;
+ }
+ q = p;
+ p = p->next;
+}
+if (!p) {
+ p = (struct generic_long_entry *)malloc(sizeof(struct generic_long_entry));
+ p->key1 = key1;
+ p->key2 = key2;
+ p->key3 = key3;
+ p->key4 = key4;
+ p->key5 = key5;
+ p->key6 = key6;
+ p->next = NULL;
+}
+if (!q)
+ htable[index] = p;
+else
+ q->next = p;
+return;
+}
+
+struct generic_entry *generic_long_lookup4(unsigned int key1, unsigned int key2, unsigned int key3, unsigned int key4, struct generic_long_entry **htable, int hsize)
+{
+int index;
+struct generic_long_entry *p;
+
+index = (key1 + key2 + key3 + key4 )%hsize;
+p = htable[index];
+while (p) {
+ if ( (p->key1==key1)
+ && (p->key2==key2)
+ && (p->key3==key3)
+ && (p->key4==key4)
+ )
+ return p;
+ p = p->next;
+}
+/*RFS_ASSERT(0);*/
+return NULL;
+}
--- /dev/null
+#include "rfs_c_def.h"
+struct generic_entry {
+ char key1[MAX_TRACE_FH_SIZE];
+ int key2;
+ int key3;
+ struct generic_entry *next;
+};
+
+struct generic_long_entry {
+ int key1;
+ int key2;
+ int key3;
+ int key4;
+ int key5;
+ int key6;
+ struct generic_long_entry *next;
+};
+
+void generic_insert(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize);
+void generic_delete(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize);
+struct generic_entry *generic_lookup(char * key1, unsigned int key2, unsigned int key3, struct generic_entry **htable, int hsize);
+
+void generic_display(struct generic_entry **htable, int hsize, int numkeys);
+
--- /dev/null
+#ifndef RFS_HASH_H
+#define RFS_HASH_H
+
+/*
+ * hash.h:
+ * header file for routines to manipulate hash_tble lookup,
+ * insert and delete.
+ *
+ * $Id: hash.h,v 1.1 2002/03/11 20:25:52 ningning Exp $
+ *
+ * Changes:
+ * $Log: hash.h,v $
+ * Revision 1.1 2002/03/11 20:25:52 ningning
+ * hash function for file handle map completely implemented.
+ *
+ */
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "../common/rfs_defines.h"
+#include "rfsu_defines.h"
+
+typedef struct rfsfh rfskey_t;
+
+struct hash_ent_struct {
+#ifdef HASH_DOUBLE_SAFE
+ struct timeval time;
+#endif
+ rfskey_t key;
+ int entry;
+ struct hash_ent_struct * next;
+};
+
+typedef struct hash_ent_struct hash_ent_t;
+
+typedef struct {
+ int size;
+ int used_entry; /* number of hash table entrie which contains valid data */
+ int valid_num; /* number of valid data including those on the chain */
+ int (*hash_func) (rfskey_t * key, int size);
+ /* it's not C++ class, so we have to get size from outside */
+ hash_ent_t * buf;
+} hash_tbl_t;
+
+inline int hash_func1 (rfskey_t * key, int size);
+inline int blk_hash_func (blkkey_t * key, int size);
+int hash_tbl_init (hash_tbl_t * hash_tbl);
+int hash_tbl_insert (hash_tbl_t * hash_tbl, rfskey_t * key, int entry, int mode);
+int hash_tbl_delete (hash_tbl_t * hash_tbl, rfskey_t * key);
+int hash_tbl_lookup (hash_tbl_t * hash_tbl, rfskey_t * key);
+
+#ifdef ULFS
+
+struct blk_hash_ent_struct {
+#ifdef HASH_DOUBLE_SAFE
+ struct timeval time;
+#endif
+ blkkey_t key;
+ int entry;
+ struct blk_hash_ent_struct * next;
+};
+
+typedef struct blk_hash_ent_struct blk_hash_ent_t;
+
+typedef struct {
+ int size;
+ int keysize;
+ int entsize;
+ int quiet_flag;
+ int used_entry; /* number of hash table entrie which contains valid data */
+ int valid_num; /* number of valid data including those on the chain */
+ int (*hash_func) (blkkey_t * key, int size);
+ /* it's not C++ class, so we have to get size from outside */
+ blk_hash_ent_t * buf;
+} blk_hash_tbl_t;
+
+int blk_hash_tbl_init (blk_hash_tbl_t * hash_tbl);
+int blk_hash_tbl_insert (blk_hash_tbl_t * hash_tbl, blkkey_t * key, int entry, int mode);
+//int blk_hash_tbl_delete (blk_hash_tbl_t * hash_tbl, blkkey_t * key);
+int blk_hash_tbl_lookup (blk_hash_tbl_t * hash_tbl, blkkey_t * key);
+
+#endif
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#define FILE_NUM 25000
+#define DIR_NUM 1
+#define FILE_SIZE 40960
+
+int print_usage()
+{
+ printf("init_holder DIR_NUM HOLDER_NUM HOLDER_SIZE testdir\n");
+}
+
+main4(int argc, char ** argv)
+{
+ int i, j;
+ char name[256];
+ char cmd[1024];
+ char buf[FILE_SIZE];
+ int fd, ret;
+ char testdir[1024];
+
+ if (argc!=5) {
+ print_usage();
+ exit (-1);
+ }
+ if (DIR_NUM !=1) {
+ for (i=0; i<DIR_NUM; i++) {
+ memset (name, 0, sizeof(name));
+ sprintf(name, "%s/%d", testdir, i);
+ ret = mkdir (name, S_IRWXU);
+ if (ret == -1) {
+ perror(name);
+ exit(-1);
+ }
+ }
+ }
+
+ for (j=0; j<DIR_NUM; j++) {
+ for (i=0; i<FILE_NUM/DIR_NUM; i++) {
+ if (DIR_NUM == 1)
+ sprintf(name, "%s/%d", testdir, i);
+ else
+ sprintf(name, "%s/%d/%d", testdir, j, i);
+ fd = open (name, O_CREAT|O_WRONLY);
+ if (fd == -1) {
+ perror(name);
+ exit(-1);
+ }
+ ret = write (fd, buf, FILE_SIZE );
+ close (fd);
+ if (ret!=FILE_SIZE) {
+ printf("try to write %d , get %d\n", SIZE, ret);
+ exit (-1);
+ }
+ }
+ }
+}
--- /dev/null
+#define __u32 unsigned int
+#define u32 unsigned int
+#define __u16 unsigned short
+#define u16 unsigned short
+#define __u64 unsigned long long
+#define u64 unsigned long long
+#define __u8 unsigned char
+#define u8 unsigned char
+#define __s64 signed long long
+
+struct nfs_fhbase_old {
+ __u32 fb_dcookie; /* dentry cookie - always 0xfeebbaca */
+ __u32 fb_ino; /* our inode number */
+ __u32 fb_dirino; /* dir inode number, 0 for directories */
+ __u32 fb_dev; /* our device */
+ __u32 fb_xdev;
+ __u32 fb_xino;
+ __u32 fb_generation;
+};
+
+/*
+ * This is the new flexible, extensible style NFSv2/v3 file handle.
+ * by Neil Brown <neilb@cse.unsw.edu.au> - March 2000
+ *
+ * The file handle is seens as a list of 4byte words.
+ * The first word contains a version number (1) and four descriptor bytes
+ * that tell how the remaining 3 variable length fields should be handled.
+ * These three bytes are auth_type, fsid_type and fileid_type.
+ *
+ * All 4byte values are in host-byte-order.
+ *
+ * The auth_type field specifies how the filehandle can be authenticated
+ * This might allow a file to be confirmed to be in a writable part of a
+ * filetree without checking the path from it upto the root.
+ * Current values:
+ * 0 - No authentication. fb_auth is 0 bytes long
+ * Possible future values:
+ * 1 - 4 bytes taken from MD5 hash of the remainer of the file handle
+ * prefixed by a secret and with the important export flags.
+ *
+ * The fsid_type identifies how the filesystem (or export point) is
+ * encoded.
+ * Current values:
+ * 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number
+ * NOTE: we cannot use the kdev_t device id value, because kdev_t.h
+ * says we mustn't. We must break it up and reassemble.
+ * Possible future encodings:
+ * 1 - 4 byte user specified identifier
+ *
+ * The fileid_type identified how the file within the filesystem is encoded.
+ * This is (will be) passed to, and set by, the underlying filesystem if it supports
+ * filehandle operations. The filesystem must not use the value '0' or '0xff' and may
+ * only use the values 1 and 2 as defined below:
+ * Current values:
+ * 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes.
+ * 1 - 32bit inode number, 32 bit generation number.
+ * 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number.
+ *
+ */
+struct nfs_fhbase_new {
+ __u8 fb_version; /* == 1, even => nfs_fhbase_old */
+ __u8 fb_auth_type;
+ __u8 fb_fsid_type;
+ __u8 fb_fileid_type;
+#ifndef RFS
+ __u32 fb_auth[1];
+/* __u32 fb_fsid[0]; floating */
+/* __u32 fb_fileid[0]; floating */
+#else
+ __u16 fb_dev_major;
+ __u16 fb_dev_minor;
+ __u32 fb_dev_ino;
+ __u32 fb_ino;
+ __u32 fb_generation;
+ __u32 fb_dirino;
+#endif
+
+};
+
+// RFS struct knfsd_fh {
+struct knfs_fh { // RFS
+ unsigned int fh_size; /* significant for NFSv3.
+ * Points to the current size while building
+ * a new file handle
+ */
+ union {
+ struct nfs_fhbase_old fh_old;
+ __u32 fh_pad[NFS3_FHSIZE/4];
+ struct nfs_fhbase_new fh_new;
+ } fh_base;
+};
+
+#define ofh_dcookie fh_base.fh_old.fb_dcookie
+#define ofh_ino fh_base.fh_old.fb_ino
+#define ofh_dirino fh_base.fh_old.fb_dirino
+#define ofh_dev fh_base.fh_old.fb_dev
+#define ofh_xdev fh_base.fh_old.fb_xdev
+#define ofh_xino fh_base.fh_old.fb_xino
+#define ofh_generation fh_base.fh_old.fb_generation
+
+#define fh_version fh_base.fh_new.fb_version
+#define fh_fsid_type fh_base.fh_new.fb_fsid_type
+#define fh_auth_type fh_base.fh_new.fb_auth_type
+#define fh_fileid_type fh_base.fh_new.fb_fileid_type
+#define fh_auth fh_base.fh_new.fb_auth
+
+// RFS
+#define fh_dev_major fh_base.fh_new.fb_dev_major
+#define fh_dev_minor fh_base.fh_new.fb_dev_minor
+#define fh_dev_ino fh_base.fh_new.fb_dev_ino
+#define fh_ino fh_base.fh_new.fb_ino
+#define fh_generation fh_base.fh_new.fb_generation
+#define fh_dirino fh_base.fh_new.fb_dirino
+
--- /dev/null
+/*
+ * profile.c
+ *
+ * $Id: profile.c,v 1.1 2002/08/21 22:08:01 ningning Exp $
+ * Changes:
+ * $Log: profile.c,v $
+ * Revision 1.1 2002/08/21 22:08:01 ningning
+ * *** empty log message ***
+ *
+ */
+
+#include <sys/time.h>
+#include <sys/sem.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "profile.h"
+#include "rfs_assert.h"
+//#include "rfs_c_def.h"
+extern FILE * profile_fp;
+
+static struct timezone tz;
+inline void calculate_interval
+ (struct timeval * ts, struct timeval * te, struct timeval * interval)
+{
+ if (te->tv_usec < ts->tv_usec) {
+ if (te->tv_sec <= ts->tv_sec) {
+ printf ("te->tv_sec %d ts->tv_sec %d\n", te->tv_sec, ts->tv_sec);
+ printf ("te->tv_usec %d ts->tv_usec %d\n", te->tv_usec, ts->tv_usec);
+ }
+ RFS_ASSERT (te->tv_sec > ts->tv_sec);
+ te->tv_usec += 1000000;
+ te->tv_sec -= 1;
+ }
+
+ interval->tv_sec = te->tv_sec - ts->tv_sec;
+ interval->tv_usec = te->tv_usec - ts->tv_usec;
+ if (interval->tv_usec > 1000000) {
+ if (interval->tv_usec > 2000000) {
+ printf ("interval->tv_sec %d interval->tv_usec %d \n", interval->tv_sec, interval->tv_usec);
+ printf ("ts->tv_sec %d ts->tv_usec %d \n", ts->tv_sec, ts->tv_usec);
+ printf ("te->tv_sec %d te->tv_usec %d \n", te->tv_sec, te->tv_usec);
+ }
+ /* Sometimes it can happend that te->tv_usec > 1000000 */
+ interval->tv_sec += 1;
+ interval->tv_usec -= 1000000;
+ RFS_ASSERT (interval->tv_usec < 1000000);
+ }
+}
+
+inline void normalize_profile (int pos, struct timeval * time)
+{
+ if (!(time->tv_sec >=0 && time->tv_usec >=0 && time->tv_usec < 2000000)) {
+ printf ("pos %d tv_sec %d tv_usec %d\n", pos, time->tv_sec, time->tv_usec);
+ };
+ RFS_ASSERT (time->tv_sec >=0 && time->tv_usec >=0 && time->tv_usec < 2000000);
+ while (time->tv_usec >= 1000000) {
+ time->tv_usec -= 1000000;
+ time->tv_sec += 1;
+ }
+}
+
+inline void start_real_profile (profile_t * profile)
+{
+ start_profile(profile);
+}
+
+inline void end_real_profile (profile_t * profile)
+{
+ end_profile(profile);
+}
+
+inline void start_profile (profile_t * profile)
+{
+/*
+ if (strlen(profile->about) < 3) {
+ printf ("total_profile address: %x %x\n", &total_profile, profile);
+ }
+*/
+
+ gettimeofday(&(profile->ts), &tz);
+ normalize_profile (1, &(profile->ts));
+}
+
+inline void end_profile (profile_t * profile)
+{
+ struct timeval te, teorg;
+ struct timeval * ts;
+ struct timeval * in;
+ struct timeval oldin;
+
+/*
+ //printf ("end_profile %s\n", profile->about);
+
+ if (strlen(profile->about) < 3) {
+ printf ("total_profile address: %x %x\n", &total_profile, profile);
+ }
+*/
+
+ oldin = profile->in;
+ in = &(profile->in);
+ ts = &(profile->ts);
+
+ gettimeofday(&te, &tz);
+ normalize_profile (2, &te);
+ teorg = te;
+
+ RFS_ASSERT (te.tv_sec >= ts->tv_sec);
+ RFS_ASSERT (te.tv_usec >=0 && ts->tv_usec >=0);
+ while (te.tv_usec < ts->tv_usec) {
+ if (te.tv_sec <= ts->tv_sec) {
+ printf ("%s ts.tv_sec %d ts.tv_usec %d\n", profile->about, ts->tv_sec, ts->tv_usec);
+ printf ("teorg.tv_sec %d teorg.tv_usec %d\n", teorg.tv_sec, teorg.tv_usec);
+ }
+ RFS_ASSERT (te.tv_sec > ts->tv_sec);
+ te.tv_usec += 1000000;
+ te.tv_sec -= 1;
+ }
+
+ if (!(in->tv_sec >=0 && in->tv_usec >=0)) {
+ printf ("in->tv_sec %d, in->tv_usec %d\n", in->tv_sec, in->tv_usec);
+ };
+ RFS_ASSERT (in->tv_sec >=0 && in->tv_usec >=0);
+ in->tv_sec += te.tv_sec - ts->tv_sec;
+ in->tv_usec += te.tv_usec - ts->tv_usec;
+ normalize_profile (3, in);
+
+ if (!(in->tv_sec >=0 && in->tv_sec <864000)) {
+ printf (" ts.tv_sec %d ts.tv_usec %d\n", ts->tv_sec, ts->tv_usec);
+ printf (" te.tv_sec %d te.tv_usec %d\n", te.tv_sec, te.tv_usec);
+ printf (" in.tv_sec %d in.tv_usec %d\n", in->tv_sec, in->tv_usec);
+ printf (" oldin.tv_sec %d oldin.tv_usec %d\n", oldin.tv_sec, oldin.tv_usec);
+ }
+
+ profile->num ++;
+ profile->ts = teorg;
+}
+
+inline void init_profile (char * string, profile_t * profile)
+{
+ RFS_ASSERT (strlen(string) < sizeof (profile->about));
+ memset (profile, 0, sizeof(profile_t));
+ strcpy (profile->about, string);
+}
+
+inline int calculate_avg_timeval (struct timeval * in, int num)
+{
+ unsigned long long i;
+ int ret;
+
+ if (in->tv_sec < 2000) {
+ return ((in->tv_sec*1000000+in->tv_usec)/num );
+ } else {
+ i = ((unsigned long long)in->tv_sec)*1000000 + in->tv_usec;
+ i/= num;
+ RFS_ASSERT (i<2000000000);
+ ret = i;
+ return ret;
+ }
+}
+
+inline void print_profile (char * string, profile_t * profile)
+{
+ struct timeval * ts = &(profile->ts);
+ struct timeval * in = &(profile->in);
+
+/*
+ if (strcmp (string, profile->about)) {
+ printf ("print_profile string %s about %s\n", string, profile->about);
+ }
+*/
+
+ //RFS_ASSERT (!strcmp (string, profile->about));
+ if (in->tv_usec<0 || in->tv_usec>1000000) {
+ printf ("%s in->tv_usec %d, in->tv_sec %d num %d\n", profile->about, in->tv_usec, in->tv_sec, profile->num);
+ }
+
+ RFS_ASSERT (in->tv_usec>=0 && in->tv_usec<1000000);
+
+ if (!(in->tv_sec >=0 && in->tv_sec <864000)) {
+ printf ("%s ts.tv_sec %d ts.tv_usec %d\n", profile->about, ts->tv_sec, ts->tv_usec);
+ printf ("%s in.tv_sec %d in.tv_usec %d\n", profile->about, in->tv_sec, in->tv_usec);
+ }
+ RFS_ASSERT (in->tv_sec >=0 && in->tv_sec <864000); /* it's about 10 days */
+
+ if (profile->num == 0) {
+ printf("... %40s %3d.%06d num %d \n", profile->about, in->tv_sec, in->tv_usec,
+ profile->num);
+
+ if (profile_fp) {
+ fprintf(profile_fp, "... %40s %3d.%06d num %d \n", profile->about, in->tv_sec,
+ in->tv_usec, profile->num );
+ //perror("print_profile_1");
+ }
+ } else {
+
+ int avg = calculate_avg_timeval (in, profile->num);
+ printf("... %40s %3d.%06d num %d avg %d \n", profile->about, in->tv_sec, in->tv_usec,
+ profile->num, avg);
+
+ if (profile_fp) {
+ fprintf(profile_fp, "... %40s %3d.%06d num %d avg %d \n", profile->about, in->tv_sec,
+ in->tv_usec, profile->num, avg);
+ }
+
+/*
+ printf("... %40s %3d.%06d num %d avg %d \n", string, in->tv_sec, in->tv_usec,
+ profile->num, (in->tv_sec*1000000+in->tv_usec)/profile->num );
+
+ if (profile_fp) {
+ fprintf(profile_fp, "... %40s %3d.%06d num %d avg %d \n", string, in->tv_sec,
+ in->tv_usec, profile->num, (in->tv_sec*1000000+in->tv_usec)/profile->num );
+ }
+*/
+ }
+}
--- /dev/null
+#ifndef PROFILE_H
+#define PROFILE_H
+
+/* profile.h: header file for profiling routine
+ * $Id: profile.h,v 1.2 2002/08/21 22:11:40 ningning Exp $
+ * Changes:
+ *
+ * $Log: profile.h,v $
+ * Revision 1.2 2002/08/21 22:11:40 ningning
+ * *** empty log message ***
+ *
+ */
+
+#include <sys/time.h>
+#define MAX_PROFILE_ABOUT_LEN 128
+typedef struct {
+ struct timeval ts;
+ struct timeval in;
+ int num;
+ char about[MAX_PROFILE_ABOUT_LEN];
+} profile_t;
+
+extern inline void start_profile (profile_t * profile);
+extern inline void end_profile (profile_t * profile);
+extern inline void print_profile (char * string, profile_t * profile);
+extern inline void init_profile (char * string, profile_t * profile);
+#endif
--- /dev/null
+#ifndef lint
+static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include "sfs_c_def.h"
+#include "rfs_c_def.h"
+
+#define TRY_SETARG_FAST
+#define TRY_SETRES_FAST
+extern fh_map_t * lookup_fh (char * trace_fh);
+extern void print_result(void);
+
+char * lookup_fhandle(char * fhandle)
+{
+ fh_map_t * fh;
+ fh = lookup_fh(fhandle);
+ RFS_ASSERT (fh);
+ return ((char *)&fh->play_fh);
+}
+
+
+#define setarg_fhandle(fhp) \
+ fh_map_t * fh; \
+ t = strstr (line, "fh"); \
+ RFS_ASSERT (t); \
+ t += 3; \
+ fh = lookup_fh(t); \
+ RFS_ASSERT (fh); \
+ (void) memmove((char *)fhp, &(fh->play_fh), \
+ sizeof (nfs_fh3)); \
+ t+=TRACE_FH_SIZE+1;
+ //t = strchr (t, ' '); t++; \
+
+void setarg_getattr (int index, char * line, GETATTR3args * args)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+#else
+ setarg_fhandle(&args->object);
+#endif
+}
+
+struct ladtime * adjust_time (struct timeval tm, int * sec, int * usec)
+{
+ struct ladtime trace_pkt_time;
+ static struct ladtime trace_arg_time;
+ struct timeval curtmp;
+ struct ladtime cur;
+
+ /* not sure whether sec ==0 means anything special, do not adjust the timestamp for this */
+ if (*sec ==0) {
+ RFS_ASSERT (0);
+ RFS_ASSERT (*usec == 0);
+ trace_arg_time.sec = 0;
+ trace_arg_time.usec = 0;
+ return (&trace_arg_time);
+ }
+ trace_pkt_time.sec = tm.tv_sec;
+ trace_pkt_time.usec = tm.tv_usec;
+
+ trace_arg_time.sec = *sec;
+ trace_arg_time.usec = *usec;
+
+ gettimeofday(&curtmp, NULL);
+ cur.sec = curtmp.tv_sec;
+ cur.usec = curtmp.tv_usec;
+
+ //fprintf (stderr, "trace_pkt_time %d.%d trace_arg_time %d.%d cur %d.%d\n", trace_pkt_time.sec, trace_pkt_time.usec, trace_arg_time.sec, trace_arg_time.usec, cur.sec, cur.usec);
+
+ ADDTIME (trace_arg_time, cur);
+ //fprintf(stderr, "after add, %d.%d\n", trace_arg_time.sec, trace_arg_time.usec);
+ RFS_ASSERT (LARGERTIME (trace_arg_time, trace_pkt_time));
+ SUBTIME (trace_arg_time, trace_pkt_time);
+ return (&trace_arg_time);
+}
+
+char * parse_create_mode(char * t, createmode3 * mode)
+{
+ *mode = UNCHECKED;
+ return (t+2);
+ /* anyway, we can not get concrete result from the trace, just chose this mode */
+ RFS_ASSERT (0);
+}
+
+char * parse_sattr3(char * t, sattr3 * args, sattrguard3 * guard, int index)
+{
+ int i,j;
+ struct ladtime * tm;
+
+ /* set the default value of SETATTR3args->*/
+ args->mode.set_it = FALSE;
+ args->uid.set_it = FALSE;
+ args->gid.set_it = FALSE;
+ args->size.set_it = FALSE;
+ args->atime.set_it = FALSE;
+ args->mtime.set_it = FALSE;
+
+ //fprintf(stderr, "parse_sattr: line %s\n", t);
+ while (1) {
+ if (!strncmp (t, "mode", 4)) {
+ t+=5;
+ sscanf(t, "%x", &i);
+ args->mode.set_it = TRUE;
+ args->mode.mode = i; // (uint32_t) 0666;
+ } else if (!strncmp (t, "ctime", 5)) {
+ RFS_ASSERT (guard);
+ t+=6;
+ RFS_ASSERT (strncmp(t, "SERVER", 6));
+ sscanf (t, "%d.%d", &i, &j);
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+#ifndef IGNORE_SETATTR_CTIME
+ guard->check = TRUE;
+#endif
+ guard->obj_ctime.seconds = tm->sec;
+ guard->obj_ctime.nseconds = tm->usec*1000;
+ } else if (!strncmp (t, "atime", 5)) {
+ t+=6;
+ if (!strncmp(t, "SERVER", 6)) {
+ args->atime.set_it = SET_TO_SERVER_TIME;
+ } else {
+ args->atime.set_it = SET_TO_CLIENT_TIME;
+ sscanf (t, "%d.%d", &i, &j);
+ if (i==0) {
+ RFS_ASSERT (j==0);
+ args->atime.atime.seconds = 0;
+ args->atime.atime.nseconds = 0;
+ } else {
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+ args->atime.atime.seconds = tm->sec;
+ args->atime.atime.nseconds = tm->usec * 1000;
+ }
+ }
+ } else if (!strncmp (t, "mtime", 5)) {
+ t+=6;
+ if (!strncmp(t, "SERVER", 6)) {
+ args->mtime.set_it = SET_TO_SERVER_TIME;
+ } else {
+ args->mtime.set_it = SET_TO_CLIENT_TIME;
+ sscanf (t, "%d.%d", &i, &j);
+ if (i==0) {
+ RFS_ASSERT (j==0);
+ args->mtime.mtime.seconds = 0;
+ args->mtime.mtime.nseconds = 0;
+ } else {
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+ args->mtime.set_it = TRUE;
+ args->mtime.mtime.seconds = tm->sec;
+ args->mtime.mtime.nseconds = tm->usec * 1000;
+ }
+ }
+ } else if (!strncmp (t, "size", 4)) {
+ t+=5;
+ sscanf(t, "%x", &i);
+ args->size.set_it = TRUE;
+ args->size.size._p._u = (uint32_t) 0;
+ args->size.size._p._l = (uint32_t) i;
+ } else if (!strncmp (t, "gid", 3)) {
+ t+=4;
+ sscanf(t, "%x", &i);
+ args->gid.set_it = TRUE;
+#ifdef TAKE_CARE_SETATTR_GID
+ args->gid.gid = i;
+#else
+ args->gid.gid = 513;
+#endif
+ } else if ( !strncmp (t, "uid", 3)) {
+ t+=4;
+ sscanf(t, "%x", &i);
+ args->uid.set_it = TRUE;
+#ifdef TAKE_CARE_SETATTR_UID
+ args->uid.uid = i;
+#else
+ args->uid.uid = 513;
+#endif
+ } else if (!strncmp (t, "con", 3)) {
+ break;
+ } else if (!strncmp (t, "sdata", 5)) {
+ break;
+ } else {
+ fprintf(stderr, "parse_sattr t: %s\n", t);
+ RFS_ASSERT (0);
+ }
+
+ while (*t!=' ')
+ t++;
+ t++;
+ }
+ return t;
+}
+
+char * parse_name (char * t, char * buf)
+{
+ int i;
+ if (!strncmp(t, "fn2", 3))
+ t+=4;
+ else if (!strncmp(t, "fn", 2))
+ t+=3;
+ else if (!strncmp(t, "name2", 5))
+ t+=6;
+ else if (!strncmp(t, "name", 4))
+ t+=5;
+ else if (!strncmp(t, "sdata", 5))
+ t+=6;
+ else {
+ fprintf(stderr, "%s\n", t);
+ RFS_ASSERT (0);
+ }
+
+ RFS_ASSERT (*t=='"');
+ t++;
+ i = 0;
+ while (*t!='"')
+ buf[i++] = *t++; // ??? name buffer?
+ RFS_ASSERT ((*t)=='"');
+ buf[i] = 0;
+ return (t+2);
+}
+
+char * parse_access_mode (char * line, int * mode)
+{
+ *mode = ACCESS3_READ;
+ return line+2;
+ /* anyway the information in the trace is not enough, so we just make up something */
+}
+
+char * parse_stable_mode (char * line, stable_how * mode)
+{
+ switch (*line) {
+ case 'U': *mode = UNSTABLE;
+ break;
+ case 'F': *mode = FILE_SYNC;
+ break;
+ case 'D': *mode = DATA_SYNC;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+ return line +2;
+}
+
+
+void setarg_setattr (int index, char * line, SETATTR3args * args)
+{
+ char * t;
+ int i, j;
+
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->object);
+#endif
+
+ args->guard.check = FALSE;
+ t = parse_sattr3 (t, &(args->new_attributes), &(args->guard), index);
+}
+
+void setarg_lookup (int index, char * line, LOOKUP3args * args, char * Cur_filename)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->what.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->what.dir)
+#endif
+ t = parse_name (t, Cur_filename);
+ args->what.name = Cur_filename;
+}
+
+void setarg_access (int index, char * line, ACCESS3args * args)
+{
+ char * t;
+
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->object);
+#endif
+
+ parse_access_mode (t, &args->access); //ACCESS3_MODIFY; // ??? the actual parameter can be different
+}
+
+void setarg_readlink (int index, char * line, READLINK3args * args)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->symlink, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->symlink);
+#endif
+}
+
+void setarg_read (int index, char * line, READ3args * args, char * buf)
+{
+ char * t;
+ int i;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->file, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->file);
+#endif
+
+ if (line[TRACE_VERSION_POS]=='3') {
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ } else {
+ t = strstr (t, "offset");
+ RFS_ASSERT (t);
+ t+=7;
+ }
+ sscanf (t, "%x", &i);
+
+ RFS_ASSERT (i>=0 && i<0x7FFFFFFF)
+ args->offset._p._u = 0;
+ args->offset._p._l = i;
+#ifdef SEQUEN_READ
+ if (index>=memory_trace_index.head)
+ args->offset._p._l = (index-memory_trace_index.head)*4096;
+#endif
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &i);
+
+ RFS_ASSERT (i <= 32768);
+ if (i > NFS_MAXDATA) {
+ read_data_owe += (i-NFS_MAXDATA);
+ read_data_adjust_times ++;
+ if (read_data_owe > 1073741824) {
+ read_data_owe -= 1073741824;
+ read_data_owe_GB ++;
+ }
+
+ //printf ("adjust read count from %d to %d\n", i, NFS_MAXDATA);
+ i = NFS_MAXDATA;
+ }
+ read_data_total += i;
+ if (read_data_total > 1073741824) {
+ read_data_total -= 1073741824;
+ read_data_total_GB ++;
+ }
+
+ args->count = i;
+}
+
+void setarg_write (int index, char * line, WRITE3args * args, char * buf)
+{
+ char * t;
+ int i;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->file, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->file);
+#endif
+
+ //fprintf (stderr, "process write: %s\n", line);
+ if (line[TRACE_VERSION_POS]=='3') {
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ } else {
+ RFS_ASSERT (line[TRACE_VERSION_POS]=='2');
+ t = strstr (t, "offset");
+ RFS_ASSERT (t);
+ t+=7;
+ }
+
+ sscanf (t, "%x", &i);
+ RFS_ASSERT (i>=0 && i<0x7FFFFFFF)
+ args->offset._p._u = 0;
+ args->offset._p._l = i;
+
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &i);
+ RFS_ASSERT (i <= 32768);
+ if (i > NFS_MAXDATA) {
+ write_data_owe += (i-NFS_MAXDATA);
+ if (write_data_owe > 1073741824) {
+ write_data_owe -= 1073741824;
+ write_data_owe_GB ++;
+ }
+ write_data_adjust_times ++;
+ //printf ("adjust write count from %d to %d\n", i, NFS_MAXDATA);
+ i = NFS_MAXDATA;
+ }
+ write_data_total += i;
+ if (write_data_total > 1073741824) {
+ write_data_total -= 1073741824;
+ write_data_total_GB ++;
+ }
+
+ RFS_ASSERT (i < MAX_BUF1_SIZE-128); /* 128 is some random safe number to add */
+ args->count = i;
+
+ if (line[TRACE_VERSION_POS]==3) {
+ t = strstr (t, "stable");
+ RFS_ASSERT (t);
+ t+=7;
+ parse_stable_mode(t, &args->stable); /* *t can be F, U, etc */
+ } else
+ args->stable = UNSTABLE;
+ args->data.data_len = args->count;
+ args->data.data_val = buf;
+}
+
+void setarg_create (int index, char * line, CREATE3args * args, char * Cur_filename)
+{
+ char * t;
+ //fprintf(stderr, "process create %s\n", line);
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->where.dir);
+#endif
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ if (line[TRACE_VERSION_POS]=='3') {
+ RFS_ASSERT (!strncmp(t, "how", 3));
+ t+=4;
+ t = parse_create_mode (t, &args->how.mode);
+ } else
+ args->how.mode = UNCHECKED;
+ t = parse_sattr3 (t, &(args->how.createhow3_u.obj_attributes), NULL, index);
+}
+
+void setarg_mkdir (int index, char * line, MKDIR3args * args, char * Cur_filename)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->where.dir);
+#endif
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ t = parse_sattr3 (t, &(args->attributes), NULL, index);
+}
+
+void setarg_symlink(int index, char * line, SYMLINK3args * args, char * Cur_filename, char * sym_data)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->where.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle (&args->where.dir);
+#endif
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ if (line[TRACE_VERSION_POS]=='2') {
+ t = parse_name (t, sym_data);
+ t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index);
+ } else {
+ t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index);
+ t = parse_name (t, sym_data);
+ }
+ args->symlink.symlink_data = sym_data;
+}
+
+void setarg_mknod(int index, char * line, MKNOD3args * args, char * Cur_filename)
+{
+ RFS_ASSERT (0);
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.what.type = NF3FIFO;
+ args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777);
+ args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid;
+ args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid;
+ args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._l =
+ (uint32_t) 512;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.what.mknoddata3_u.pipe_attributes.mtime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+#endif
+}
+
+void setarg_remove (int index, char * line, REMOVE3args * args, char * Cur_filename)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->object.dir)
+#endif
+ t = parse_name (t, Cur_filename);
+ args->object.name = Cur_filename;
+}
+
+void setarg_rmdir (int index, char * line, RMDIR3args * args, char * Cur_filename)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object.dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->object.dir)
+#endif
+ t = parse_name (t, Cur_filename);
+ args->object.name = Cur_filename;
+}
+
+void setarg_rename (int index, char * line, RENAME3args * args, char * fromname, char * toname)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->from.dir, &dep_tab[index].fh_2->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh_2 + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh_2, ' '); t++;
+#else
+ setarg_fhandle(&args->from.dir)
+#endif
+ t = parse_name (t, fromname);
+ args->from.name = fromname;
+
+ t = strstr (t, "fh2");
+ RFS_ASSERT (t);
+ t += 4;
+ memmove((char *)&args->to.dir, lookup_fhandle(t), sizeof (nfs_fh3));
+ t+=65;
+
+ t = parse_name (t, toname);
+ args->to.name = toname;
+}
+
+void setarg_link (int index, char * line, LINK3args * args, char * Cur_filename)
+{
+ char * t;
+
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->file, &dep_tab[index].fh_2->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh_2 + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh_2, ' '); t++;
+#else
+ setarg_fhandle(&args->file)
+#endif
+
+ t = strstr (t, "fh2");
+ RFS_ASSERT (t);
+ t += 4;
+ memmove((char *)&args->link.dir, lookup_fhandle(t), sizeof (nfs_fh3));
+ t+=65;
+
+ t = parse_name (t, Cur_filename);
+ args->link.name = Cur_filename;
+}
+
+void setarg_readdir (int index, char * line, READDIR3args * args)
+{
+ char * t;
+
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->dir);
+#endif
+ /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */
+ sscanf(t, "cookie %d count %d", &args->cookie._p._l, &args->count);
+ (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args->cookie._p._u = (uint32_t) 0;
+}
+
+void setarg_readdirplus (int index, char * line, READDIRPLUS3args * args)
+{
+ char * t;
+
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->dir, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->dir);
+#endif
+ /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */
+ sscanf(t, "cookie %d count %d maxcnt", &args->cookie._p._l, &args->dircount, &args->maxcount);
+ (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args->cookie._p._u = (uint32_t) 0;
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args.dircount = DEFAULT_MAX_BUFSIZE;
+ args.maxcount = DEFAULT_MAX_BUFSIZE;
+#endif
+}
+
+void setarg_fsstat (int index, char * line, FSSTAT3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->fsroot);
+}
+
+void setarg_fsinfo (int index, char * line, FSINFO3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->fsroot);
+}
+
+void setarg_pathconf (int index, char * line, PATHCONF3args * args)
+{
+ char * t;
+#ifdef TRY_SETARG_FAST
+ memcpy (&args->object, &dep_tab[index].fh->play_fh, sizeof(nfs_fh3));
+ //t = dep_tab[index].trace_fh + TRACE_FH_SIZE+1;
+ t = strchr(dep_tab[index].trace_fh, ' '); t++;
+#else
+ setarg_fhandle(&args->object);
+#endif
+}
+
+void setarg_commit (int index, char * line, COMMIT3args * args)
+{
+ RFS_ASSERT (0);
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = Cur_file_ptr->attributes3.size._p._l;
+#endif
+}
+
+void setbuf_void (char * buf)
+{
+ return;
+}
+
+void setbuf_invalid (char * buf)
+{
+ RFS_ASSERT (0);
+}
+
+void setres_lookup (LOOKUP3res * reply)
+{
+#ifndef TRY_SETRES_FAST
+ (void) memset((char *) &(reply->resok.object), '\0', sizeof (nfs_fh3));
+#endif
+}
+
+void setres_readlink (READLINK3res * reply, char * sym_data)
+{
+ /* Have lower layers fill in the data directly. */
+ reply->resok.data = sym_data;
+}
+
+void setres_read (READ3res * reply, char * buf)
+{
+ /* Have lower layers fill in the data directly. */
+ reply->resok.data.data_len = 0;
+ reply->resok.data.data_val = buf;
+}
+
+void setres_readdir (READDIR3res * reply, entry3 * entry_stream)
+{
+#ifndef TRY_SETRES_FAST
+ /* Have lower layers fill in the data directly. */
+ (void) memset((char *) reply, '\0', sizeof (READDIR3res));
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entry3) * SFS_MAXDIRENTS);
+#endif
+ reply->resok.count = SFS_MAXDIRENTS;
+ reply->resok.reply.entries = entry_stream;
+}
+
+void setres_readdirplus (READDIRPLUS3res * reply, entryplus3 * entry_stream)
+{
+#ifndef TRY_SETRES_FAST
+ (void) memset((char *) reply, '\0', sizeof (READDIRPLUS3res));
+ //printf ("sizeof(entryplus3) %d SFS_MAXDIRENT %d\n", sizeof (entryplus3), SFS_MAXDIRENTS);
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entryplus3) * SFS_MAXDIRENTS);
+#endif
+ reply->resok.count = SFS_MAXDIRENTS;
+ reply->resok.reply.entries = entry_stream;
+}
+
+#define NFSPROC3_INVALID -1
+/* the array is indexed by sfs operation number */
+rfs_op_type rfs_Ops[TOTAL] = {
+{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void},
+{NFSPROC3_GETATTR, setarg_getattr, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res},
+{NFSPROC3_SETATTR, setarg_setattr, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res},
+{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL},
+{NFSPROC3_LOOKUP, setarg_lookup, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res},
+{NFSPROC3_READLINK, setarg_readlink, setres_readlink, xdr_READLINK3args, xdr_READLINK3res},
+{NFSPROC3_READ, setarg_read, setres_read, xdr_READ3args, xdr_READ3res},
+{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL},
+{NFSPROC3_WRITE, setarg_write, setbuf_void, xdr_WRITE3args, xdr_WRITE3res},
+{NFSPROC3_CREATE, setarg_create, setbuf_void, xdr_CREATE3args, xdr_CREATE3res},
+{NFSPROC3_REMOVE, setarg_remove, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res},
+{NFSPROC3_RENAME, setarg_rename, setbuf_void, xdr_RENAME3args, xdr_RENAME3res},
+{NFSPROC3_LINK, setarg_link, setbuf_void, xdr_LINK3args, xdr_LINK3res},
+{NFSPROC3_SYMLINK, setarg_symlink, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res},
+{NFSPROC3_MKDIR, setarg_mkdir, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res},
+{NFSPROC3_RMDIR, setarg_rmdir, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res},
+{NFSPROC3_READDIR, setarg_readdir, setres_readdir, xdr_READDIR3args, xdr_READDIR3res},
+{NFSPROC3_FSSTAT, setarg_fsstat, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res},
+{NFSPROC3_ACCESS, setarg_access, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res},
+{NFSPROC3_COMMIT, setarg_commit, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res},
+{NFSPROC3_FSINFO, setarg_fsinfo, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res},
+{NFSPROC3_MKNOD, setarg_mknod, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res},
+{NFSPROC3_PATHCONF, setarg_pathconf, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res},
+{NFSPROC3_READDIRPLUS, setarg_readdirplus, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}};
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+sfs_op_type nfsv3_Ops[] = {
+
+/* name mix op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+
+sfs_op_type *Ops;
+
--- /dev/null
+#ifndef lint
+static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include "sfs_c_def.h"
+#include "rfs_c_def.h"
+
+extern fh_map_t * lookup_fh (char * trace_fh);
+
+char * lookup_fhandle(char * fhandle)
+{
+ fh_map_t * fh;
+ fh = lookup_fh(fhandle);
+ RFS_ASSERT (fh);
+ return ((char *)&fh->play_fh);
+}
+
+
+#define setarg_fhandle(fhp) \
+ fh_map_t * fh; \
+ t = strstr (line, "fh"); \
+ RFS_ASSERT (t); \
+ t += 3; \
+ fh = lookup_fh(t); \
+ RFS_ASSERT (fh); \
+ (void) memmove((char *)fhp, &(fh->play_fh), \
+ sizeof (nfs_fh3)); \
+ t+=TRACE_FH_SIZE+1;
+
+void setarg_getattr (int index, char * line, GETATTR3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->object);
+}
+
+struct ladtime * adjust_time (struct timeval tm, int * sec, int * usec)
+{
+ struct ladtime trace_pkt_time;
+ static struct ladtime trace_arg_time;
+ struct timeval curtmp;
+ struct ladtime cur;
+
+ /* not sure whether sec ==0 means anything special, do not adjust the timestamp for this */
+ if (*sec ==0) {
+ RFS_ASSERT (0);
+ RFS_ASSERT (*usec == 0);
+ trace_arg_time.sec = 0;
+ trace_arg_time.usec = 0;
+ return (&trace_arg_time);
+ }
+ trace_pkt_time.sec = tm.tv_sec;
+ trace_pkt_time.usec = tm.tv_usec;
+
+ trace_arg_time.sec = *sec;
+ trace_arg_time.usec = *usec;
+
+ gettimeofday(&curtmp, NULL);
+ cur.sec = curtmp.tv_sec;
+ cur.usec = curtmp.tv_usec;
+
+ //fprintf (stderr, "trace_pkt_time %d.%d trace_arg_time %d.%d cur %d.%d\n", trace_pkt_time.sec, trace_pkt_time.usec, trace_arg_time.sec, trace_arg_time.usec, cur.sec, cur.usec);
+
+ ADDTIME (trace_arg_time, cur);
+ //fprintf(stderr, "after add, %d.%d\n", trace_arg_time.sec, trace_arg_time.usec);
+ RFS_ASSERT (LARGERTIME (trace_arg_time, trace_pkt_time));
+ SUBTIME (trace_arg_time, trace_pkt_time);
+ return (&trace_arg_time);
+}
+
+char * parse_create_mode(char * t, createmode3 * mode)
+{
+ *mode = UNCHECKED;
+ return (t+2);
+ /* anyway, we can not get concrete result from the trace, just chose this mode */
+ RFS_ASSERT (0);
+}
+
+char * parse_sattr3(char * t, sattr3 * args, sattrguard3 * guard, int index)
+{
+ int i,j;
+ struct ladtime * tm;
+
+ /* set the default value of SETATTR3args->*/
+ args->mode.set_it = FALSE;
+ args->uid.set_it = FALSE;
+ args->gid.set_it = FALSE;
+ args->size.set_it = FALSE;
+ args->atime.set_it = FALSE;
+ args->mtime.set_it = FALSE;
+
+ //fprintf(stderr, "parse_sattr: line %s\n", t);
+ while (1) {
+ if (!strncmp (t, "mode", 4)) {
+ t+=5;
+ sscanf(t, "%x", &i);
+ args->mode.set_it = TRUE;
+ args->mode.mode = i; // (uint32_t) 0666;
+ } else if (!strncmp (t, "ctime", 5)) {
+ RFS_ASSERT (guard);
+ t+=6;
+ RFS_ASSERT (strncmp(t, "SERVER", 6));
+ sscanf (t, "%d.%d", &i, &j);
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+#ifndef IGNORE_SETATTR_CTIME
+ guard->check = TRUE;
+#endif
+ guard->obj_ctime.seconds = tm->sec;
+ guard->obj_ctime.nseconds = tm->usec*1000;
+ } else if (!strncmp (t, "atime", 5)) {
+ t+=6;
+ if (!strncmp(t, "SERVER", 6)) {
+ args->atime.set_it = SET_TO_SERVER_TIME;
+ } else {
+ args->atime.set_it = SET_TO_CLIENT_TIME;
+ sscanf (t, "%d.%d", &i, &j);
+ if (i==0) {
+ RFS_ASSERT (j==0);
+ args->atime.atime.seconds = 0;
+ args->atime.atime.nseconds = 0;
+ } else {
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+ args->atime.atime.seconds = tm->sec;
+ args->atime.atime.nseconds = tm->usec * 1000;
+ }
+ }
+ } else if (!strncmp (t, "mtime", 5)) {
+ t+=6;
+ if (!strncmp(t, "SERVER", 6)) {
+ args->mtime.set_it = SET_TO_SERVER_TIME;
+ } else {
+ args->mtime.set_it = SET_TO_CLIENT_TIME;
+ sscanf (t, "%d.%d", &i, &j);
+ if (i==0) {
+ RFS_ASSERT (j==0);
+ args->mtime.mtime.seconds = 0;
+ args->mtime.mtime.nseconds = 0;
+ } else {
+ tm = adjust_time (dep_tab[index].timestamp, &i, &j);
+ args->mtime.set_it = TRUE;
+ args->mtime.mtime.seconds = tm->sec;
+ args->mtime.mtime.nseconds = tm->usec * 1000;
+ }
+ }
+ } else if (!strncmp (t, "size", 4)) {
+ t+=5;
+ sscanf(t, "%x", &i);
+ args->size.set_it = TRUE;
+ args->size.size._p._u = (uint32_t) 0;
+ args->size.size._p._l = (uint32_t) i;
+ } else if (!strncmp (t, "gid", 3)) {
+ t+=4;
+ sscanf(t, "%x", &i);
+ args->gid.set_it = TRUE;
+#ifdef TAKE_CARE_SETATTR_GID
+ args->gid.gid = i;
+#else
+ args->gid.gid = 513;
+#endif
+ } else if ( !strncmp (t, "uid", 3)) {
+ t+=4;
+ sscanf(t, "%x", &i);
+ args->uid.set_it = TRUE;
+#ifdef TAKE_CARE_SETATTR_UID
+ args->uid.uid = i;
+#else
+ args->uid.uid = 513;
+#endif
+ } else if (!strncmp (t, "con", 3)) {
+ break;
+ } else if (!strncmp (t, "sdata", 5)) {
+ break;
+ } else {
+ fprintf(stderr, "parse_sattr t: %s\n", t);
+ RFS_ASSERT (0);
+ }
+
+ while (*t!=' ')
+ t++;
+ t++;
+ }
+ return t;
+}
+
+char * parse_name (char * t, char * buf)
+{
+ int i;
+ if (!strncmp(t, "fn2", 3))
+ t+=4;
+ else if (!strncmp(t, "fn", 2))
+ t+=3;
+ else if (!strncmp(t, "name2", 5))
+ t+=6;
+ else if (!strncmp(t, "name", 4))
+ t+=5;
+ else if (!strncmp(t, "sdata", 5))
+ t+=6;
+ else {
+ fprintf(stderr, "%s\n", t);
+ RFS_ASSERT (0);
+ }
+
+ RFS_ASSERT (*t=='"');
+ t++;
+ i = 0;
+ while (*t!='"')
+ buf[i++] = *t++; // ??? name buffer?
+ RFS_ASSERT ((*t)=='"');
+ buf[i] = 0;
+ return (t+2);
+}
+
+char * parse_access_mode (char * line, int * mode)
+{
+ *mode = ACCESS3_READ;
+ return line+2;
+ /* anyway the information in the trace is not enough, so we just make up something */
+}
+
+char * parse_stable_mode (char * line, stable_how * mode)
+{
+ switch (*line) {
+ case 'U': *mode = UNSTABLE;
+ break;
+ case 'F': *mode = FILE_SYNC;
+ break;
+ case 'D': *mode = DATA_SYNC;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+ return line +2;
+}
+
+
+void setarg_setattr (int index, char * line, SETATTR3args * args)
+{
+ char * t;
+ int i, j;
+
+ setarg_fhandle(&args->object);
+ args->guard.check = FALSE;
+ t = parse_sattr3 (t, &(args->new_attributes), &(args->guard), index);
+}
+
+void setarg_lookup (int index, char * line, LOOKUP3args * args, char * Cur_filename)
+{
+ char * t;
+ setarg_fhandle(&args->what.dir)
+ t = parse_name (t, Cur_filename);
+ args->what.name = Cur_filename;
+}
+
+void setarg_access (int index, char * line, ACCESS3args * args)
+{
+ char * t;
+
+ setarg_fhandle (&args->object);
+ parse_access_mode (t, &args->access); //ACCESS3_MODIFY; // ??? the actual parameter can be different
+}
+
+void setarg_readlink (int index, char * line, READLINK3args * args)
+{
+ char * t;
+ setarg_fhandle (&args->symlink);
+}
+
+void setarg_read (int index, char * line, READ3args * args, char * buf)
+{
+ char * t;
+ int i;
+ setarg_fhandle (&args->file);
+
+ if (line[TRACE_VERSION_POS]=='3') {
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ } else {
+ t = strstr (t, "offset");
+ RFS_ASSERT (t);
+ t+=7;
+ }
+ sscanf (t, "%x", &i);
+
+ RFS_ASSERT (i>=0 && i<0x7FFFFFFF)
+ args->offset._p._u = 0;
+ args->offset._p._l = i;
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &i);
+
+ RFS_ASSERT (i <= 32768);
+ if (i > NFS_MAXDATA) {
+ read_data_owe += (i-NFS_MAXDATA);
+ read_data_adjust_times ++;
+ if (read_data_owe > 1073741824) {
+ read_data_owe -= 1073741824;
+ read_data_owe_GB ++;
+ }
+
+ //printf ("adjust read count from %d to %d\n", i, NFS_MAXDATA);
+ i = NFS_MAXDATA;
+ }
+ read_data_total += i;
+ RFS_ASSERT (read_data_total <1073741824);
+
+ args->count = i;
+}
+
+void setarg_write (int index, char * line, WRITE3args * args, char * buf)
+{
+ char * t;
+ int i;
+ setarg_fhandle (&args->file);
+
+ //fprintf (stderr, "process write: %s\n", line);
+ if (line[TRACE_VERSION_POS]=='3') {
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ } else {
+ RFS_ASSERT (line[TRACE_VERSION_POS]=='2');
+ t = strstr (t, "offset");
+ RFS_ASSERT (t);
+ t+=7;
+ }
+
+ sscanf (t, "%x", &i);
+ RFS_ASSERT (i>=0 && i<0x7FFFFFFF)
+ args->offset._p._u = 0;
+ args->offset._p._l = i;
+
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &i);
+ RFS_ASSERT (i <= 32768);
+ if (i > NFS_MAXDATA) {
+ write_data_owe += (i-NFS_MAXDATA);
+ if (write_data_owe > 1073741824) {
+ write_data_owe -= 1073741824;
+ write_data_owe_GB ++;
+ }
+ write_data_adjust_times ++;
+ //printf ("adjust write count from %d to %d\n", i, NFS_MAXDATA);
+ i = NFS_MAXDATA;
+ }
+ write_data_total += i;
+ RFS_ASSERT (write_data_total <1073741824);
+
+ RFS_ASSERT (i < MAX_BUF1_SIZE-128); /* 128 is some random safe number to add */
+ args->count = i;
+
+ if (line[TRACE_VERSION_POS]==3) {
+ t = strstr (t, "stable");
+ RFS_ASSERT (t);
+ t+=7;
+ parse_stable_mode(t, &args->stable); /* *t can be F, U, etc */
+ } else
+ args->stable = UNSTABLE;
+ args->data.data_len = args->count;
+ args->data.data_val = buf;
+}
+
+void setarg_create (int index, char * line, CREATE3args * args, char * Cur_filename)
+{
+ char * t;
+ //fprintf(stderr, "process create %s\n", line);
+ setarg_fhandle (&args->where.dir);
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ if (line[TRACE_VERSION_POS]=='3') {
+ RFS_ASSERT (!strncmp(t, "how", 3));
+ t+=4;
+ t = parse_create_mode (t, &args->how.mode);
+ } else
+ args->how.mode = UNCHECKED;
+ t = parse_sattr3 (t, &(args->how.createhow3_u.obj_attributes), NULL, index);
+}
+
+void setarg_create_old (int index, char * line, CREATE3args * retargs, char * Cur_filename)
+{
+ CREATE3args args;
+
+ sprintf(Cur_filename, "%d", index);
+ if (rfs_debug)
+ printf ("create file %s\n", Cur_filename);
+
+ /* set up the arguments */
+ (void) memmove((char *)&args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename; //RFS need a buffer for the name
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666);
+ args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ args.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0;
+ args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0;
+
+ memcpy (retargs, &args, sizeof (CREATE3args));
+}
+
+void setarg_mkdir (int index, char * line, MKDIR3args * args, char * Cur_filename)
+{
+ char * t;
+ setarg_fhandle (&args->where.dir);
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ t = parse_sattr3 (t, &(args->attributes), NULL, index);
+}
+
+void setarg_symlink(int index, char * line, SYMLINK3args * args, char * Cur_filename, char * sym_data)
+{
+ char * t;
+ setarg_fhandle (&args->where.dir);
+ t = parse_name (t, Cur_filename);
+ args->where.name = Cur_filename;
+ if (line[TRACE_VERSION_POS]=='2') {
+ t = parse_name (t, sym_data);
+ t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index);
+ } else {
+ t = parse_sattr3 (t, &(args->symlink.symlink_attributes), NULL, index);
+ t = parse_name (t, sym_data);
+ }
+ args->symlink.symlink_data = sym_data;
+}
+
+void setarg_mknod(int index, char * line, MKNOD3args * args, char * Cur_filename)
+{
+ RFS_ASSERT (0);
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.what.type = NF3FIFO;
+ args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777);
+ args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid;
+ args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid;
+ args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._l =
+ (uint32_t) 512;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.what.mknoddata3_u.pipe_attributes.mtime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+#endif
+}
+
+void setarg_remove (int index, char * line, REMOVE3args * args, char * Cur_filename)
+{
+ char * t;
+ setarg_fhandle(&args->object.dir)
+ t = parse_name (t, Cur_filename);
+ args->object.name = Cur_filename;
+}
+
+void setarg_rmdir (int index, char * line, RMDIR3args * args, char * Cur_filename)
+{
+ char * t;
+ setarg_fhandle(&args->object.dir)
+ t = parse_name (t, Cur_filename);
+ args->object.name = Cur_filename;
+}
+
+void setarg_rename (int index, char * line, RENAME3args * args, char * fromname, char * toname)
+{
+ char * t;
+ setarg_fhandle(&args->from.dir)
+ t = parse_name (t, fromname);
+ args->from.name = fromname;
+
+ t = strstr (t, "fh2");
+ RFS_ASSERT (t);
+ t += 4;
+ memmove((char *)&args->to.dir, lookup_fhandle(t), sizeof (nfs_fh3));
+ t+=65;
+
+ t = parse_name (t, toname);
+ args->to.name = toname;
+}
+
+void setarg_link (int index, char * line, LINK3args * args, char * Cur_filename)
+{
+ char * t;
+
+ setarg_fhandle(&args->file)
+
+ t = strstr (t, "fh2");
+ RFS_ASSERT (t);
+ t += 4;
+ memmove((char *)&args->link.dir, lookup_fhandle(t), sizeof (nfs_fh3));
+ t+=65;
+
+ t = parse_name (t, Cur_filename);
+ args->link.name = Cur_filename;
+}
+
+void setarg_readdir (int index, char * line, READDIR3args * args)
+{
+ char * t;
+
+ setarg_fhandle(&args->dir);
+ /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */
+ sscanf(t, "cookie %d count %d", &args->cookie._p._l, &args->count);
+ (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args->cookie._p._u = (uint32_t) 0;
+}
+
+void setarg_readdirplus (int index, char * line, READDIRPLUS3args * args)
+{
+ char * t;
+
+ setarg_fhandle(&args->dir);
+ /* args->cookieverf is notset, it is not implemented in the linux-2.4.7 */
+ sscanf(t, "cookie %d count %d maxcnt", &args->cookie._p._l, &args->dircount, &args->maxcount);
+ (void) memset((char *) args->cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args->cookie._p._u = (uint32_t) 0;
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args.dircount = DEFAULT_MAX_BUFSIZE;
+ args.maxcount = DEFAULT_MAX_BUFSIZE;
+#endif
+}
+
+void setarg_fsstat (int index, char * line, FSSTAT3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->fsroot);
+}
+
+void setarg_fsinfo (int index, char * line, FSINFO3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->fsroot);
+}
+
+void setarg_pathconf (int index, char * line, PATHCONF3args * args)
+{
+ char * t;
+ setarg_fhandle(&args->object);
+}
+
+void setarg_commit (int index, char * line, COMMIT3args * args)
+{
+ RFS_ASSERT (0);
+
+#ifdef notdef
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = Cur_file_ptr->attributes3.size._p._l;
+#endif
+}
+
+void setbuf_void (char * buf)
+{
+ return;
+}
+
+void setbuf_invalid (char * buf)
+{
+ RFS_ASSERT (0);
+}
+
+void setres_lookup (LOOKUP3res * reply)
+{
+ (void) memset((char *) &(reply->resok.object), '\0', sizeof (nfs_fh3));
+}
+
+void setres_readlink (READLINK3res * reply, char * sym_data)
+{
+ /* Have lower layers fill in the data directly. */
+ reply->resok.data = sym_data;
+}
+
+void setres_read (READ3res * reply, char * buf)
+{
+ /* Have lower layers fill in the data directly. */
+ reply->resok.data.data_len = 0;
+ reply->resok.data.data_val = buf;
+}
+
+void setres_readdir (READDIR3res * reply, entry3 * entry_stream)
+{
+ /* Have lower layers fill in the data directly. */
+ (void) memset((char *) reply, '\0', sizeof (READDIR3res));
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entry3) * SFS_MAXDIRENTS);
+ reply->resok.count = SFS_MAXDIRENTS;
+ reply->resok.reply.entries = entry_stream;
+}
+
+void setres_readdirplus (READDIRPLUS3res * reply, entryplus3 * entry_stream)
+{
+ (void) memset((char *) reply, '\0', sizeof (READDIRPLUS3res));
+ //printf ("sizeof(entryplus3) %d SFS_MAXDIRENT %d\n", sizeof (entryplus3), SFS_MAXDIRENTS);
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entryplus3) * SFS_MAXDIRENTS);
+ reply->resok.count = SFS_MAXDIRENTS;
+ reply->resok.reply.entries = entry_stream;
+}
+
+#define NFSPROC3_INVALID -1
+/* the array is indexed by sfs operation number */
+rfs_op_type rfs_Ops[TOTAL] = {
+{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void},
+{NFSPROC3_GETATTR, setarg_getattr, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res},
+{NFSPROC3_SETATTR, setarg_setattr, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res},
+{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL},
+{NFSPROC3_LOOKUP, setarg_lookup, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res},
+{NFSPROC3_READLINK, setarg_readlink, setres_readlink, xdr_READLINK3args, xdr_READLINK3res},
+{NFSPROC3_READ, setarg_read, setres_read, xdr_READ3args, xdr_READ3res},
+{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, NULL, NULL},
+{NFSPROC3_WRITE, setarg_write, setbuf_void, xdr_WRITE3args, xdr_WRITE3res},
+{NFSPROC3_CREATE, setarg_create, setbuf_void, xdr_CREATE3args, xdr_CREATE3res},
+{NFSPROC3_REMOVE, setarg_remove, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res},
+{NFSPROC3_RENAME, setarg_rename, setbuf_void, xdr_RENAME3args, xdr_RENAME3res},
+{NFSPROC3_LINK, setarg_link, setbuf_void, xdr_LINK3args, xdr_LINK3res},
+{NFSPROC3_SYMLINK, setarg_symlink, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res},
+{NFSPROC3_MKDIR, setarg_mkdir, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res},
+{NFSPROC3_RMDIR, setarg_rmdir, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res},
+{NFSPROC3_READDIR, setarg_readdir, setres_readdir, xdr_READDIR3args, xdr_READDIR3res},
+{NFSPROC3_FSSTAT, setarg_fsstat, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res},
+{NFSPROC3_ACCESS, setarg_access, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res},
+{NFSPROC3_COMMIT, setarg_commit, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res},
+{NFSPROC3_FSINFO, setarg_fsinfo, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res},
+{NFSPROC3_MKNOD, setarg_mknod, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res},
+{NFSPROC3_PATHCONF, setarg_pathconf, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res},
+{NFSPROC3_READDIRPLUS, setarg_readdirplus, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}};
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+sfs_op_type nfsv3_Ops[] = {
+
+/* name mix op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+
+sfs_op_type *Ops;
+
--- /dev/null
+#ifndef RFS_ASSERT_H
+#define RFS_ASSERT_H
+#define RFS_ASSERT(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "Assertion failed: line %d, file \"%s\"\n", \
+ __LINE__, __FILE__); \
+ fflush(stdout); \
+ fflush(stdout); \
+ fflush(stderr); \
+ fflush(stderr); \
+ exit(-1); \
+ }
+#endif
--- /dev/null
+/* rfs_age_unit_base.c */
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include "rfs_assert.h"
+#include "profile.h"
+#define MKDIR 1
+#define RMDIR 2
+#define CREATE 3
+#define REMOVE 4
+#define WRITE 5
+#define TRUNCATE 6
+
+#define MAX_FILES 400000
+#define MAX_DIRS 100000
+#define FILE_FH_HTABLE_SIZE MAX_FILES
+#define MAX_NAMELEN 512
+#define MAX_PLAY_PATH_SIZE 256
+//#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16)
+//#define NFS_MAXDATA 4096
+#define NFS_MAXDATA 32768
+#define TRACE_FH_SIZE 64
+
+#define FH_T_FLAG_FREE 0
+#define FH_T_FLAG_IN_USE 1
+#define IS_FILE 0
+#define IS_DIR 1
+#define EXIST 0
+#define NON_EXIST 1
+#define COMPLETE 3
+#define ACTIVE 0
+#define INACTIVE 1
+#define DONT_CARE 2
+
+int EVEN_CHUNK_SIZE = 0;
+int STAGE_NUM = 10;
+
+static char ftypename[3][32] = {"FILE", "DIR", "FTYPE_DONT_CARE"};
+static char activename[3][32] = {"ACTIVE", "INACTIVE", "ACTIVE_DONT_CARE"};
+static char existname[4][32] = {"EXIST", "NON_EXIST", "EXIST_DONT_CARE", "COMPLETE"};
+
+typedef struct {
+ char flag;
+ char ftype;
+ char exist_flag;
+ int psfh;
+ int size;
+ int cur_size;
+ int accumulated_write_size;
+ //char trace_fh [TRACE_FH_SIZE+1];
+ char path[MAX_PLAY_PATH_SIZE];
+} fh_t;
+
+typedef struct {
+ char name[32];
+ fh_t * fh;
+ //struct generic_entry * htable;
+ int fh_size;
+ int fh_max;
+ int active_fh_max;
+ //int index;
+ //int htable_size;
+} fh_info_t;
+
+fh_info_t obj_fh;
+profile_t read_line_profile, fgets_profile;
+char trace_file[MAX_NAMELEN];
+FILE * profile_fp = NULL;
+char testdir[MAX_NAMELEN];
+
+int active_obj_num = 0;
+int exist_active_obj_num = 0;
+static int active_file_num = 0, active_dir_num =0, age_file_num = 0, age_dir_num = 0;
+
+int age_create_num = 0;
+int age_mkdir_num = 0;
+int assure_create_num = 0;
+int assure_mkdir_num = 0;
+int age_write_num = 0;
+int nonage_write_num = 0;
+int overlap_write_num = 0;
+
+int fs_size_MB = 0, fs_size = 0;
+int rfs_debug = 0;
+
+
+int ACTIVE_RATIO;
+int FILE_RATIO = 50;
+int WRITE_CHUNK_NUM;
+int MAX_FS_SIZE_MB = 1000000;
+int DISK_FRAGMENT_SIZE = 4096;
+int DISK_FRAGMENT_SIZE_MASK = 0xFFFFF000;
+
+int MIN_WRITE_SIZE;
+
+int aging_dirs ()
+{
+
+}
+
+int f()
+{};
+
+int init_profile_variables()
+{
+ init_profile ("read_line profile", &read_line_profile);
+ init_profile ("fgets profile", &fgets_profile);
+}
+
+int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size)
+{
+ int i;
+
+ RFS_ASSERT (strlen(name) < sizeof(fh_infop->name));
+ strcpy (fh_infop->name, name);
+ fh_infop->fh_max = 0;
+ //fh_infop->index = 0;
+ fh_infop->fh_size = fh_size;
+ //fh_infop->htable_size = htable_size;
+ fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size);
+ RFS_ASSERT (fh_infop->fh);
+ //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size);
+ //RFS_ASSERT (fh_infop->htable);
+ printf("initialize %s size %d bytes\n",
+ //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size);
+ name, sizeof(fh_t)*fh_size);
+
+ for (i=0; i<fh_size; i++)
+ fh_infop->fh[i].flag = FH_T_FLAG_FREE;
+}
+
+int init()
+{
+// init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES);
+// init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS);
+ init_fh_info ("obj_fh", &obj_fh, MAX_FILES+MAX_DIRS, MAX_FILES+MAX_DIRS);
+}
+
+int add_fh_t (fh_info_t * fh_table, char * path, int sfh, int psfh, int size, int ftype, int exist_flag, int active_flag)
+{
+ int i;
+
+ if (size == -1)
+ fs_size += DISK_FRAGMENT_SIZE;
+ else {
+ fs_size += ((size+DISK_FRAGMENT_SIZE-1) & DISK_FRAGMENT_SIZE_MASK);
+ if (size > (DISK_FRAGMENT_SIZE*12)) // first indirect block
+ fs_size += DISK_FRAGMENT_SIZE;
+ }
+ if (fs_size > 1000000) {
+ fs_size_MB += fs_size/1000000;
+ fs_size = fs_size % 1000000;
+ }
+
+ RFS_ASSERT (sfh >0);
+
+ if (active_flag == ACTIVE)
+ active_obj_num ++;
+ else
+ RFS_ASSERT (sfh >= fh_table->active_fh_max);
+
+ if (rfs_debug)
+ printf ("add to %s path %s sfh %d size %d %s %s %s\n", fh_table->name, path, sfh, size,
+ ftypename[ftype], existname[exist_flag], activename[active_flag]);
+
+ RFS_ASSERT ( (sfh>=0) && (sfh<fh_table->fh_size) );
+ RFS_ASSERT (fh_table->fh[sfh].flag==FH_T_FLAG_FREE);
+ fh_table->fh[sfh].flag = FH_T_FLAG_IN_USE;
+ if (sfh >= fh_table->fh_max)
+ fh_table->fh_max = sfh+1;
+ RFS_ASSERT (strlen(path)<MAX_PLAY_PATH_SIZE);
+ strcpy (fh_table->fh[sfh].path, path);
+ fh_table->fh[sfh].psfh = psfh;
+ fh_table->fh[sfh].size = size;
+ fh_table->fh[sfh].cur_size = 0;
+ fh_table->fh[sfh].accumulated_write_size = 0;
+ fh_table->fh[sfh].ftype = ftype;
+ fh_table->fh[sfh].exist_flag = exist_flag;
+ if (active_flag == ACTIVE) {
+ if (ftype == IS_FILE)
+ active_file_num ++;
+ else {
+ RFS_ASSERT (ftype== IS_DIR);
+ active_dir_num ++;
+ }
+ } else {
+ if (ftype == IS_FILE)
+ age_file_num ++;
+ else {
+ RFS_ASSERT (ftype== IS_DIR);
+ age_dir_num ++;
+ }
+ }
+ //print_fh_map(fh_table);
+}
+
+
+int loop_write (int fd, char * buf, int buflen)
+{
+ int ret;
+ int pos = 0;
+
+ while (1) {
+ ret = write (fd, buf+pos, buflen-pos);
+
+ if (ret == -1) {
+ RFS_ASSERT (errno == ENOSPC);
+ fflush(stdout);
+ perror ("loop write");
+ check_free_blocks(0);
+ return -1;
+ }
+ if (ret == buflen-pos)
+ break;
+ pos += ret;
+ }
+ return 0;
+}
+
+int assure_exist(int sfh, char * path, int ftype_flag)
+{
+ char name[MAX_NAMELEN];
+ int ret;
+ char *p, *q;
+ int non_exist_flag = 0;
+ int count=0;
+ struct stat st;
+
+ if (rfs_debug)
+ printf("assure_exist %s\n", path);
+
+ ret = stat (path, &st);
+ if (ret == 0) {
+ if (ftype_flag == IS_DIR) {
+ RFS_ASSERT (st.st_mode & S_IFDIR);
+ RFS_ASSERT (!(st.st_mode & S_IFREG));
+ } else {
+ RFS_ASSERT (st.st_mode & S_IFREG);
+ RFS_ASSERT (!(st.st_mode & S_IFDIR));
+ }
+ return 0;
+ }
+ if (errno!=ENOENT) {
+ perror(path);
+ }
+ //RFS_ASSERT (errno == ENOENT);
+
+ p = path;
+ q = name;
+ if (*p=='/') {
+ *q='/';
+ p++;
+ q++;
+ }
+ while (count++<100) {
+ /* copy the next component from path to name */
+ for (; *p!=0 && *p!='/'; p++, q++ )
+ *q = *p;
+ *q = 0;
+ ret = stat (name, &st);
+ if (ret == -1) {
+ if (errno != ENOENT)
+ perror (name);
+ RFS_ASSERT (errno == ENOENT)
+ if ((*p)==0 && (ftype_flag==IS_FILE)) {
+ ret = creat (name, S_IRWXU);
+ if (ret == -1)
+ perror (name);
+ RFS_ASSERT (ret >=0);
+ assure_create_num ++;
+ if (rfs_debug)
+ printf("sfh %d create %s\n", sfh, name);
+ close(ret);
+ } else {
+ ret = mkdir (name, S_IRWXU);
+ if (ret == -1)
+ perror (name);
+ RFS_ASSERT (ret >=0);
+ assure_mkdir_num ++;
+ if (rfs_debug) {
+ if (*p==0)
+ printf("sfh %d mkdir %s\n", sfh, name);
+ else
+ printf("sfh %d middle mkdir %s\n", sfh, name);
+ }
+ RFS_ASSERT (ret ==0);
+ }
+ }
+ if ((*p)=='/') {
+ *q = '/';
+ p++; q++;
+ } else {
+ RFS_ASSERT ((*p)==0)
+ return 0;
+ }
+ }
+ RFS_ASSERT (0);
+}
+
+
+int print_fh_map(fh_info_t * fhp)
+{
+ int i;
+ int num = 0;
+ int active_obj_num = 0;
+
+
+ for (i=0; i<fhp->fh_max; i++) {
+ if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) {
+ num ++;
+ if (i < fhp->active_fh_max)
+ active_obj_num++;
+
+ if (rfs_debug)
+ printf("%s[%d] %s %s %s\n", fhp->name, i, fhp->fh[i].path, ftypename[fhp->fh[i].ftype], existname[fhp->fh[i].exist_flag]);
+ }
+ }
+ fprintf(stderr, "fh_max %d active_fh_max %d, in_use_num %d entries active_obj_num %d \n", fhp->fh_max, fhp->active_fh_max, num, active_obj_num);
+}
+
+void read_fh_map_line_skimmer (char * buf)
+{
+ char * p;
+ char name[MAX_NAMELEN];
+ int psfh, sfh, size;
+ char filename[MAX_NAMELEN];
+
+ sfh = 0;
+ if (!strncmp(buf, "::DIR ", strlen("::DIR "))) {
+ strcpy (name, testdir);
+ if (buf[6]=='/') {
+ sscanf(buf, "::DIR %s %d\n", name+strlen(name), &sfh);
+ add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE);
+ } /* else {
+ RFS_ASSERT (!strncmp(buf,"::DIR Fake 1\n", strlen("::DIR Fake 1\n")));
+ sfh = 1;
+ add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, EXIST, ACTIVE);
+ exist_active_obj_num ++;
+ } */
+ } else {
+
+ if (!strncmp(buf, "::TBDIR", strlen("::TBDIR")))
+ return;
+
+ p = strstr(buf, "parent");
+ RFS_ASSERT (p);
+ sscanf(p, "parent %d\n", &psfh);
+ RFS_ASSERT (obj_fh.fh[psfh].flag == FH_T_FLAG_IN_USE);
+ p = strstr(p, "name");
+ RFS_ASSERT (p);
+ if (!strncmp(p, "name xx", strlen("name xx"))) {
+ sscanf(p, "name xx-%s sfh %d size %x", filename, &sfh, &size);
+ //printf ("name xx-%s sfh %d\n", filename, sfh);
+ } else {
+ sscanf(p, "name \"%s sfh %d size %x", filename, &sfh, &size);
+ //printf ("name %s sfh %d\n", filename, sfh);
+ filename[strlen(filename)-1]=0;
+ }
+ strcpy (name, obj_fh.fh[psfh].path);
+ strcat (name, "/");
+ strcat (name, filename);
+ add_fh_t (&obj_fh, name, sfh, psfh, size, IS_FILE, NON_EXIST, ACTIVE);
+ }
+}
+
+void read_fh_map_line_ls (char * buf)
+{
+ static int sfh = 2;
+ int size;
+ char name[MAX_NAMELEN];
+ char * p = name;
+
+ strcpy (name, testdir);
+ strcat (name, "/");
+ if (strchr(buf, ' ')) {
+ sscanf(buf, "%d %s\n", &size, p+strlen(name));
+ add_fh_t (&obj_fh, name, sfh, -1, size, IS_FILE, NON_EXIST, ACTIVE);
+ } else {
+ sscanf(buf, "%s\n", p+strlen(name));
+ add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE);
+ };
+ sfh ++;
+}
+
+void read_fh_map (char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ int lineno = 0;
+ int fh_map_debug =0;
+#define FH_MAP_FORMAT_SKIMMER 0
+#define FH_MAP_FORMAT_LS 1
+ int fh_map_format;
+
+ if (strstr(fh_map_file, ".ski"))
+ fh_map_format = FH_MAP_FORMAT_SKIMMER;
+ else
+ fh_map_format = FH_MAP_FORMAT_LS;
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+
+
+ memset(buf, 0, sizeof(buf));
+
+ while (fgets(buf, 1024, fp)) {
+ RFS_ASSERT (fh_map_debug==0);
+ lineno ++;
+ if (rfs_debug)
+ printf ("line %d %s", lineno, buf);
+ if (lineno % 10000==0)
+ printf("%d fh_map entry read\n", lineno);
+ if (fh_map_format == FH_MAP_FORMAT_SKIMMER)
+ read_fh_map_line_skimmer(buf);
+ else
+ read_fh_map_line_ls (buf);
+ }
+
+ fclose(fp);
+ obj_fh.active_fh_max = obj_fh.fh_max;
+ if (fh_map_debug) {
+ print_fh_map (&obj_fh);
+ }
+}
+
+int print_usage()
+{
+ printf("\n\nagefs fmt4 HOLDER_NUM HOLDER_SIZE DISK_FRAGMENT_SIZE testdir\n");
+ printf("Note: fmt4 is used to initialize the holders in a logical partition before starting writing aged files in a specific pattern as by fmt3 command\n");
+
+ printf("\n\nagefs fmt3 aged_file CHUNK_SIZE CHUNK_DISTANCE CHUNK_NUM START_BNO HOLDER_SIZE HOLDER_NUM DISK_FRAGMENT_SIZE testdir\n");
+ printf("Note: one file is written as CHUNK_NUM number of continuous chunks on disk, each chunk is of CHUNK_SIZE blocks, the distance between two adjacent chunks is CHUNK_DISTANCE blocks\n");
+
+ printf("\n\nagefs fmt2 size1 .. size_n num testdir\n");
+ printf("Note: N file is writen interleavingly for _num_ times, each time _size1_ bytes is written to file1, _size2_ bytes is written to file2, _sizen_ bytes is written to filen\n");
+
+ printf("\n\nagefs EVEN_CHUNK_SIZE FILE_RATIO ACTIVE_RATIO WRITE_CHUNK_NUM MAX_FS_SIZE_MB STAGE_NUM fh_path_map testdir\n");
+ printf("Note: EVEN_CHUNK_SIZE: if 1, each file is chopped to equal size chunks, if 0, each file size is chopped randomly but with the average size to be CHUNK_SIZE");
+ printf(" FILE_RATIO: percentage of number of inactive files over number of inactive file system objects\n");
+ printf(" ACTIVE_RATIO: percentage of number of active file system objects over all file system objects\n");
+ printf(" WRITE_CHUNK_NUM: when a file is initialized, it is written in several open-close session interleved with writes to other files. Except small files where file_size/WRITE_CHUNK_SIZE is less than DISK_FRAGMENT_SIZE, each open-close session on the average write (file_size/WRITE_CHUNK_NUM) bytes. \n");
+ printf(" MAX_FS_SIZE_MB: another condition to stop initialization, either all active file is initialized, or max file system size is reached\n");
+ printf(" STAGE_NUM: divide the writing of files into several stages, each stage finish initialization of some of the files. The bigger the STAGE_NUM, the less concurrency is there\n");
+}
+
+inline char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+
+#define READ_LINE_BUF_SIZE 1000
+#define READ_LINE_LENGTH 32
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ fclose(fp);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+int print_result()
+{
+ struct statfs stfs;
+ int ret;
+ static struct statfs first_stfs;
+ static int first_entry = 1;
+
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ if (first_entry) {
+ first_entry = 0;
+ first_stfs = stfs;
+ }
+
+ fprintf(stderr, "active_file_num %d active_dir_num %d age_file_num %d age_dir_num %d\n",
+ active_file_num, active_dir_num, age_file_num, age_dir_num);
+ fprintf(stderr, "number of used file nodes %d, used (4K) blocks in fs %d (%d MB)\n", first_stfs.f_ffree-stfs.f_ffree, first_stfs.f_bfree - stfs.f_bfree, (first_stfs.f_bfree-stfs.f_bfree)/(1000000/DISK_FRAGMENT_SIZE));
+ fprintf(stderr, "assure_create_num %d assure_mkdir_num %d\n", assure_create_num, assure_mkdir_num);
+}
+
+typedef struct {
+ int pcnt; /* percentile */
+ int size; /* file size in KB */
+} sfs_io_file_size_dist;
+
+sfs_io_file_size_dist Default_file_size_dist[] = {
+ /* percentage KB size */
+#ifdef notdef
+ { 100, 128},
+ { 94, 64}, /* 4% */
+ { 97, 128}, /* 3% */
+#endif
+ { 33, 1}, /* 33% */
+ { 54, 2}, /* 21% */
+ { 67, 4}, /* 13% */
+ { 77, 8}, /* 10% */
+ { 85, 16}, /* 8% */
+ { 90, 32}, /* 5% */
+ { 94, 64}, /* 4% */
+ { 97, 128}, /* 3% */
+ { 99, 256}, /* 2% */
+ { 100, 1024}, /* 1% */
+ { 0, 0}
+};
+
+/*
+ * For a value between 0-99, return a size based on distribution
+ */
+static int
+get_file_size()
+{
+ static file_array_initialized = 0;
+ static int file_size_array[100];
+ int i;
+
+ i = random() % 100;
+
+ if (i < 0 || i > 99)
+ return (0);
+
+ if (file_array_initialized == 0) {
+ int j, k;
+ for (j = 0, k = 0; j < 100; j++) {
+ if (j >= Default_file_size_dist[k].pcnt &&
+ Default_file_size_dist[k + 1].size != 0)
+ k++;
+ file_size_array[j] = Default_file_size_dist[k].size * 1024;
+ }
+ file_array_initialized++;
+ }
+ return (file_size_array[i]);
+}
+
+int range_random(int min, int max)
+{
+ int i;
+ i = (random()%(max-min)) + min;
+ return i;
+}
+
+/* answer 1 with a probability of percent/100 */
+int decide(int percent)
+{
+ int i = random()%100;
+ if (i<percent)
+ return 1;
+ else
+ return 0;
+}
+
+int select_obj (fh_info_t * fhp, int ftype, int exist_flag, int active_flag, int min, int max)
+{
+ int i;
+ int sfh, count = 0;
+
+ //printf ("select_obj %s %s %s\n", ftypename[ftype], existname[exist_flag], activename[active_flag]);
+ if (active_flag == ACTIVE) {
+ sfh = range_random (0, fhp->active_fh_max);
+ for (i=0; i<fhp->active_fh_max; i++) {
+ if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) &&
+ ((ftype==DONT_CARE) || (ftype ==fhp->fh[sfh].ftype)) &&
+ ((exist_flag==DONT_CARE) || (fhp->fh[sfh].exist_flag == exist_flag))) {
+ return sfh;
+ }
+ sfh = (sfh+1) % fhp->active_fh_max;
+ }
+ } else {
+ //min = 0;
+ //max = fhp->fh_max;
+ //printf ("select_obj min %d max %d\n", min, max);
+ RFS_ASSERT (active_flag == DONT_CARE);
+ RFS_ASSERT (exist_flag == EXIST);
+ sfh = range_random (min, max);
+ for (i=min; i<max; i++) {
+ // printf("check %d\n", sfh);
+ RFS_ASSERT ((sfh>=min) && (sfh<max));
+ if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) &&
+ ((ftype==DONT_CARE) || (fhp->fh[sfh].ftype == ftype)) &&
+ (fhp->fh[sfh].exist_flag == EXIST)) {
+ return sfh;
+ }
+ sfh++;
+ if (sfh==max)
+ sfh = min;
+ }
+ }
+/*
+ for (i=min; i<max; i++) {
+ if ((fhp->fh[i].flag == FH_T_FLAG_IN_USE) &&
+ (fhp->fh[i].ftype == IS_FILE) ) {
+ if (fhp->fh[i].exist_flag == EXIST) {
+ printf ("actually %d\n", i);
+ }
+ RFS_ASSERT (fhp->fh[i].exist_flag != EXIST);
+ RFS_ASSERT (fhp->fh[i].exist_flag == COMPLETE);
+ }
+ }
+*/
+ return -1;
+}
+
+/* append "size" to file "path" */
+int append_file (int sfh, char * path, int size)
+{
+ int fd;
+ int written_bytes = 0;
+ int ret;
+#define BUF_SIZE 32768
+ static char buf[BUF_SIZE];
+
+ if (rfs_debug)
+ printf ("sfh %d append_file %s size %d\n", sfh, path, size);
+
+ fd = open (path, O_WRONLY|O_APPEND);
+ if (fd==-1)
+ perror(path);
+ RFS_ASSERT (fd > 0);
+
+ while (written_bytes+NFS_MAXDATA < size) {
+ ret = loop_write (fd, buf, NFS_MAXDATA);
+ RFS_ASSERT (ret!=-1);
+ written_bytes += NFS_MAXDATA;
+ }
+ ret = loop_write (fd, buf, size-written_bytes);
+ RFS_ASSERT (ret!=-1);
+ close(fd);
+}
+
+int get_write_size (int target_size, int cur_size)
+{
+ int i;
+
+ if (target_size - cur_size < MIN_WRITE_SIZE)
+ return (target_size - cur_size);
+
+ /* target_size/WRITE_CHUNK_NUM would be the average value of i */
+ if (target_size < WRITE_CHUNK_NUM) {
+ i = MIN_WRITE_SIZE;
+ } else {
+
+ if (EVEN_CHUNK_SIZE)
+ i = target_size/WRITE_CHUNK_NUM;
+ else
+ i = random() % (2*(target_size/WRITE_CHUNK_NUM));
+
+ if (i < MIN_WRITE_SIZE)
+ i = MIN_WRITE_SIZE;
+ }
+
+ if (i > (target_size - cur_size))
+ i = target_size - cur_size;
+
+ return i;
+}
+
+FILE * fplog;
+
+
+int CHUNK_SIZE;
+int CHUNK_DISTANCE;
+int CHUNK_NUM;
+int START_BNO;
+int HOLDER_NUM;
+int HOLDER_SIZE;
+int INDIRECT_FANOUT;
+char agename[1024];
+
+#define MAX_DISK_FRAGMENT_SIZE 4096
+#define MAX_HOLDER_SIZE 10
+#define HOLDER_DIR_NUM 1
+#define DUMMY_FILE_COUNT 1000
+
+main4(int argc, char ** argv)
+{
+ int i, j;
+ char name[256];
+ char cmd[1024];
+ char * buf;
+ int fd, ret;
+ char testdir[1024];
+
+ if (argc!=6) {
+ print_usage();
+ return;
+ }
+ i = 2;
+ HOLDER_NUM = atoi(argv[i++]);
+ HOLDER_SIZE = atoi(argv[i++]);
+ DISK_FRAGMENT_SIZE = atoi(argv[i++]);
+ RFS_ASSERT (DISK_FRAGMENT_SIZE <= MAX_DISK_FRAGMENT_SIZE);
+ DISK_FRAGMENT_SIZE_MASK = ~(DISK_FRAGMENT_SIZE-1);
+ RFS_ASSERT ((DISK_FRAGMENT_SIZE_MASK == 0xFFFFF000) ||
+ (DISK_FRAGMENT_SIZE_MASK == 0xFFFFFC00) );
+ strcpy (testdir, argv[i]);
+
+ fprintf(fplog, "main4: initialize the holders HOLDER_NUM %d HOLDER_SIZE %d DISK_FRAGMENT_SIZE %d testdir %s\n",
+ HOLDER_NUM, HOLDER_SIZE, DISK_FRAGMENT_SIZE, testdir);
+ fflush(fplog);
+
+ buf = (char *)malloc (HOLDER_SIZE*DISK_FRAGMENT_SIZE);
+ RFS_ASSERT (buf);
+
+ /* create some dummy files */
+ for (i=0; i<DUMMY_FILE_COUNT; i++) {
+ memset (name, 0, sizeof(name));
+ sprintf(name, "%s/dummy%d", testdir, i);
+ fd = creat (name, S_IRWXU);
+ if (fd == -1) {
+ perror(name);
+ exit(-1);
+ }
+ close (fd);
+ }
+
+ /* create directories */
+ if (HOLDER_DIR_NUM !=1) {
+ for (i=0; i<HOLDER_DIR_NUM; i++) {
+ memset (name, 0, sizeof(name));
+ sprintf(name, "%s/%d", testdir, i);
+ ret = mkdir (name, S_IRWXU);
+ if (ret == -1) {
+ perror(name);
+ exit(-1);
+ }
+ }
+ }
+
+ /* create regular files */
+ for (j=0; j<HOLDER_DIR_NUM; j++) {
+ for (i=0; i<HOLDER_NUM/HOLDER_DIR_NUM; i++) {
+ if (HOLDER_DIR_NUM == 1)
+ sprintf(name, "%s/%d", testdir, i);
+ else
+ sprintf(name, "%s/%d/%d", testdir, j, i);
+ fd = open (name, O_CREAT|O_WRONLY);
+ if (fd == -1) {
+ perror(name);
+ exit(-1);
+ }
+ ret = loop_write (fd, buf, HOLDER_SIZE*DISK_FRAGMENT_SIZE);
+ close (fd);
+ if (ret == -1)
+ break;
+ }
+ }
+
+#ifdef notdef
+ /* delete the dummy files */
+ for (i=0; i<DUMMY_FILE_COUNT; i++) {
+ memset (name, 0, sizeof(name));
+ sprintf(name, "%s/dummy%d", testdir, i);
+ ret = unlink (name);
+ if (ret == -1) {
+ perror(name);
+ exit(-1);
+ }
+ }
+#endif
+}
+
+int append_space_occupier()
+{
+ static char name [1024];
+ static FILE * fp = NULL;
+ char buf[MAX_DISK_FRAGMENT_SIZE];
+ int ret;
+ if (fp == NULL) {
+ sprintf(name, "%s/space_ocuupier", testdir);
+ fp = fopen (name, "a+");
+ RFS_ASSERT (fp!= NULL);
+ ret = fwrite (buf, DISK_FRAGMENT_SIZE, 1, fp);
+ if (ret != 1) {
+ perror("append space occupier");
+ }
+ fclose (fp);
+ fp = NULL;
+ };
+ ret = fwrite (buf, DISK_FRAGMENT_SIZE, 1, fp);
+ if (ret != 1) {
+ perror("append space occupier");
+ }
+ RFS_ASSERT (ret == 1);
+}
+
+int create_one_dummy_file()
+{
+ int i, fd, ret;
+ static int index = 0;
+ static char name[1024];
+ struct stat st;
+
+ for (i=0; i<DUMMY_FILE_COUNT; i++) {
+ index = (index+1) % DUMMY_FILE_COUNT;
+ sprintf(name, "%s/dummy%d", testdir, i);
+ ret = stat (name, &st);
+ if (ret == -1) {
+ RFS_ASSERT (errno == ENOENT);
+ fd = open (name, O_CREAT|O_WRONLY);
+ RFS_ASSERT (fd >=0);
+ close (fd);
+ return 0;
+ };
+ }
+}
+
+int delete_one_dummy_file()
+{
+ int i,ret;
+ static int index = 0;
+ static char name[1024];
+ struct stat st;
+
+ for (i=0; i<DUMMY_FILE_COUNT; i++) {
+ index = (index+1) % DUMMY_FILE_COUNT;
+ sprintf(name, "%s/dummy%d", testdir, i);
+ ret = stat (name, &st);
+ if (ret == 0) {
+ ret = unlink (name);
+ RFS_ASSERT (ret == 0);
+ return;
+ } else
+ RFS_ASSERT (errno == ENOENT);
+ }
+ RFS_ASSERT (0);
+}
+
+int create_sub_holder_file(int holderno, int sub_holderno)
+{
+ char name[256];
+ int fd, ret;
+ static char buf[MAX_DISK_FRAGMENT_SIZE];
+
+
+ RFS_ASSERT (MAX_DISK_FRAGMENT_SIZE >= DISK_FRAGMENT_SIZE);
+
+ sprintf(name, "%d.%d", holderno, sub_holderno);
+ printf ("create/write %s\n", name);
+ fd = open (name, O_CREAT|O_WRONLY);
+ RFS_ASSERT (fd >=0);
+ loop_write (fd, buf, DISK_FRAGMENT_SIZE);
+ close(fd);
+}
+
+int get_free_blocks ()
+{
+ static struct statfs stfs;
+ int ret;
+
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ return (stfs.f_bfree);
+}
+
+int print_free_blocks (char *string)
+{
+ static struct statfs stfs;
+ int ret;
+
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ printf("%s f_bfree %d \n", string, stfs.f_bfree);
+}
+
+int check_free_blocks (int num)
+{
+ static struct statfs stfs;
+ int ret;
+
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ if (stfs.f_bfree!=num) {
+ printf("f_bfree %d expected %d\n", stfs.f_bfree, num);
+ RFS_ASSERT (0);
+ }
+}
+
+int progress_on_aged_file(int * num)
+{
+ char buf[MAX_DISK_FRAGMENT_SIZE*MAX_HOLDER_SIZE];
+ static need_indirect_blocks = 0;
+ static skip_for_indirect_blocks = 0;
+ static int blkno = 0;
+
+ printf ("\n");
+ print_free_blocks("progress_on_aged_file begin");
+
+ if (skip_for_indirect_blocks == need_indirect_blocks) {
+ //check_free_blocks(free_block_num);
+ //RFS_ASSERT (free_block_num >= (1+need_indirect_blocks));
+ (*num) --;
+ printf("append to aged file %d bytes\n", DISK_FRAGMENT_SIZE);
+ append_file (0, agename, DISK_FRAGMENT_SIZE);
+ //*free_block_num -= (need_indirect_blocks +1)
+ //check_free_blocks(free_block_num);
+
+ blkno ++;
+ if (((blkno - 12) % INDIRECT_FANOUT) == 0) {
+ if (((blkno - (INDIRECT_FANOUT+12)) % (INDIRECT_FANOUT*INDIRECT_FANOUT)) == 0) {
+ if (blkno == 12 + INDIRECT_FANOUT + INDIRECT_FANOUT*INDIRECT_FANOUT) {
+ printf ("need_indirect_blocks is set to 3 blkno %d\n", blkno);
+ need_indirect_blocks = 3;
+ } else {
+ printf ("need_indirect_blocks is set to 2 blkno %d\n", blkno);
+ need_indirect_blocks = 2;
+ }
+ } else {
+ printf ("need_indirect_blocks is set to 1 blkno %d\n", blkno);
+ need_indirect_blocks = 1;
+ };
+ } else {
+ need_indirect_blocks = 0;
+ }
+ skip_for_indirect_blocks = 0;
+ } else {
+ skip_for_indirect_blocks ++;
+ }
+
+ printf ("skip_for_indirect_blocks -- %d\n", skip_for_indirect_blocks);
+ print_free_blocks("progress_on_aged_file end");
+}
+
+int free_blocks (char * agename, int start, int num)
+{
+ int holderno;
+ char name [128];
+ int ret;
+ struct stat st;
+ int sub_holderno;
+ int i;
+
+ printf ("free_blocks start %d num %d\n", start, num);
+
+BEGIN:
+ check_free_blocks(0);
+ if (num == 0)
+ return start;
+ holderno = start/HOLDER_SIZE;
+ sub_holderno = start%HOLDER_SIZE;
+
+ sprintf (name, "%d", holderno);
+
+ ret = stat (name, &st);
+ if (ret == -1) {
+ RFS_ASSERT (errno == ENOENT);
+ for (i=sub_holderno; (i<HOLDER_SIZE && num>0); i++) {
+ sprintf(name, "%d.%d", holderno, i);
+ ret = stat (name, &st);
+ if (ret == 0) {
+
+ printf ("sub_holder file %s is unlinked\n", name);
+ ret = unlink(name);
+ RFS_ASSERT (ret == 0);
+
+ create_one_dummy_file();
+
+ printf ("write to age file %d bytes\n", DISK_FRAGMENT_SIZE);
+
+ progress_on_aged_file(&num);
+
+ } else {
+ printf ("sub_holder file %s is already used\n", name);
+ RFS_ASSERT ((ret == -1) && (errno ==ENOENT));
+ }
+ start ++;
+ }
+ goto BEGIN;
+ }
+
+ RFS_ASSERT (ret == 0);
+ RFS_ASSERT (st.st_size == HOLDER_SIZE * DISK_FRAGMENT_SIZE);
+ printf ("holder file %s is unlinked\n", name);
+ ret = unlink(name);
+ RFS_ASSERT (ret == 0);
+ check_free_blocks(HOLDER_SIZE);
+
+ /* create the sub holders before the first slot that we need */
+ for (i=0; i<sub_holderno; i++) {
+ delete_one_dummy_file();
+ create_sub_holder_file (holderno, i);
+ }
+
+ /*
+ i = (HOLDER_SIZE - sub_holderno) < num? (HOLDER_SIZE - sub_holderno): num;
+ sub_holderno += i;
+ start += i;
+ num -= i;
+ check_free_blocks (i);
+ ret = loop_write (agefd, buf, DISK_FRAGMENT_SIZE*i);
+ RFS_ASSERT (ret != -1);
+ */
+ i = HOLDER_SIZE - sub_holderno;
+ check_free_blocks(i);
+
+ while ((sub_holderno < HOLDER_SIZE) && (num>0)) {
+ sub_holderno ++;
+ start ++;
+
+ progress_on_aged_file(&num);
+
+ RFS_ASSERT (ret != -1);
+ }
+
+ /* create the sub holders after the slot that we need */
+ for (i=sub_holderno; i<HOLDER_SIZE; i++) {
+ delete_one_dummy_file();
+ create_sub_holder_file (holderno, i);
+ }
+
+ create_one_dummy_file();
+ goto BEGIN;
+}
+
+int main3(int argc, char ** argv)
+{
+
+ int block_anchor;
+ int i;
+ int agefd;
+ char * buf;
+ char cwd[1024];
+ char * ret;
+ struct stat st;
+
+ if (argc!=11) {
+ print_usage();
+ return;
+ }
+ i = 2;
+
+ CHUNK_SIZE = atoi(argv[i++]);
+ CHUNK_DISTANCE = atoi (argv[i++]);
+ CHUNK_NUM = atoi (argv[i++]);
+ START_BNO = atoi (argv[i++]);
+ HOLDER_SIZE = atoi (argv[i++]);
+ RFS_ASSERT (HOLDER_SIZE <= MAX_HOLDER_SIZE);
+ HOLDER_NUM = atoi (argv[i++]);
+ DISK_FRAGMENT_SIZE = atoi (argv[i++]);
+ RFS_ASSERT (DISK_FRAGMENT_SIZE <= MAX_DISK_FRAGMENT_SIZE);
+ INDIRECT_FANOUT = (DISK_FRAGMENT_SIZE/sizeof(int));
+ strcpy (testdir, argv[i++]);
+ strcpy (agename, testdir);
+ strcat (agename, argv[i++]);
+ ret = stat (agename, &st);
+ if (ret!=-1) {
+ printf ("%s already exists\n", agename);
+ }
+ RFS_ASSERT (errno == ENOENT);
+
+ fprintf(fplog, "main3: to age one file %s in a customized way, CHUNK_SIZE %d CHUNK_DISTANCE %d CHUNK_NUM %d START_BNO %d HOLDER_SIZE %d HOLDER_NUM %d DISK_FRAGMENT_SIZE %d MAX_DISK_FRAGMENT_SIZE %d testdir %s\n", agename, CHUNK_SIZE, CHUNK_DISTANCE, CHUNK_NUM, START_BNO, HOLDER_SIZE, HOLDER_NUM, DISK_FRAGMENT_SIZE, MAX_DISK_FRAGMENT_SIZE, testdir);
+ fflush(fplog);
+
+ /* change working directory */
+ ret = getcwd(cwd, sizeof(cwd));
+ RFS_ASSERT (ret == cwd);
+ i = chdir (testdir);
+ RFS_ASSERT (i==0);
+
+ if (START_BNO == -1) {
+ block_anchor = random() % (HOLDER_NUM*HOLDER_SIZE);
+ } else {
+ RFS_ASSERT (START_BNO >=0);
+ block_anchor = START_BNO % (HOLDER_NUM*HOLDER_SIZE);
+ }
+ while (get_free_blocks()!=0) {
+ print_free_blocks("fill up initial file system blocks using space occupier");
+ append_file (0, "/b6/space_occupier", DISK_FRAGMENT_SIZE);
+ };
+ delete_one_dummy_file();
+ agefd = open (agename, O_CREAT|O_WRONLY);
+ RFS_ASSERT (agefd>=0);
+ close (agefd);
+
+ buf = (char *)malloc (CHUNK_SIZE*DISK_FRAGMENT_SIZE);
+ RFS_ASSERT (buf);
+
+ for (i=0; i<CHUNK_NUM; i++) {
+ block_anchor = (block_anchor + CHUNK_DISTANCE) % (HOLDER_NUM * HOLDER_SIZE);
+ block_anchor = free_blocks (agename, block_anchor, CHUNK_SIZE);
+ }
+
+ check_free_blocks(0);
+ i = chdir (cwd);
+ RFS_ASSERT (i==0);
+}
+
+int main2(int argc, char ** argv)
+{
+ int i, j;
+ int size[3], num;
+ FILE * fp[3];
+ char name[3][128];
+ char cmd[1024];
+
+ if (argc <= 4) {
+ print_usage();
+ return;
+ }
+ num = atoi(argv[argc-2]);
+ strcpy (testdir, argv[argc-1]);
+ fprintf(fplog, "main2: generate interleaved files\n");
+ fprintf(fplog, "testdir %s number of files %d ", testdir, num);
+ for (i=0; i<argc-4; i++) {
+ size[i] = atoi(argv[i+2]);
+ fprintf(fplog, "size[%d] %d ", i, size[i]);
+
+ RFS_ASSERT (size[i] >=0 && size[i] < 1000000000);
+ strcpy (name[i], testdir);
+ sprintf (name[i], "%s/file%d", testdir, i);
+ sprintf(cmd, "touch %s", name[i]);
+ system(cmd);
+ printf ("write %s \n", name[i]);
+ };
+ fprintf(fplog, "\n");
+ fflush(fplog);
+
+ for (j=0; j<num; j++) {
+ for (i=0; i<argc-4; i++)
+ append_file (i, name[i], size[i]);
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int i;
+ char cmd[1024];
+ char AGELOG_NAME[1024]= "/home/ningning/agefs.log";
+
+ sprintf(cmd, "date >> %s", AGELOG_NAME);
+ system (cmd);
+ fplog = fopen(AGELOG_NAME, "a");
+ RFS_ASSERT (fplog);
+ for (i=0; i<argc; i++) {
+ fprintf(fplog, "%s ", argv[i]);
+ }
+ fprintf (fplog, "\n");
+
+ if (argc>1 && (!strcmp(argv[1], "fmt2")))
+ main2 (argc, argv);
+ else if (argc>1 && (!strcmp(argv[1], "fmt3")))
+ main3 (argc, argv);
+ else if (argc>1 && (!strcmp(argv[1], "fmt4")))
+ main4 (argc, argv);
+ else
+ main1 (argc, argv);
+END:
+ fclose (fplog);
+ sprintf(cmd, "date >> %s", AGELOG_NAME);
+ system (cmd);
+}
+
+int main1(int argc, char ** argv)
+{
+ char * buf;
+ static int disk_index=0;
+ int nfs3proc, size, off, count;
+ char procname[16];
+ struct stat st;
+ int ret;
+ int i,j,k;
+ int ftype_flag = 0, active_flag = 0;
+ char name[MAX_PLAY_PATH_SIZE];
+ int sfh, psfh;
+ char mapname[1024];
+
+ profile_t create_profile, write_profile;
+ if (argc!=9) {
+ print_usage();
+ return;
+ }
+
+ init();
+ EVEN_CHUNK_SIZE = atoi(argv[1]);
+ RFS_ASSERT ((EVEN_CHUNK_SIZE==0) || (EVEN_CHUNK_SIZE==1));
+ FILE_RATIO = atoi (argv[2]);
+ ACTIVE_RATIO = atoi(argv[3]);
+ WRITE_CHUNK_NUM = atoi(argv[4]);
+ MAX_FS_SIZE_MB = atoi(argv[5]);
+
+ if (WRITE_CHUNK_NUM==0)
+ MIN_WRITE_SIZE = 2000000000;
+ else {
+ //MIN_WRITE_SIZE = DISK_FRAGMENT_SIZE;
+ MIN_WRITE_SIZE = 1;
+ }
+
+ STAGE_NUM = atoi (argv[6]);
+ strcpy (mapname, argv[7]);
+ strcpy (testdir, argv[8]);
+ ret = stat (testdir, &st);
+ if ((ret == -1) && (errno==ENOENT)) {
+ ret = mkdir (testdir, S_IRWXU);
+ }
+ RFS_ASSERT (ret >= 0);
+
+
+ /* add testdir to obj_fh */
+ add_fh_t (&obj_fh, testdir, 1, -1, -1, IS_DIR, EXIST, ACTIVE);
+ exist_active_obj_num ++;
+ if (ACTIVE_RATIO >0)
+ read_fh_map (mapname);
+
+ print_fh_map(&obj_fh);
+ init_profile_variables();
+
+ fprintf(fplog, "main1: populate the file system with both trace files and randomly generated files\n");
+ fprintf(fplog, "EVEN_CHUNK_SIZE %d FILE_RATIO %d ACTIVE_RATIO %d WRITE_CHUNK_NUM %d MAX_FS_SIZE_MB %d STAGE_NUM %d fh_map %s testdir %s\n", EVEN_CHUNK_SIZE, FILE_RATIO, ACTIVE_RATIO, WRITE_CHUNK_NUM, MAX_FS_SIZE_MB, STAGE_NUM, mapname, testdir);
+ system("date");
+ printf("EVEN_CHUNK_SIZE %d FILE_RATIO %d ACTIVE_RATIO %d WRITE_CHUNK_NUM %d MAX_FS_SIZE_MB %d STAGE_NUM %d fh_map %s testdir %s\n", EVEN_CHUNK_SIZE, FILE_RATIO, ACTIVE_RATIO, WRITE_CHUNK_NUM, MAX_FS_SIZE_MB, STAGE_NUM, mapname, testdir);
+ fflush(fplog);
+
+ profile_fp = fplog;
+ init_profile ("create_profile", &create_profile);
+ init_profile ("write_profile", &write_profile);
+
+ start_profile (&create_profile);
+ printf ("start creat/mkdir, active_obj_num %d\n", active_obj_num);
+ for (i=0; (exist_active_obj_num <= active_obj_num) && (fs_size_MB < MAX_FS_SIZE_MB); i++) {
+
+ if ((i!=0) && ((i%10000)==0)) {
+ fprintf (stderr, "\n%d object created, exist_active_obj_num %d expected size %d MB\n", i, exist_active_obj_num, fs_size_MB);
+ }
+
+ /* decide on the exact active obj or populated obj */
+ if (decide(ACTIVE_RATIO)) {
+ sfh = select_obj (&obj_fh, DONT_CARE, NON_EXIST, ACTIVE, 0, obj_fh.fh_max);
+ if (sfh == -1)
+ break;
+
+ obj_fh.fh[sfh].exist_flag = EXIST;
+ exist_active_obj_num ++;
+ ftype_flag = obj_fh.fh[sfh].ftype;
+ size = obj_fh.fh[sfh].size;
+ } else {
+ psfh = select_obj (&obj_fh, IS_DIR, EXIST, DONT_CARE, 0, obj_fh.fh_max);
+ strcpy (name, obj_fh.fh[psfh].path);
+ sfh = obj_fh.fh_max;
+ sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max);
+
+ /* decide next obj is file or directory */
+ if (decide(FILE_RATIO)) {
+ ftype_flag = IS_FILE;
+ size = get_file_size();
+ } else {
+ ftype_flag = IS_DIR;
+ size = -1;
+ }
+ add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE);
+
+ }
+
+ /* make sure/create the obj pathname on disk */
+ assure_exist (sfh, obj_fh.fh[sfh].path, ftype_flag);
+ /* write file to sizes according certain distribution
+ if (ftype_flag == IS_FILE)
+ append_file (obj_fh.fh[sfh].path, obj_fh.fh[sfh].size);
+ */
+ }
+ fprintf (stderr, "\n%d object created, exist_active_obj_num %d expected size %d MB\n", i, exist_active_obj_num, fs_size_MB);
+
+ end_profile (&create_profile);
+ start_profile (&write_profile);
+ //write_file_range (0, obj_fh.fh_max);
+ for (i=1; i<=STAGE_NUM; i++) {
+ write_file_range (obj_fh.fh_max*(i-1)/STAGE_NUM, obj_fh.fh_max*i/STAGE_NUM);
+ //fprintf(stderr, "getchar\n");
+ //getchar();
+ }
+ end_profile (&write_profile);
+
+ print_profile ("create_profile", &create_profile);
+ print_profile ("write_profile", &write_profile);
+ printf ("exist_active_obj_num %d active_obj_num %d fs_size_MB %d\n",
+ exist_active_obj_num, active_obj_num, fs_size_MB);
+
+ print_fh_map(&obj_fh);
+ print_result();
+ printf ("end of file system initialization\n");
+ system("date");
+}
+
+int write_file_range (int min, int max)
+{
+ int i, j, k, sfh;
+ int write_size, disk_write_size;
+
+ i = 0, j=0, k=0;
+ printf ("start writing files min %d max %d\n", min, max);
+ while (1) {
+ sfh = select_obj (&obj_fh, IS_FILE, EXIST, DONT_CARE, min, max);
+/*
+ if (!decide(obj_fh.fh[sfh].size*100/(MIN_WRITE_SIZE*WRITE_CHUNK_NUM))) {
+ printf("skip writing small file\n");
+ continue;
+ }
+*/
+ if (sfh == -1)
+ break;
+ write_size = get_write_size (obj_fh.fh[sfh].size, obj_fh.fh[sfh].cur_size);
+ obj_fh.fh[sfh].cur_size += write_size;
+ if (obj_fh.fh[sfh].cur_size == obj_fh.fh[sfh].size) {
+ obj_fh.fh[sfh].exist_flag = COMPLETE;
+ };
+
+#define ACCUMULATE_SMALL_WRITE
+// This option improves speed by 12 times.
+#ifdef ACCUMULATE_SMALL_WRITE
+ obj_fh.fh[sfh].accumulated_write_size += write_size;
+ if (obj_fh.fh[sfh].exist_flag == COMPLETE) {
+ disk_write_size = obj_fh.fh[sfh].accumulated_write_size;
+ } else {
+ disk_write_size = (obj_fh.fh[sfh].accumulated_write_size -
+ (obj_fh.fh[sfh].accumulated_write_size%DISK_FRAGMENT_SIZE));
+ };
+ obj_fh.fh[sfh].accumulated_write_size -= disk_write_size;
+#else
+ disk_write_size = write_size;
+#endif
+
+ if (disk_write_size >0) {
+ append_file (sfh, obj_fh.fh[sfh].path, disk_write_size);
+ if ((i%1000)==0) {
+ printf ("%d C ", i);
+ fflush(stdout);
+ k++;
+ if ((k%10)==0)
+ printf("\n");
+ }
+ i++;
+ } else {
+ if ((j%100000)==0) {
+ printf ("%d c ", j);
+ fflush(stdout);
+ k++;
+ if ((k%10)==0)
+ printf("\n");
+ }
+ j++;
+ }
+ }
+}
+
--- /dev/null
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include "rfs_assert.h"
+#include "profile.h"
+#define MKDIR 1
+#define RMDIR 2
+#define CREATE 3
+#define REMOVE 4
+#define WRITE 5
+#define TRUNCATE 6
+
+#define MAX_FILES 100000
+#define MAX_DIRS 100000
+#define FILE_FH_HTABLE_SIZE MAX_FILES
+#define MAX_NAMELEN 256
+#define MAX_PLAY_PATH_SIZE 256
+#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16)
+#define NFS_MAXDATA 32768
+#define TRACE_FH_SIZE 64
+
+#define FH_T_FLAG_FREE 0
+#define FH_T_FLAG_IN_USE 1
+#define IS_FILE 0
+#define IS_DIR 1
+#define NOT_EXIST 0
+#define EXIST 1
+
+typedef struct {
+ char flag;
+ //char trace_fh [TRACE_FH_SIZE+1];
+ char path[MAX_PLAY_PATH_SIZE];
+} fh_t;
+
+typedef struct {
+ char name[32];
+ fh_t * fh;
+ //struct generic_entry * htable;
+ int fh_size;
+ int fh_max;
+ int index;
+ //int htable_size;
+} fh_info_t;
+
+fh_info_t file_fh, dir_fh;
+profile_t read_line_profile, fgets_profile;
+char trace_file[MAX_NAMELEN];
+FILE * profile_fp = NULL;
+char testdir[MAX_NAMELEN];
+
+int age_create_num = 0;
+int age_mkdir_num = 0;
+int assure_create_num = 0;
+int assure_mkdir_num = 0;
+int age_write_num = 0;
+int nonage_write_num = 0;
+int overlap_write_num = 0;
+
+int init_profile_variables()
+{
+ init_profile ("read_line profile", &read_line_profile);
+ init_profile ("fgets profile", &fgets_profile);
+}
+
+int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size)
+{
+ RFS_ASSERT (strlen(name) < sizeof(fh_infop->name));
+ strcpy (fh_infop->name, name);
+ fh_infop->fh_max = 0;
+ fh_infop->index = 0;
+ fh_infop->fh_size = fh_size;
+ //fh_infop->htable_size = htable_size;
+ fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size);
+ RFS_ASSERT (fh_infop->fh);
+ //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size);
+ //RFS_ASSERT (fh_infop->htable);
+ printf("initialize %s size %d bytes\n",
+ //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size);
+ name, sizeof(fh_t)*fh_size);
+}
+
+int init()
+{
+ init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES);
+ init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS);
+}
+
+int add_fh_t (fh_info_t * fh_table, char * path, int exist_flag)
+{
+ int i;
+
+ for (i=0; i<fh_table->fh_size; i++,fh_table->index++) {
+ if (fh_table->index==fh_table->fh_size)
+ fh_table->index = 0;
+ if (fh_table->fh[fh_table->index].flag == FH_T_FLAG_FREE) {
+ fh_table->fh[fh_table->index].flag = FH_T_FLAG_IN_USE;
+ //RFS_ASSERT(strlen(path)<MAX_PLAY_PATH_SIZE);
+ strcpy (fh_table->fh[fh_table->index].path, path);
+ if (fh_table->index > fh_table->fh_max)
+ fh_table->fh_max = fh_table->index;
+ return 0;
+ }
+ }
+ //print_fh_map(fh_table);
+ RFS_ASSERT (0);
+}
+
+int create_mkdir_op (int flag)
+{
+ static int fhno = 0;
+ char name[MAX_NAMELEN];
+ char command[MAX_COMMAND_LEN];
+ int i;
+ int fd;
+ int count = 0;
+ fh_info_t * fh_infop;
+
+ while (count++ < 100) {
+ i = random()%dir_fh.fh_max;
+ if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) {
+ assure_exist(dir_fh.fh[i].path);
+ strcpy (name, dir_fh.fh[i].path);
+ if (flag == IS_FILE) {
+ sprintf (name+strlen(name), "AGEfile%d", fhno++);
+ fd = creat (name, S_IRWXU);
+ age_create_num++;
+ //printf ("create fd %d\n", fd);
+ close(fd);
+ fh_infop = &file_fh;
+ } else {
+ sprintf (name+strlen(name), "AGEdir%d", fhno++);
+ fd = mkdir (name, S_IRWXU);
+ age_mkdir_num++;
+ fh_infop = &dir_fh;
+ }
+ if (fd == -1) {
+ perror("");
+ if (errno == ENOENT) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ } else
+ RFS_ASSERT (0);
+ }
+ add_fh_t (fh_infop, name, EXIST);
+ RFS_ASSERT (fd >=0);
+ return 0;
+ }
+ };
+ return -1;
+}
+
+int remove_op ()
+{
+ int i;
+ int count = 0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = remove (file_fh.fh[i].path);
+ RFS_ASSERT (ret ==0);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int rmdir_op()
+{
+ int i;
+ int count=0;
+ char command[MAX_COMMAND_LEN];
+ int ret;
+
+ while (count++<100) {
+ i = random()%dir_fh.fh_max;
+ if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = rmdir (dir_fh.fh[i].path);
+ if (ret == 0) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY));
+ //strcpy (command, "rm -r %s", dir_fh.fh[i].path);
+ //system (command);
+ }
+ }
+ return -1;
+}
+
+int loop_write (int fd, char * buf, int buflen)
+{
+ int ret;
+ int pos = 0;
+
+ while (1) {
+ ret = write (fd, buf+pos, buflen-pos);
+
+ if (ret == -1) {
+ perror ("loop write");
+ exit (-1);
+ }
+ if (ret == buflen-pos)
+ break;
+ pos += ret;
+ }
+ return 0;
+}
+
+int assure_exist(char * path)
+{
+ char name[MAX_NAMELEN];
+ int ret;
+ char *p, *q;
+ int non_exist_flag = 0;
+ int count=0;
+ struct stat st;
+
+ ret = stat (path, &st);
+ if (ret == 0)
+ return 0;
+ RFS_ASSERT (errno == ENOENT);
+
+ RFS_ASSERT (!strstr (path, "AGE"));
+ p = path;
+ q = name;
+ while (count++<100) {
+ for (; *p!=0 && *p!='/'; p++, q++ )
+ *q = *p;
+ *q = 0;
+ ret = stat (name, &st);
+ if (ret == -1) {
+ RFS_ASSERT (errno == ENOENT)
+ if ((*p)==0) {
+ ret = creat (name, S_IRWXU);
+ assure_create_num ++;
+ RFS_ASSERT (ret >=0);
+ close(ret);
+ } else {
+ ret = mkdir (name, S_IRWXU);
+ assure_mkdir_num ++;
+ RFS_ASSERT (ret >=0);
+ }
+ }
+ if ((*p)=='/') {
+ *q = '/';
+ p++; q++;
+ } else {
+ RFS_ASSERT ((*p)==0)
+ return 0;
+ }
+ }
+ RFS_ASSERT (0);
+}
+
+int write_op (int off, int size)
+{
+ static char buf[NFS_MAXDATA];
+ int i;
+ int count=0;
+ int fd;
+ int ret;
+ struct stat st;
+
+ RFS_ASSERT (size <= NFS_MAXDATA);
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+ assure_exist(file_fh.fh[i].path);
+ fd = open(file_fh.fh[i].path, O_WRONLY);
+ if (fd == -1)
+ perror("");
+ //else
+ //printf ("write fd %d\n", fd);
+ RFS_ASSERT (fd!=-1);
+ fstat (fd, &st);
+ if (st.st_size < (off+size)) {
+ int written_bytes = 0;
+ while (written_bytes+NFS_MAXDATA < off+size-st.st_size) {
+ loop_write (fd, buf, NFS_MAXDATA);
+ written_bytes += NFS_MAXDATA;
+ }
+ loop_write (fd, buf, off+size-st.st_size-written_bytes);
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ overlap_write_num++;
+/*
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num++;
+ } else
+ nonage_write_num++;
+ loop_write (fd, buf, size);
+*/
+ close(fd);
+ return 0;
+ };
+ }
+ return -1;
+}
+
+int truncate_op(int size)
+{
+ int i;
+ int count=0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist (file_fh.fh[i].path);
+ ret = truncate(file_fh.fh[i].path, size);
+ if (ret ==0)
+ return 0;
+ RFS_ASSERT (errno == ENOENT);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ }
+ };
+ return -1;
+}
+
+int print_fh_map(fh_info_t * fhp)
+{
+ int i;
+ int num = 0;
+ for (i=0; i<fhp->fh_max; i++) {
+ if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) {
+ num ++;
+ printf("%s[%d] %s\n", fhp->name, i, fhp->fh[i].path);
+ }
+ }
+ fprintf(stderr, "fh_max %d total %d entries \n", fhp->fh_max, num);
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+ int lineno = 0;
+ int fh_map_debug =0;
+ char name[MAX_NAMELEN];
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+ RFS_ASSERT (fh_map_debug==0);
+ lineno ++;
+ if (lineno % 10000==0)
+ printf("%d fh_map entry read\n", lineno);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+
+ trace_path = buf + TRACE_FH_SIZE +1;
+
+ strcpy (name, testdir);
+ strcat (name, trace_path);
+ if ((*(buf+strlen(buf)-1))=='/') {
+ *(buf+strlen(buf)-1)=0;
+ add_fh_t (&dir_fh, name, NOT_EXIST);
+ } else {
+ add_fh_t (&file_fh, name, NOT_EXIST);
+ }
+ }
+
+ fclose(fp);
+ if (fh_map_debug) {
+ print_fh_map (&file_fh);
+ print_fh_map (&dir_fh);
+ }
+}
+
+int print_usage()
+{
+ printf("age trace_file fh_path_map testdir\n");
+}
+
+inline char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+
+#define READ_LINE_BUF_SIZE 1000
+#define READ_LINE_LENGTH 32
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ fclose(fp);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+int f()
+{};
+
+int print_result()
+{
+ struct statfs stfs;
+ int ret;
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ fprintf(stderr, "number of files %d size of file system %d\n", stfs.f_files, stfs.f_bfree);
+ fprintf(stderr, "assure_create_num %d assure_mkdir_num %d, age_create_num %d age_mkdir_num %d age_write_num %d nonage_write_num %d overlap_write_num %d\n",
+ assure_create_num, assure_mkdir_num, age_create_num, age_mkdir_num, age_write_num, nonage_write_num, overlap_write_num);
+ printf("assure_create_num %d assure_mkdir_num %d, age_create_num %d age_mkdir_num %d age_write_num %d nonage_write_num %d overlap_write_num %d\n",
+ assure_create_num, assure_mkdir_num, age_create_num, age_mkdir_num, age_write_num, nonage_write_num, overlap_write_num);
+}
+
+int main(int argc, char ** argv)
+{
+ char * buf;
+ static int disk_index=0;
+ int j, nfs3proc, size, off, count;
+ char procname[16];
+ struct stat st;
+ int ret;
+
+ if (argc!=4) {
+ print_usage();
+ exit(0);
+ }
+
+ init();
+ strcpy (trace_file, argv[1]);
+ strcpy (testdir, argv[3]);
+ ret = stat (testdir, &st);
+ RFS_ASSERT (ret == 0);
+ read_fh_map(argv[2]);
+ init_profile_variables();
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+
+ memset (procname, 0, sizeof(procname));
+ //printf ("line[%d] %s", disk_index, buf);
+ sscanf (&buf[0], "%s", procname);
+ j = 0;
+ while ((*(buf+j)!=' ') && (*(buf+j)!='\n'))
+ j++;
+ j++;
+ if (!strcmp(procname, "write"))
+ sscanf (&buf[j], "off %x count %x", &off, &count);
+
+ if (!strcmp(procname, "setattr"))
+ sscanf (&buf[j], "size %x", &size);
+
+ if (!strcmp(procname, "write"))
+ ret = write_op(off, count);
+ else if (!strcmp(procname, "create"))
+ ret = create_mkdir_op(IS_FILE);
+ else if (!strcmp(procname, "mkdir"))
+ ret = create_mkdir_op(IS_DIR);
+ else if (!strcmp(procname, "remove"))
+ ret = remove_op();
+ else if (!strcmp(procname, "rmdir"))
+ ret = rmdir_op();
+ else if (!strcmp(procname, "setattr"))
+ ret = truncate_op(size);
+ else {
+ printf("disk_index %d procname %s\n", disk_index, procname);
+ RFS_ASSERT (0);
+ }
+ if (ret!=0) {
+ printf("execute disk_line[%d] %s error\n", disk_index, buf);
+ }
+ if ((disk_index%100)==0) {
+ fprintf (stderr, "%d disk trace parsed \n", disk_index);
+ print_result();
+ }
+ }
+ print_result();
+}
--- /dev/null
+/* rfs_age_unit_base.c */
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include "rfs_assert.h"
+#include "profile.h"
+#define MKDIR 1
+#define RMDIR 2
+#define CREATE 3
+#define REMOVE 4
+#define WRITE 5
+#define TRUNCATE 6
+
+#define MAX_FILES 100000
+#define MAX_DIRS 100000
+#define FILE_FH_HTABLE_SIZE MAX_FILES
+#define MAX_NAMELEN 512
+#define MAX_PLAY_PATH_SIZE 1024
+#define MAX_COMMAND_LEN (MAX_PLAY_PATH_SIZE+16)
+#define NFS_MAXDATA 4096
+//#define NFS_MAXDATA 32768
+#define TRACE_FH_SIZE 64
+
+#define FH_T_FLAG_FREE 0
+#define FH_T_FLAG_IN_USE 1
+#define IS_FILE 0
+#define IS_DIR 1
+#define EXIST 0
+#define NON_EXIST 1
+#define COMPLETE 3
+#define ACTIVE 0
+#define INACTIVE 1
+#define DONT_CARE 2
+#define FILE_RATIO 50
+#define DISK_FRAGMENT_SIZE 4096
+//#define FRAGMENT_NUM 5
+//#define MIN_WRITE_SIZE 512
+//#define MIN_WRITE_SIZE 2000000000
+
+static char ftypename[3][32] = {"FILE", "DIR", "FTYPE_DONT_CARE"};
+static char activename[3][32] = {"ACTIVE", "INACTIVE", "ACTIVE_DONT_CARE"};
+static char existname[4][32] = {"EXIST", "NON_EXIST", "EXIST_DONT_CARE", "COMPLETE"};
+
+typedef struct {
+ char flag;
+ char ftype;
+ char exist_flag;
+ int psfh;
+ int size;
+ int cur_size;
+ //char trace_fh [TRACE_FH_SIZE+1];
+ char path[MAX_PLAY_PATH_SIZE];
+} fh_t;
+
+typedef struct {
+ char name[32];
+ fh_t * fh;
+ //struct generic_entry * htable;
+ int fh_size;
+ int fh_max;
+ int active_fh_max;
+ //int index;
+ //int htable_size;
+} fh_info_t;
+
+fh_info_t obj_fh;
+profile_t read_line_profile, fgets_profile;
+char trace_file[MAX_NAMELEN];
+FILE * profile_fp = NULL;
+char testdir[MAX_NAMELEN];
+
+int active_obj_num = 0;
+int exist_active_obj_num = 0;
+static int active_file_num = 0, active_dir_num =0, age_file_num = 0, age_dir_num = 0;
+
+int age_create_num = 0;
+int age_mkdir_num = 0;
+int assure_create_num = 0;
+int assure_mkdir_num = 0;
+int age_write_num = 0;
+int nonage_write_num = 0;
+int overlap_write_num = 0;
+
+int rfs_debug = 0;
+
+
+int ACTIVE_RATIO;
+int FRAGMENT_NUM;
+int MIN_WRITE_SIZE = 512;
+
+int aging_dirs ()
+{
+
+}
+
+
+int init_profile_variables()
+{
+ init_profile ("read_line profile", &read_line_profile);
+ init_profile ("fgets profile", &fgets_profile);
+}
+
+int init_fh_info (char * name, fh_info_t * fh_infop, int fh_size, int htable_size)
+{
+ int i;
+
+ RFS_ASSERT (strlen(name) < sizeof(fh_infop->name));
+ strcpy (fh_infop->name, name);
+ fh_infop->fh_max = 0;
+ //fh_infop->index = 0;
+ fh_infop->fh_size = fh_size;
+ //fh_infop->htable_size = htable_size;
+ fh_infop->fh = (fh_t *)malloc (sizeof(fh_t)*fh_size);
+ RFS_ASSERT (fh_infop->fh);
+ //fh_infop->htable = malloc (sizeof(struct*generic_entry)*htable_size);
+ //RFS_ASSERT (fh_infop->htable);
+ printf("initialize %s size %d bytes\n",
+ //name, sizeof(fh_t)*fh_size + sizeof(struct*generic_entry)*htable_size);
+ name, sizeof(fh_t)*fh_size);
+
+ for (i=0; i<fh_size; i++)
+ fh_infop->fh[i].flag = FH_T_FLAG_FREE;
+}
+
+int init()
+{
+// init_fh_info ("file_fh", &file_fh, MAX_FILES, MAX_FILES);
+// init_fh_info ("dir_fh", &dir_fh, MAX_DIRS, MAX_DIRS);
+ init_fh_info ("obj_fh", &obj_fh, MAX_FILES+MAX_DIRS, MAX_FILES+MAX_DIRS);
+}
+
+int add_fh_t (fh_info_t * fh_table, char * path, int sfh, int psfh, int size, int ftype, int exist_flag, int active_flag)
+{
+ int i;
+
+ RFS_ASSERT (sfh >0);
+
+ if (active_flag == ACTIVE)
+ active_obj_num ++;
+ else
+ RFS_ASSERT (sfh >= fh_table->active_fh_max);
+
+ if (rfs_debug)
+ printf ("add to %s path %s sfh %d size %d %s %s %s\n", fh_table->name, path, sfh, size,
+ ftypename[ftype], existname[exist_flag], activename[active_flag]);
+
+ RFS_ASSERT ( (sfh>=0) && (sfh<fh_table->fh_size) );
+ RFS_ASSERT (fh_table->fh[sfh].flag==FH_T_FLAG_FREE);
+ fh_table->fh[sfh].flag = FH_T_FLAG_IN_USE;
+ if (sfh >= fh_table->fh_max)
+ fh_table->fh_max = sfh+1;
+ strcpy (fh_table->fh[sfh].path, path);
+ fh_table->fh[sfh].psfh = psfh;
+ fh_table->fh[sfh].size = size;
+ fh_table->fh[sfh].cur_size = 0;
+ fh_table->fh[sfh].ftype = ftype;
+ fh_table->fh[sfh].exist_flag = exist_flag;
+ if (active_flag == ACTIVE) {
+ if (ftype == IS_FILE)
+ active_file_num ++;
+ else {
+ RFS_ASSERT (ftype== IS_DIR);
+ active_dir_num ++;
+ }
+ } else {
+ if (ftype == IS_FILE)
+ age_file_num ++;
+ else {
+ RFS_ASSERT (ftype== IS_DIR);
+ age_dir_num ++;
+ }
+ }
+ //print_fh_map(fh_table);
+}
+
+
+int loop_write (int fd, char * buf, int buflen)
+{
+ int ret;
+ int pos = 0;
+
+ while (1) {
+ ret = write (fd, buf+pos, buflen-pos);
+
+ if (ret == -1) {
+ printf ("fd %d\n", fd);
+ perror ("loop write");
+ exit (-1);
+ }
+ if (ret == buflen-pos)
+ break;
+ pos += ret;
+ }
+ return 0;
+}
+
+int assure_exist(int sfh, char * path, int ftype_flag)
+{
+ char name[MAX_NAMELEN];
+ int ret;
+ char *p, *q;
+ int non_exist_flag = 0;
+ int count=0;
+ struct stat st;
+
+ if (rfs_debug)
+ printf("assure_exist %s\n", path);
+
+ ret = stat (path, &st);
+ if (ret == 0)
+ return 0;
+ RFS_ASSERT (errno == ENOENT);
+
+ p = path;
+ q = name;
+ if (*p=='/') {
+ *q='/';
+ p++;
+ q++;
+ }
+ while (count++<100) {
+ /* copy the next component from path to name */
+ for (; *p!=0 && *p!='/'; p++, q++ )
+ *q = *p;
+ *q = 0;
+ ret = stat (name, &st);
+ if (ret == -1) {
+ RFS_ASSERT (errno == ENOENT)
+ if ((*p)==0 && (ftype_flag==IS_FILE)) {
+ ret = creat (name, S_IRWXU);
+ if (ret == -1)
+ perror (name);
+ RFS_ASSERT (ret >=0);
+ assure_create_num ++;
+ if (rfs_debug)
+ printf("sfh %d create %s\n", sfh, name);
+ close(ret);
+ } else {
+ ret = mkdir (name, S_IRWXU);
+ assure_mkdir_num ++;
+ if (rfs_debug) {
+ if (*p==0)
+ printf("sfh %d mkdir %s\n", sfh, name);
+ else
+ printf("sfh %d middle mkdir %s\n", sfh, name);
+ }
+ RFS_ASSERT (ret >=0);
+ }
+ }
+ if ((*p)=='/') {
+ *q = '/';
+ p++; q++;
+ } else {
+ RFS_ASSERT ((*p)==0)
+ return 0;
+ }
+ }
+ RFS_ASSERT (0);
+}
+
+
+int print_fh_map(fh_info_t * fhp)
+{
+ int i;
+ int num = 0;
+ int active_obj_num = 0;
+
+
+ for (i=0; i<fhp->fh_max; i++) {
+ if (fhp->fh[i].flag == FH_T_FLAG_IN_USE) {
+ num ++;
+ if (i < fhp->active_fh_max)
+ active_obj_num++;
+
+ if (rfs_debug)
+ printf("%s[%d] %s %s %s\n", fhp->name, i, fhp->fh[i].path, ftypename[fhp->fh[i].ftype], existname[fhp->fh[i].exist_flag]);
+ }
+ }
+ fprintf(stderr, "fh_max %d active_fh_max %d, in_use_num %d entries active_obj_num %d \n", fhp->fh_max, fhp->active_fh_max, num, active_obj_num);
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+ int lineno = 0;
+ int fh_map_debug =0;
+ char name[MAX_NAMELEN];
+ int sfh;
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+ RFS_ASSERT (fh_map_debug==0);
+ lineno ++;
+ if (rfs_debug)
+ printf ("line %d %s", lineno, buf);
+ if (lineno % 10000==0)
+ printf("%d fh_map entry read\n", lineno);
+
+ sfh = 0;
+ if (!strncmp(buf, "::DIR ", strlen("::DIR "))) {
+ strcpy (name, testdir);
+ if (buf[6]=='/') {
+ sscanf(buf, "::DIR %s %d\n", name+strlen(name), &sfh);
+ add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, NON_EXIST, ACTIVE);
+ } else {
+ RFS_ASSERT (!strncmp(buf,"::DIR Fake 1\n", strlen("::DIR Fake 1\n")));
+ sfh = 1;
+ add_fh_t (&obj_fh, name, sfh, -1, -1, IS_DIR, EXIST, ACTIVE);
+ exist_active_obj_num ++;
+ }
+ } else {
+ char * p;
+ int psfh, sfh, size;
+ char filename[MAX_NAMELEN];
+
+ if (!strncmp(buf, "::TBDIR", strlen("::TBDIR")))
+ continue;
+
+ p = strstr(buf, "parent");
+ RFS_ASSERT (p);
+ sscanf(p, "parent %d\n", &psfh);
+ RFS_ASSERT (obj_fh.fh[psfh].flag == FH_T_FLAG_IN_USE);
+ p = strstr(p, "name");
+ RFS_ASSERT (p);
+ if (!strncmp(p, "name xx", strlen("name xx"))) {
+ sscanf(p, "name xx-%s sfh %d size %x", filename, &sfh, &size);
+ //printf ("name xx-%s sfh %d\n", filename, sfh);
+ } else {
+ sscanf(p, "name \"%s sfh %d size %x", filename, &sfh, &size);
+ //printf ("name %s sfh %d\n", filename, sfh);
+ filename[strlen(filename)-1]=0;
+ }
+ strcpy (name, obj_fh.fh[psfh].path);
+ strcat (name, "/");
+ strcat (name, filename);
+ add_fh_t (&obj_fh, name, sfh, psfh, size, IS_FILE, NON_EXIST, ACTIVE);
+ }
+ }
+
+ fclose(fp);
+ obj_fh.active_fh_max = obj_fh.fh_max;
+ if (fh_map_debug) {
+ print_fh_map (&obj_fh);
+ }
+}
+
+int print_usage()
+{
+ printf("agefs ACTIVE_RATIO FRAGMENT_NUM fh_path_map testdir\n");
+ printf("Note: if populate_scale is 4, the total active file size is 1GB\n");
+ printf(" then the total initial file system size is about 4GB\n");
+}
+
+inline char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+
+#define READ_LINE_BUF_SIZE 1000
+#define READ_LINE_LENGTH 32
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ fclose(fp);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+int f()
+{};
+
+int print_result()
+{
+ struct statfs stfs;
+ int ret;
+ static struct statfs first_stfs;
+ static int first_entry = 1;
+
+ ret = statfs (testdir, &stfs);
+ RFS_ASSERT (ret == 0);
+ if (first_entry) {
+ first_entry = 0;
+ first_stfs = stfs;
+ }
+
+ fprintf(stderr, "active_file_num %d active_dir_num %d age_file_num %d age_dir_num %d\n",
+ active_file_num, active_dir_num, age_file_num, age_dir_num);
+ fprintf(stderr, "number of used file nodes %d, used (4K) blocks in fs %d (%d MB)\n", first_stfs.f_ffree-stfs.f_ffree, first_stfs.f_bfree - stfs.f_bfree, (first_stfs.f_bfree-stfs.f_bfree)/(1000000/4096));
+ fprintf(stderr, "assure_create_num %d assure_mkdir_num %d\n", assure_create_num, assure_mkdir_num);
+}
+
+typedef struct {
+ int pcnt; /* percentile */
+ int size; /* file size in KB */
+} sfs_io_file_size_dist;
+
+sfs_io_file_size_dist Default_file_size_dist[] = {
+ /* percentage KB size */
+ { 94, 64}, /* 4% */
+ { 97, 128}, /* 3% */
+#ifdef notdef
+ { 33, 1}, /* 33% */
+ { 54, 2}, /* 21% */
+ { 67, 4}, /* 13% */
+ { 77, 8}, /* 10% */
+ { 85, 16}, /* 8% */
+ { 90, 32}, /* 5% */
+ { 94, 64}, /* 4% */
+ { 97, 128}, /* 3% */
+#endif
+ { 99, 256}, /* 2% */
+ { 100, 1024}, /* 1% */
+ { 0, 0}
+};
+
+/*
+ * For a value between 0-99, return a size based on distribution
+ */
+static int
+get_file_size()
+{
+ static file_array_initialized = 0;
+ static int file_size_array[100];
+ int i;
+
+ i = random() % 100;
+
+ if (i < 0 || i > 99)
+ return (0);
+
+ if (file_array_initialized == 0) {
+ int j, k;
+ for (j = 0, k = 0; j < 100; j++) {
+ if (j >= Default_file_size_dist[k].pcnt &&
+ Default_file_size_dist[k + 1].size != 0)
+ k++;
+ file_size_array[j] = Default_file_size_dist[k].size * 1024;
+ }
+ file_array_initialized++;
+ }
+ return (file_size_array[i]);
+}
+
+int range_random(int min, int max)
+{
+ int i;
+ i = random()%(max-min) + min;
+ return i;
+}
+
+/* answer 1 with a probability of percent/100 */
+int decide(int percent)
+{
+ int i = random()%100;
+ if (i<percent)
+ return 1;
+ else
+ return 0;
+}
+
+int select_obj (fh_info_t * fhp, int ftype, int exist_flag, int active_flag)
+{
+ int i;
+ int min, max;
+ int sfh, count = 0;
+
+ //printf ("select_obj %s %s %s\n", ftypename[ftype], existname[exist_flag], activename[active_flag]);
+ if (active_flag == ACTIVE) {
+ sfh = range_random (0, fhp->active_fh_max);
+ for (i=0; i<fhp->active_fh_max; i++) {
+ if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) &&
+ ((ftype==DONT_CARE) || (ftype ==fhp->fh[sfh].ftype)) &&
+ ((exist_flag==DONT_CARE) || (fhp->fh[sfh].exist_flag == exist_flag)))
+ return sfh;
+ sfh = (sfh+1) % fhp->active_fh_max;
+ }
+ } else {
+ RFS_ASSERT (active_flag == DONT_CARE);
+ RFS_ASSERT (exist_flag == EXIST);
+ sfh = range_random (0, fhp->fh_max);
+ for (i=0; i<fhp->fh_max; i++) {
+ if ((fhp->fh[sfh].flag == FH_T_FLAG_IN_USE) &&
+ ((ftype==DONT_CARE) || (fhp->fh[sfh].ftype == ftype)) &&
+ (fhp->fh[sfh].exist_flag == EXIST)) {
+ return sfh;
+ }
+ sfh = (sfh+1) % fhp->fh_max;
+ }
+ }
+ return -1;
+
+ print_fh_map(&obj_fh);
+ printf ("active_obj_num %d exist_active_obj_num %d \n", active_obj_num, exist_active_obj_num);
+ printf ("failed select_obj %s %s\n", ftypename[ftype], activename[active_flag]);
+ RFS_ASSERT (0);
+}
+
+/* append "size" to file "path" */
+int append_file (int sfh, char * path, int size)
+{
+ int fd;
+ int written_bytes = 0;
+ static char buf[NFS_MAXDATA];
+
+ if (rfs_debug)
+ printf ("sfh %d append_file %s size %d\n", sfh, path, size);
+
+ fd = open (path, O_WRONLY|O_APPEND);
+ if (fd==-1)
+ perror(path);
+ RFS_ASSERT (fd > 0);
+
+ while (written_bytes+NFS_MAXDATA < size) {
+ loop_write (fd, buf, NFS_MAXDATA);
+ written_bytes += NFS_MAXDATA;
+ }
+ loop_write (fd, buf, size-written_bytes);
+ close(fd);
+}
+
+int get_write_size (int target_size, int cur_size)
+{
+ int i;
+ if (target_size - cur_size < MIN_WRITE_SIZE)
+ return (target_size - cur_size);
+
+ /* target_size/FRAGMENT_NUM would be the average value of i */
+ if (target_size < FRAGMENT_NUM) {
+ i = MIN_WRITE_SIZE;
+ } else {
+ i = random() % (2*(target_size/FRAGMENT_NUM));
+ if (i < MIN_WRITE_SIZE)
+ i = MIN_WRITE_SIZE;
+ }
+ if (i > (target_size - cur_size))
+ i = target_size - cur_size;
+
+ return i;
+}
+
+int main(int argc, char ** argv)
+{
+ char * buf;
+ static int disk_index=0;
+ int j, nfs3proc, size, off, count;
+ char procname[16];
+ struct stat st;
+ int ret;
+ int i;
+ int ftype_flag = 0, active_flag = 0;
+ char name[MAX_PLAY_PATH_SIZE];
+ int sfh, psfh;
+
+ if (argc!=5) {
+ print_usage();
+ exit(0);
+ }
+
+ init();
+ ACTIVE_RATIO = atoi(argv[1]);
+ FRAGMENT_NUM = atoi(argv[2]);
+ if (FRAGMENT_NUM==0)
+ MIN_WRITE_SIZE = 2000000000;
+ else
+ MIN_WRITE_SIZE = DISK_FRAGMENT_SIZE;
+
+ strcpy (testdir, argv[4]);
+ ret = stat (testdir, &st);
+ if ((ret == -1) && (errno==ENOENT)) {
+ ret = mkdir (testdir, S_IRWXU);
+ }
+ RFS_ASSERT (ret >= 0);
+ read_fh_map(argv[3]);
+ print_fh_map(&obj_fh);
+ init_profile_variables();
+
+/* NOTE: should have put file and directories in one table */
+
+ for (i=0; exist_active_obj_num < active_obj_num; i++) {
+
+ if ((i!=0) && ((i%1000)==0)) {
+ fprintf (stderr, "\n%d object created \n", i);
+ print_result();
+ }
+
+ /* decide on the exact active obj or populated obj */
+ if (decide(ACTIVE_RATIO)) {
+ sfh = select_obj (&obj_fh, DONT_CARE, NON_EXIST, ACTIVE);
+ if (sfh == -1)
+ break;
+
+ obj_fh.fh[sfh].exist_flag = EXIST;
+ exist_active_obj_num ++;
+ ftype_flag = obj_fh.fh[sfh].ftype;
+ size = obj_fh.fh[sfh].size;
+
+/*
+ {
+ int tmpfh = sfh;
+ while (obj_fh.fh[--tmpfh].exist_flag == NON_EXIST) {
+ if (strstr(obj_fh.fh[sfh].path, obj_fh.fh[tmpfh].path)) {
+ obj_fh.fh[tmpfh].exist_flag = EXIST;
+ //printf ("set %s to exist due to %s\n", obj_fh.fh[tmpfh].path, obj_fh.fh[sfh].path);
+ exist_active_obj_num ++;
+ }
+ }
+ }
+*/
+ } else {
+ psfh = select_obj (&obj_fh, IS_DIR, EXIST, DONT_CARE);
+ strcpy (name, obj_fh.fh[psfh].path);
+ sfh = obj_fh.fh_max;
+ sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max);
+
+ /* decide next obj is file or directory */
+ if (decide(FILE_RATIO)) {
+ ftype_flag = IS_FILE;
+ size = get_file_size();
+ } else {
+ ftype_flag = IS_DIR;
+ size = -1;
+ }
+ add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE);
+
+ }
+
+ /* make sure/create the obj pathname on disk */
+ assure_exist (sfh, obj_fh.fh[sfh].path, ftype_flag);
+ /* write file to sizes according certain distribution
+ if (ftype_flag == IS_FILE)
+ append_file (obj_fh.fh[sfh].path, obj_fh.fh[sfh].size);
+ */
+
+ }
+
+ i = 0;
+ printf ("start writing files\n");
+ while (1) {
+ int write_size;
+ sfh = select_obj (&obj_fh, IS_FILE, EXIST, DONT_CARE);
+ if (sfh == -1)
+ break;
+ write_size = get_write_size (obj_fh.fh[sfh].size, obj_fh.fh[sfh].cur_size);
+ append_file (sfh, obj_fh.fh[sfh].path, write_size);
+ obj_fh.fh[sfh].cur_size += write_size;
+ if (obj_fh.fh[sfh].cur_size == obj_fh.fh[sfh].size) {
+ obj_fh.fh[sfh].exist_flag = COMPLETE;
+ }
+ if ((i%100)==0)
+ printf ("%d append_file operation performed\n", i);
+ i++;
+ }
+
+ printf ("end of file system object creation\n");
+ print_fh_map(&obj_fh);
+ print_result();
+}
+
+#ifdef notdef
+int create_mkdir_op (int flag)
+{
+ static int fhno = 0;
+ char name[MAX_NAMELEN];
+ char command[MAX_COMMAND_LEN];
+ int i;
+ int fd;
+ int count = 0;
+ fh_info_t * fh_infop;
+
+ while (count++ < 100) {
+ i = random()%dir_fh.fh_max;
+ if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) {
+ assure_exist(dir_fh.fh[i].path);
+ strcpy (name, dir_fh.fh[i].path);
+ if (flag == IS_FILE) {
+ sprintf (name+strlen(name), "AGEfile%d", fhno++);
+ fd = creat (name, S_IRWXU);
+ age_create_num++;
+ //printf ("create fd %d\n", fd);
+ close(fd);
+ fh_infop = &file_fh;
+ } else {
+ sprintf (name+strlen(name), "AGEdir%d", fhno++);
+ fd = mkdir (name, S_IRWXU);
+ age_mkdir_num++;
+ fh_infop = &dir_fh;
+ }
+ if (fd == -1) {
+ perror("");
+ if (errno == ENOENT) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ } else
+ RFS_ASSERT (0);
+ }
+ add_fh_t (fh_infop, name, EXIST);
+ RFS_ASSERT (fd >=0);
+ return 0;
+ }
+ };
+ return -1;
+}
+
+int remove_op ()
+{
+ int i;
+ int count = 0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = remove (file_fh.fh[i].path);
+ RFS_ASSERT (ret ==0);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int rmdir_op()
+{
+ int i;
+ int count=0;
+ char command[MAX_COMMAND_LEN];
+ int ret;
+
+ while (count++<100) {
+ i = random()%dir_fh.fh_max;
+ if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = rmdir (dir_fh.fh[i].path);
+ if (ret == 0) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY));
+ //strcpy (command, "rm -r %s", dir_fh.fh[i].path);
+ //system (command);
+ }
+ }
+ return -1;
+}
+
+int write_op (int off, int size)
+{
+ static char buf[NFS_MAXDATA];
+ int i;
+ int count=0;
+ int fd;
+ int ret;
+ struct stat st;
+
+ RFS_ASSERT (size <= NFS_MAXDATA);
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+ assure_exist(file_fh.fh[i].path);
+ fd = open(file_fh.fh[i].path, O_WRONLY);
+ if (fd == -1)
+ perror("");
+ //else
+ //printf ("write fd %d\n", fd);
+ RFS_ASSERT (fd!=-1);
+ fstat (fd, &st);
+ if (st.st_size < (off+size)) {
+ int written_bytes = 0;
+ while (written_bytes+NFS_MAXDATA < off+size-st.st_size) {
+ loop_write (fd, buf, NFS_MAXDATA);
+ written_bytes += NFS_MAXDATA;
+ }
+ loop_write (fd, buf, off+size-st.st_size-written_bytes);
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ overlap_write_num++;
+/*
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num++;
+ } else
+ nonage_write_num++;
+ loop_write (fd, buf, size);
+*/
+ close(fd);
+ return 0;
+ };
+ }
+ return -1;
+}
+
+int truncate_op(int size)
+{
+ int i;
+ int count=0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist (file_fh.fh[i].path);
+ ret = truncate(file_fh.fh[i].path, size);
+ if (ret ==0)
+ return 0;
+ RFS_ASSERT (errno == ENOENT);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ }
+ };
+ return -1;
+}
+
+int aging_files ()
+{
+ char name[MAX_NAMELEN];
+ int sfh; /* file to be aged */
+ int psfh;
+ int ret;
+ struct stat st;
+ int agefd;
+
+ /* get the sfh and size of the selected file to be aged */
+ sfh = select_obj (&obj_fh, IS_FILE, EXIST, ACTIVE);
+ ret = stat (obj_fh.fh[sfh].path, &st);
+ RFS_ASSERT (ret == 0);
+ ret = truncate(obj_fh.fh[i].path, st.st_size/2);
+ RFS_ASSERT (ret==0);
+
+ psfh = obj_fh.fh[sfh].psfh;
+ strcpy (name, obj_fh.fh[psfh].path);
+ sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max);
+ agefd = creat (name, S_IRWXU);
+ //write (agefs, buf, 0);
+
+ add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE);
+}
+#endif
--- /dev/null
+#ifdef notdef
+int create_mkdir_op (int flag)
+{
+ static int fhno = 0;
+ char name[MAX_NAMELEN];
+ char command[MAX_COMMAND_LEN];
+ int i;
+ int fd;
+ int count = 0;
+ fh_info_t * fh_infop;
+
+ while (count++ < 100) {
+ i = random()%dir_fh.fh_max;
+ if (dir_fh.fh[i].flag==FH_T_FLAG_IN_USE) {
+ assure_exist(dir_fh.fh[i].path);
+ strcpy (name, dir_fh.fh[i].path);
+ if (flag == IS_FILE) {
+ sprintf (name+strlen(name), "AGEfile%d", fhno++);
+ fd = creat (name, S_IRWXU);
+ age_create_num++;
+ //printf ("create fd %d\n", fd);
+ close(fd);
+ fh_infop = &file_fh;
+ } else {
+ sprintf (name+strlen(name), "AGEdir%d", fhno++);
+ fd = mkdir (name, S_IRWXU);
+ age_mkdir_num++;
+ fh_infop = &dir_fh;
+ }
+ if (fd == -1) {
+ perror("");
+ if (errno == ENOENT) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ } else
+ RFS_ASSERT (0);
+ }
+ add_fh_t (fh_infop, name, EXIST);
+ RFS_ASSERT (fd >=0);
+ return 0;
+ }
+ };
+ return -1;
+}
+
+int remove_op ()
+{
+ int i;
+ int count = 0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = remove (file_fh.fh[i].path);
+ RFS_ASSERT (ret ==0);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int rmdir_op()
+{
+ int i;
+ int count=0;
+ char command[MAX_COMMAND_LEN];
+ int ret;
+
+ while (count++<100) {
+ i = random()%dir_fh.fh_max;
+ if ( (dir_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist(file_fh.fh[i].path);
+ ret = rmdir (dir_fh.fh[i].path);
+ if (ret == 0) {
+ dir_fh.fh[i].flag = FH_T_FLAG_FREE;
+ return 0;
+ }
+ RFS_ASSERT ((ret == -1) && (errno == ENOTEMPTY));
+ //strcpy (command, "rm -r %s", dir_fh.fh[i].path);
+ //system (command);
+ }
+ }
+ return -1;
+}
+
+int write_op (int off, int size)
+{
+ static char buf[NFS_MAXDATA];
+ int i;
+ int count=0;
+ int fd;
+ int ret;
+ struct stat st;
+
+ RFS_ASSERT (size <= NFS_MAXDATA);
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+ assure_exist(file_fh.fh[i].path);
+ fd = open(file_fh.fh[i].path, O_WRONLY);
+ if (fd == -1)
+ perror("");
+ //else
+ //printf ("write fd %d\n", fd);
+ RFS_ASSERT (fd!=-1);
+ fstat (fd, &st);
+ if (st.st_size < (off+size)) {
+ int written_bytes = 0;
+ while (written_bytes+NFS_MAXDATA < off+size-st.st_size) {
+ loop_write (fd, buf, NFS_MAXDATA);
+ written_bytes += NFS_MAXDATA;
+ }
+ loop_write (fd, buf, off+size-st.st_size-written_bytes);
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ nonage_write_num+=(written_bytes+NFS_MAXDATA-1)/NFS_MAXDATA;
+ } else
+ overlap_write_num++;
+/*
+ if (strstr(file_fh.fh[i].path, "AGE")) {
+ age_write_num++;
+ } else
+ nonage_write_num++;
+ loop_write (fd, buf, size);
+*/
+ close(fd);
+ return 0;
+ };
+ }
+ return -1;
+}
+
+int truncate_op(int size)
+{
+ int i;
+ int count=0;
+ int ret;
+
+ while (count++<100) {
+ i = random()%file_fh.fh_max;
+ if ( (file_fh.fh[i].flag == FH_T_FLAG_IN_USE) ) {
+/*
+ if (!strstr(file_fh.fh[i].path, "AGE"))
+ continue;
+*/
+ assure_exist (file_fh.fh[i].path);
+ ret = truncate(file_fh.fh[i].path, size);
+ if (ret ==0)
+ return 0;
+ RFS_ASSERT (errno == ENOENT);
+ file_fh.fh[i].flag = FH_T_FLAG_FREE;
+ continue;
+ }
+ };
+ return -1;
+}
+
+int aging_files ()
+{
+ char name[MAX_NAMELEN];
+ int sfh; /* file to be aged */
+ int psfh;
+ int ret;
+ struct stat st;
+ int agefd;
+
+ /* get the sfh and size of the selected file to be aged */
+ sfh = select_obj (&obj_fh, IS_FILE, EXIST, ACTIVE);
+ ret = stat (obj_fh.fh[sfh].path, &st);
+ RFS_ASSERT (ret == 0);
+ ret = truncate(obj_fh.fh[i].path, st.st_size/2);
+ RFS_ASSERT (ret==0);
+
+ psfh = obj_fh.fh[sfh].psfh;
+ strcpy (name, obj_fh.fh[psfh].path);
+ sprintf(name+strlen(name), "/AGE%d", obj_fh.fh_max);
+ agefd = creat (name, S_IRWXU);
+ //write (agefs, buf, 0);
+
+ add_fh_t (&obj_fh, name, sfh, psfh, size, ftype_flag, EXIST, INACTIVE);
+}
+#endif
--- /dev/null
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+dep_tab_t dep_tab[DEP_TAB_SIZE];
+int req_num_with_new_fh = 0;
+int req_num_with_discard_fh = 0;
+int req_num_with_init_fh =0;
+
+int event_order [EVENT_ORDER_SIZE];
+int event_order_index = 0;
+
+memory_trace_ent_t memory_trace[MAX_MEMORY_TRACE_LINES];
+
+/* the offset between the replay time and timestamp in the log */
+struct ladtime current;
+#if 0
+//struct ladtime trace_starttime = {1003636801, 39949, 0};
+struct ladtime trace_starttime = {1003723201, 313373, 0};
+#else
+/* timestamp extracted from the used trace. G. Jason Peng */
+//struct ladtime trace_starttime = {1005620400, 499221, 0};
+/* timestamp extracted nfsdump.gzip.pair Ningning for osdi measurement */
+struct ladtime trace_starttime = {1085067131, 107476, 0};
+#endif
+struct ladtime time_offset;
+
+fh_map_t fh_map [FH_MAP_SIZE];
+struct generic_entry * fh_htable[FH_HTABLE_SIZE];
+int fh_i=0;
+int fh_map_debug = 0;
+struct generic_entry * fh_htable [FH_HTABLE_SIZE];
+
+#ifdef notdef
+/* the array is indexed by sfs operation number */
+rfs_op_type rfs_Ops[TOTAL] = {
+{NFSPROC3_NULL, setbuf_void, setbuf_void, xdr_void, xdr_void},
+{NFSPROC3_GETATTR, setarg_GETATTR3, setbuf_void, xdr_GETATTR3args, xdr_GETATTR3res},
+{NFSPROC3_SETATTR, setarg_SETATTR3, setbuf_void, xdr_SETATTR3args, xdr_SETATTR3res},
+{NFSPROC3_INVALID, setbuf_invalid, setbuf_invalid, xdr_invalid, xdr_invalid},
+{NFSPROC3_LOOKUP, setarg_LOOKUP3, setres_lookup, xdr_LOOKUP3args, xdr_LOOKUP3res},
+{NFSPROC3_READLINK, setarg_READLINK3, setres_readlink, xdr_READLINK3args, xdr_READLINK3res},
+{NFSPROC3_READ, setarg_READ3, setres_read, xdr_READ3args, xdr_READ3res},
+{NFSPROC3_INVALID, setarg_invalid, setbuf_invalid, xdr_invalid, xdr_invalid},
+{NFSPROC3_WRITE, setarg_WRITE3, setbuf_void, xdr_WRITE3args, xdr_WRITE3res},
+{NFSPROC3_CREATE, setarg_CREATE3, setbuf_void, xdr_CREATE3args, xdr_CREATE3res},
+{NFSPROC3_REMOVE, setarg_REMOVE3, setbuf_void, xdr_REMOVE3args, xdr_REMOVE3res},
+{NFSPROC3_RENAME, setarg_RENAME3, setbuf_void, xdr_RENAME3args, xdr_RENAME3res},
+{NFSPROC3_LINK, setarg_LINK3, setbuf_void, xdr_LINK3args, xdr_LINK3res},
+{NFSPROC3_SYMLINK, setarg_SYMLINK3, setbuf_void, xdr_SYMLINK3args, xdr_SYMLINK3res},
+{NFSPROC3_MKDIR, setarg_MKDIR3, setbuf_void, xdr_MKDIR3args, xdr_MKDIR3res},
+{NFSPROC3_RMDIR, setarg_RMDIR3, setbuf_void, xdr_RMDIR3args, xdr_RMDIR3res},
+{NFSPROC3_READDIR, setarg_READDIR3, setres_readdir, xdr_READDIR3args, xdr_READDIR3res},
+{NFSPROC3_FSSTAT, setarg_FSSTAT3, setbuf_void, xdr_FSSTAT3args, xdr_FSSTAT3res},
+{NFSPROC3_ACCESS, setarg_ACCESS3, setbuf_void, xdr_ACCESS3args, xdr_ACCESS3res},
+{NFSPROC3_COMMIT, setarg_COMMIT3, setbuf_void, xdr_COMMIT3args, xdr_COMMIT3res},
+{NFSPROC3_FSINFO, setarg_FSINFO3, setbuf_void, xdr_FSINFO3args, xdr_FSINFO3res},
+{NFSPROC3_MKNOD, setarg_MKNOD3, setbuf_void, xdr_MKNOD3args, xdr_MKNOD3res},
+{NFSPROC3_PATHCONF, setarg_PATHCONF3, setbuf_void, xdr_PATHCONF3args, xdr_PATHCONF3res}
+{NFSPROC3_READDIRPLUS, setarg_READDIRPLUS3, setres_readdirplus, xdr_READDIRPLUS3args, xdr_READDIRPLUS3res}};
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+sfs_op_type nfsv3_Ops[] = {
+
+/* name mix op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 11, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 27, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 18, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 9, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 2, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 7, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 5, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 1, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 9, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+#endif
+
+sfs_op_type *Ops;
+
+int num_out_reqs = 0;
+
+cyclic_index_t dep_tab_index;
+cyclic_index_t dep_window_index;
+cyclic_index_t memory_trace_index;
+int dep_window_max = 0;
+
+/* note that for each dep_tab entry, there is a memory trace line, but
+ * not vise vesa because some memory trace line may not have corresponding
+ * dep_tab entry. According entry TIMESTAMP value, the order is
+ *
+ * memory_trace_tail line < dep_tab_tail entry < dep_window_max entry <
+ * dep_tab_head entry < memory_trace_head entry */
+
+int rfs_debug = 0;
+int per_packet_debug = 0;
+int adjust_play_window_debug = 0;
+int dependency_debug = 0;
+int profile_debug = 0;
+int quiet_flag = 0;
+int stage = FIRST_STAGE;
+int read_data_owe = 0;
+int read_data_total = 0;
+int write_data_owe = 0;
+int write_data_total = 0;
+int read_data_adjust_times = 0;
+int write_data_adjust_times = 0;
+int read_data_owe_GB = 0;
+int write_data_owe_GB = 0;
+int read_data_total_GB = 0;
+int write_data_total_GB = 0;
+
+int failed_create_command_num = 0;
+int failed_other_command_num = 0;
+int skipped_readlink_command_num = 0;
+int skipped_custom_command_num = 0;
+int fh_path_map_err_num = 0;
+int skipped_fsstat_command_num = 0;
+int missing_reply_num = 0;
+int rename_rmdir_noent_reply_num = 0;
+int rmdir_not_empty_reply_num = 0;
+int loose_access_control_reply_num = 0;
+int lookup_err_due_to_rename_num = 0;
+int lookup_err_due_to_parallel_remove_num = 0;
+int lookup_eaccess_enoent_mismatch_num = 0;
+int read_io_err_num = 0;
+int stale_fhandle_err_num = 0;
+int proper_reply_num = 0;
+int run_stage_proper_reply_num = 0;
+int lookup_retry_num = 0;
+int can_not_catch_speed_num = 0;
+int can_not_catch_speed_num_total = 0;
+int poll_timeout_0_num = 0;
+int poll_timeout_pos_num = 0;
+int abnormal_EEXIST_num = 0;
+int abnormal_ENOENT_num = 0;
+
+FILE * profile_fp = 0;
+profile_t total_profile;
+profile_t valid_get_nextop_profile;
+profile_t invalid_get_nextop_profile;
+profile_t valid_poll_and_get_reply_profile;
+profile_t invalid_poll_and_get_reply_profile;
+profile_t execute_next_request_profile;
+profile_t receive_next_reply_profile;
+profile_t decode_reply_profile;
+profile_t check_reply_profile;
+profile_t add_create_object_profile;
+profile_t prepare_argument_profile;
+profile_t biod_clnt_call_profile;
+profile_t check_timeout_profile;
+profile_t adjust_play_window_profile;
+profile_t fgets_profile;
+profile_t read_line_profile;
+profile_t read_trace_profile;
+
+int PLAY_SCALE = 1;
+int skip_sec = 0;
+int trace_timestamp1=0, trace_timestamp2=0;
+
+int disk_io_status = TRACE_BUF_FULL;
+int WARMUP_TIME = 0; /* other values that has been used: 100 */
+int disk_index = -1;
+
+int TRACE_COMMAND_REPLY_FLAG_POS=36;
+int TRACE_VERSION_POS=37;
+int TRACE_MSGID_POS=39;
+int TRACE_FH_SIZE=64;
--- /dev/null
+#ifndef RFS_H
+#define RFS_H
+#include "sfs_c_def.h"
+#include "profile.h"
+#include "rfs_assert.h"
+
+/* the maximum number of operations depended by one operation */
+/* the dependency include read/write, write/write, all operations with a
+ * one file handle/delete or rename's target if exists, the dependency
+ * does not include the create/all operations with relevant file handle
+ * This dependency is maintained by a flag in file handle mapping table
+ */
+#define MAX_DEP_OPS 10
+//#define DEP_TAB_SIZE 200000 /* the dependency table size */
+/* Right now we don't wrap around with dep_tab, for each request, there
+ * is one entry in dependency table and one entry in lines buffer.
+ */
+#define REDUCE_MEMORY_TRACE_SIZE
+
+#define EVENT_ORDER_SIZE 1000000
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+//#define DEP_TAB_SIZE 1100000 /* the dependency table size */
+#define DEP_TAB_SIZE 100000 /* the dependency table size */
+//#define DEP_TAB_SIZE 10000 /* the dependency table size */
+#define MAX_MEMORY_TRACE_LINES DEP_TAB_SIZE
+//#define MAX_TRACE_LINE_LENGTH 768
+#define MAX_TRACE_LINE_LENGTH 300
+#else
+#define MAX_TRACE_LINE_LENGTH 768
+#define DEP_TAB_SIZE 500000 /* the dependency table size */
+#define MAX_MEMORY_TRACE_LINES DEP_TAB_SIZE*2
+#endif
+
+#define FH_MAP_SIZE 400000
+//#define FH_MAP_SIZE 40000
+#define FH_HTABLE_SIZE FH_MAP_SIZE
+
+/* trace play policy related defines */
+#define SPEED_UP // only one of SPEED_UP and SLOW_DOWN is defined
+//#define SLOW_DOWN
+extern int PLAY_SCALE;
+//#define TIME_PLAY /* play according original timestamp or scaled timestamp together with dependency */
+#define NO_DEPENDENCY_TABLE
+//#define MAX_COMMAND_REPLY_DISTANCE 1000
+#define MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR 1000
+#define MAX_COMMAND_REPLY_DISTANCE 2
+
+#define WORKLOAD_PLAY /* play according dependency and as fast as it can, ignore timestamp */
+
+#ifdef TIME_PLAY
+#define MAX_PLAY_WINDOW 8
+#define MAX_OUTSTANDING_REQ 4 /* too big outstanding window reduces throughput */
+#else
+#define MAX_PLAY_WINDOW 1000
+//#define MAX_PLAY_WINDOW 8
+#define MAX_OUTSTANDING_REQ 8
+//#define MAX_OUTSTANDING_REQ 16
+#endif
+#define UDP /* currently we only support using UDP, support for TCP will be added later */
+
+/* the flag to indicate the current status of a trace request */
+#define DEP_FLAG_FREE 0 /* free entry */
+#define DEP_FLAG_INIT 1 /* initialized */
+#define DEP_FLAG_CANDIDATE 2
+#define DEP_FLAG_WAIT_FHANDLE 3 /* waiting for leading fhandle */
+#define DEP_FLAG_FHANDLE_READY 4
+#define DEP_FLAG_WAIT_DIRECTORY 5
+#define DEP_FLAG_DIRECTORY_READY 6
+#define DEP_FLAG_WAIT_DELETE 7 /* waiting for deleting the object */
+#define DEP_FLAG_SENT 8 /* Request has been sent out, waiting for replies */
+#define DEP_FLAG_DONE 9 /* Reply has been received and processed */
+#define BUSY 1
+#define IDLE 0
+
+#define MAX_ARG_RES_SIZE 512
+#define MAX_BUF1_SIZE 65536 /* for readdirplus, 208 each entry, max 128 entries */
+#define MAX_BUF2_SIZE NFS_MAXPATHLEN
+
+extern int TRACE_COMMAND_REPLY_FLAG_POS;
+extern int TRACE_VERSION_POS;
+extern int TRACE_MSGID_POS;
+extern int TRACE_FH_SIZE;
+
+/* Two setbuf_t procedure exists for each NFS operation, one for setting up NFS
+ * operation argument buffer, another for setting up NFS operation result receiving buffer.
+ *
+ * When setting up argument, the function takes two argument, the first
+ * argument is index to dep_tab, the second argument is the buffer pointer to
+ * be setup. Sometimes, there is no extra argument other than index, for example: read
+ * Sometimes, there is one extra argument, e.g the buffer for lookup name.
+ * Sometimes, there are two extra arguments, e..g rename, symlink.
+ *
+ * void (*setbuf_t)(int index, char * arg, char * arg);
+ *
+ * When setting up receiving result buffer, the function takes one arguement,
+ * the buffer pointer.
+ * void (*setbuf_t)(char * res, char * arg);
+ */
+typedef void (*setbuf_t)();
+
+typedef struct {
+ int nfsv3_proc;
+ setbuf_t setarg;
+ setbuf_t setres;
+ xdrproc_t xdr_arg;
+ xdrproc_t xdr_res;
+} rfs_op_type;
+
+#define MAX_TRACE_FH_SIZE 64
+#define TRACE_FH_FILE_SIZE 48
+#define TRACE_FH_DIR_SIZE 40
+#define MAX_PLAY_PATH_SIZE 256
+
+/* flags in fh_map_t */
+#define FH_MAP_FLAG_FREE 0
+#define FH_MAP_FLAG_DISCARD 1
+#define FH_MAP_FLAG_PARTIAL 2
+#define FH_MAP_FLAG_COMPLETE 3
+typedef struct {
+ int flag;
+ int lock;
+ char trace_fh [MAX_TRACE_FH_SIZE+1];
+ char path[MAX_PLAY_PATH_SIZE];
+ nfs_fh3 play_fh;
+} fh_map_t;
+
+typedef struct {
+ int disk_index;
+ int trace_status;
+ char reply_trace_fh[MAX_TRACE_FH_SIZE+1];
+ char line[MAX_TRACE_LINE_LENGTH];
+} memory_trace_ent_t;
+
+
+typedef struct {
+ int flag;
+ int disk_index;
+ int memory_index;
+ char * line;
+ char * reply_line;
+ fh_map_t * fh;
+ fh_map_t * fh_2;
+ char * trace_fh;
+ char * trace_fh_2;
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ char * reply_trace_fh;
+#endif
+ int proc; /* the prototype independent NFS operation number defined in sfs_c_def.h
+ * e.g. NULLCALL NFS3PROC_READ etc */
+ struct timeval timestamp; /* The timestamp of a request in the trace */
+#ifdef NO_DEPENDENCY_TABLE
+ int dep_ops[MAX_DEP_OPS]; /* Other requests need to be processed prior this request */
+ /* dep_tab[i] == -1 means the dependency has been resolved */
+ int init_dep_num; /* The number of request being depended initially */
+ int cur_dep_num; /* The number of remaining ones which have not been finished */
+ /* at initialization time, init_dep_num == cur_dep_num */
+#endif
+ int biod_req_index; /* Index in to the biod_req array which contains all outstanding requests */
+ struct ladtime start;
+ struct ladtime stop;
+ struct ladtime interval;
+ int skip_sec;
+ int status;
+ int trace_status;
+} dep_tab_t;
+
+typedef struct {
+ char name[32];
+ int head;
+ int tail;
+ int size;
+} cyclic_index_t;
+
+#define CYCLIC_INIT(str,index,SIZE) \
+ {index.head=0; index.tail=0; index.size=SIZE; \
+ RFS_ASSERT(strlen(str)<sizeof(index.name)); \
+ strcpy(index.name, str); }
+
+#define CYCLIC_PRINT(index) \
+ {printf("%s head %d tail %d, size %d\n", index.name, index.head, index.tail, index.size);}
+
+#define CYCLIC_FULL(index) \
+ (((index.head+1)%index.size)==index.tail)
+
+#define CYCLIC_EMPTY(index) \
+ (index.tail==index.head)
+
+#define CYCLIC_NUM(index) \
+ ((index.head + index.size - index.tail) % index.size)
+
+#define CYCLIC_MOVE_HEAD(index) \
+ { \
+ if (CYCLIC_FULL(index)) { \
+ CYCLIC_PRINT(index) \
+ } \
+ RFS_ASSERT (!CYCLIC_FULL(index)); \
+ index.head=(index.head+1)%(index.size); \
+ }
+
+#define CYCLIC_MOVE_TAIL(index) \
+ { \
+ RFS_ASSERT (!CYCLIC_EMPTY(index)) \
+ index.tail=(index.tail+1)%(index.size); \
+ }
+
+/*
+#define CYCLIC_MOVE_TAIL_TO(index,dest) \
+ { \
+ int oldnum = CYCLIC_NUM(index); \
+ if (! ((dest>=0) && (dest<index.size) && (dest!=index.head))) { \
+ CYCLIC_PRINT(index); \
+ printf("dest %d\n", dest); \
+ } \
+ RFS_ASSERT ((dest>=0) && (dest<index.size) && (dest!=index.head)) \
+ index.tail = dest; \
+ if (CYCLIC_NUM(index) > oldnum) { \
+ CYCLIC_PRINT(index); \
+ printf("dest %d old_num %d \n", dest, oldnum); \
+ } \
+ RFS_ASSERT (CYCLIC_NUM(index) <= oldnum); \
+ }
+*/
+
+#define CYCLIC_MINUS(A,B,size) ((A+size-B)%size)
+#define CYCLIC_ADD(A,B,size) ((A+B)%size)
+#define CYCLIC_LESS(index,A,B) \
+(CYCLIC_MINUS(A,index.tail,index.size)<CYCLIC_MINUS(B,index.tail,index.size))
+
+extern struct ladtime current;
+extern struct ladtime trace_starttime;
+extern struct ladtime time_offset;
+
+extern rfs_op_type rfs_Ops[];
+extern sfs_op_type nfsv3_Ops[];
+extern sfs_op_type * Ops;
+extern int num_out_reqs;
+
+extern cyclic_index_t dep_tab_index;
+extern cyclic_index_t dep_window_index;
+extern cyclic_index_t memory_trace_index;
+int dep_window_max; /* the first entry outside of the dependency window */
+/* If ordered by TIMESTAMP,
+ * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <=
+ * dep_tab_index.head <= memory_trace_index.head
+ */
+
+extern fh_map_t fh_map [FH_MAP_SIZE];
+extern int fh_i;
+extern int fh_map_debug ;
+extern struct generic_entry * fh_htable [FH_HTABLE_SIZE];
+
+extern dep_tab_t dep_tab[DEP_TAB_SIZE];
+extern int req_num_with_new_fh;
+extern int req_num_with_discard_fh;
+extern int req_num_with_init_fh;
+extern memory_trace_ent_t memory_trace[MAX_MEMORY_TRACE_LINES];
+extern int event_order[];
+extern int event_order_index;
+extern struct biod_req * biod_reqp;
+extern int max_biod_reqs;
+extern int rfs_debug;
+extern int per_packet_debug;
+extern int profile_debug;
+extern int adjust_play_window_debug;
+extern int dependency_debug;
+extern int quiet_flag;
+extern int read_data_owe;
+extern int write_data_owe;
+extern int read_data_total;
+extern int write_data_total;
+extern int read_data_adjust_times;
+extern int write_data_adjust_times;
+extern int read_data_owe_GB;
+extern int write_data_owe_GB;
+extern int read_data_total_GB;
+extern int write_data_total_GB;
+
+extern int failed_create_command_num;
+extern int failed_other_command_num;
+extern int skipped_readlink_command_num;
+extern int skipped_custom_command_num;
+extern int fh_path_map_err_num;
+extern int skipped_fsstat_command_num;
+extern int missing_reply_num;
+extern int lookup_noent_reply_num;
+extern int rename_rmdir_noent_reply_num;
+extern int rmdir_not_empty_reply_num;
+extern int loose_access_control_reply_num;
+extern int lookup_err_due_to_rename_num;
+extern int lookup_err_due_to_parallel_remove_num;
+extern int lookup_eaccess_enoent_mismatch_num;
+extern int read_io_err_num;
+extern int stale_fhandle_err_num;
+extern int proper_reply_num;
+extern int run_stage_proper_reply_num;
+extern int lookup_retry_num;
+extern int can_not_catch_speed_num_total;
+extern int can_not_catch_speed_num;
+extern int poll_timeout_0_num;
+extern int poll_timeout_pos_num;
+extern int abnormal_EEXIST_num;
+extern int abnormal_ENOENT_num;
+
+#define FIRST_STAGE 0
+#define READ_DEP_TAB_STAGE 1
+#define TRACE_PLAY_STAGE 2
+extern int stage;
+
+#define IGNORE_SETATTR_CTIME
+//#define TAKE_CARE_SETATTR_GID
+//#define TAKE_CARE_SETATTR_UID
+//#define TAKE_CARE_CREATE_MODE_BY_DAN
+//#define TAKE_CARE_OTHER_FAILED_COMMAND
+//#define TAKE_CARE_CUSTOM_COMMAND
+//#define TAKE_CARE_FSSTAT_COMMAND
+//#define TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+//#define TAKE_CARE_NOEMPTY_RMDIR
+//#define TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH
+//#define TAKE_CARE_ACCESS_ERROR
+//#define TAKE_CARE_SYMBOLIC_LINK
+#define TOLERANT_READ_IO_ERR
+#define TOLERANT_STALE_FHANDLE_ERR
+#define IO_THREAD
+//#define RECV_THREAD can not tune up the version with receive thread quickly
+
+extern FILE * profile_fp;
+extern profile_t total_profile;
+extern profile_t valid_get_nextop_profile;
+extern profile_t invalid_get_nextop_profile;
+extern profile_t valid_poll_and_get_reply_profile;
+extern profile_t invalid_poll_and_get_reply_profile;
+extern profile_t execute_next_request_profile;
+extern profile_t receive_next_reply_profile;
+extern profile_t decode_reply_profile;
+extern profile_t check_reply_profile;
+extern profile_t add_create_object_profile;
+extern profile_t prepare_argument_profile;
+extern profile_t biod_clnt_call_profile;
+extern profile_t check_timeout_profile;
+extern profile_t adjust_play_window_profile;
+extern profile_t fgets_profile;
+extern profile_t read_line_profile;
+extern profile_t read_trace_profile;
+
+extern int skip_sec;
+extern int trace_timestamp1, trace_timestamp2;
+//#define SEQUEN_READ
+//#define SEQUEN_READ_NUM 1000
+
+#define TRACE_BUF_FULL 0
+#define TRACE_FILE_END 1
+#define ASYNC_RPC_SEM_KEY 60000
+extern int disk_io_status;
+extern int WARMUP_TIME;
+extern int disk_index;
+//#define NO_SEM
+#endif
--- /dev/null
+/*
+ * @(#)auth.h 2.1 97/10/23
+ */
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/* @(#)auth.h 2.3 88/08/07 4.0 RPCSRC; from 1.17 88/02/08 SMI */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * auth.h, Authentication interface.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * The data structures are completely opaque to the client. The client
+ * is required to pass a AUTH * to routines that create rpc
+ * "sessions".
+ */
+
+
+#define MAX_AUTH_BYTES 400
+#define MAXNETNAMELEN 255 /* maximum length of network user's name */
+
+/*
+ * Status returned from authentication check
+ */
+enum auth_stat {
+ AUTH_OK=0,
+ /*
+ * failed at remote end
+ */
+ AUTH_BADCRED=1, /* bogus credentials (seal broken) */
+ AUTH_REJECTEDCRED=2, /* client should begin new session */
+ AUTH_BADVERF=3, /* bogus verifier (seal broken) */
+ AUTH_REJECTEDVERF=4, /* verifier expired or was replayed */
+ AUTH_TOOWEAK=5, /* rejected due to security reasons */
+ /*
+ * failed locally
+ */
+ AUTH_INVALIDRESP=6, /* bogus response verifier */
+ AUTH_FAILED=7 /* some unknown reason */
+};
+
+union des_block {
+ struct {
+ uint_t high;
+ uint_t low;
+ } key;
+ char c[8];
+};
+typedef union des_block des_block;
+extern bool_t xdr_des_block();
+
+/*
+ * Authentication info. Opaque to client.
+ */
+struct opaque_auth {
+ enum_t oa_flavor; /* flavor of auth */
+ void *oa_base; /* address of more auth stuff */
+ uint_t oa_length; /* not to exceed MAX_AUTH_BYTES */
+};
+
+
+/*
+ * Auth handle, interface to client side authenticators.
+ */
+typedef struct {
+ struct opaque_auth ah_cred;
+ struct opaque_auth ah_verf;
+ union des_block ah_key;
+ struct auth_ops {
+ void (*ah_nextverf)();
+ int (*ah_marshal)(); /* nextverf & serialize */
+ int (*ah_validate)(); /* validate varifier */
+ int (*ah_refresh)(); /* refresh credentials */
+ void (*ah_destroy)(); /* destroy this structure */
+ } *ah_ops;
+ void * ah_private;
+} AUTH;
+
+
+/*
+ * Authentication ops.
+ * The ops and the auth handle provide the interface to the authenticators.
+ *
+ * AUTH *auth;
+ * XDR *xdrs;
+ * struct opaque_auth verf;
+ */
+#define AUTH_NEXTVERF(auth) \
+ ((*((auth)->ah_ops->ah_nextverf))(auth))
+#define auth_nextverf(auth) \
+ ((*((auth)->ah_ops->ah_nextverf))(auth))
+
+#define AUTH_MARSHALL(auth, xdrs) \
+ ((*((auth)->ah_ops->ah_marshal))(auth, xdrs))
+#define auth_marshall(auth, xdrs) \
+ ((*((auth)->ah_ops->ah_marshal))(auth, xdrs))
+
+#define AUTH_VALIDATE(auth, verfp) \
+ ((*((auth)->ah_ops->ah_validate))((auth), verfp))
+#define auth_validate(auth, verfp) \
+ ((*((auth)->ah_ops->ah_validate))((auth), verfp))
+
+#define AUTH_REFRESH(auth) \
+ ((*((auth)->ah_ops->ah_refresh))(auth))
+#define auth_refresh(auth) \
+ ((*((auth)->ah_ops->ah_refresh))(auth))
+
+#define AUTH_DESTROY(auth) \
+ ((*((auth)->ah_ops->ah_destroy))(auth))
+#define auth_destroy(auth) \
+ ((*((auth)->ah_ops->ah_destroy))(auth))
+
+
+extern struct opaque_auth _null_auth;
+
+
+/*
+ * These are the various implementations of client side authenticators.
+ */
+
+/*
+ * Unix style authentication
+ * AUTH *authunix_create(machname, uid, gid, len, aup_gids)
+ * char *machname;
+ * int uid;
+ * int gid;
+ * int len;
+ * int *aup_gids;
+ */
+extern AUTH *authunix_create(char *, uid_t, gid_t, int, gid_t *);
+extern AUTH *authunix_create_default(void);
+extern AUTH *authnone_create(); /* takes no parameters */
+extern AUTH *authdes_create();
+
+#define AUTH_NONE 0 /* no authentication */
+#define AUTH_NULL 0 /* backward compatibility */
+#define AUTH_UNIX 1 /* unix style (uid, gids) */
+#define AUTH_SHORT 2 /* short hand unix style */
+#define AUTH_DES 3 /* des style (encrypted timestamps) */
--- /dev/null
+#ifndef lint
+static char sfs_auth_none_c_id[] = "@(#)auth_none.c 2.1 97/10/23";
+#endif
+/* @(#)auth_none.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)auth_none.c 1.19 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * auth_none.c
+ * Creates a client authentication handle for passing "null"
+ * credentials and verifiers to remote systems.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdlib.h>
+#include "rpc/rpc.h"
+#define MAX_MARSHEL_SIZE 20
+
+/*
+ * Authenticator operations routines
+ */
+static void authnone_verf(void);
+static void authnone_destroy(void);
+static bool_t authnone_marshal(AUTH *, XDR *);
+static bool_t authnone_validate(void);
+static bool_t authnone_refresh(void);
+
+static struct auth_ops ops = {
+ authnone_verf,
+ authnone_marshal,
+ authnone_validate,
+ authnone_refresh,
+ authnone_destroy
+};
+
+static struct authnone_private {
+ AUTH no_client;
+ char marshalled_client[MAX_MARSHEL_SIZE];
+ uint_t mcnt;
+} *authnone_private;
+
+AUTH *
+authnone_create(void)
+{
+ register struct authnone_private *ap = authnone_private;
+ XDR xdr_stream;
+ register XDR *xdrs;
+
+ if (ap == 0) {
+ ap = (struct authnone_private *)calloc(1,
+ sizeof (struct authnone_private));
+ if (ap == 0)
+ return (0);
+ authnone_private = ap;
+ }
+ if (!ap->mcnt) {
+ ap->no_client.ah_cred = ap->no_client.ah_verf = _null_auth;
+ ap->no_client.ah_ops = &ops;
+ xdrs = &xdr_stream;
+ xdrmem_create(xdrs, ap->marshalled_client, (uint_t)MAX_MARSHEL_SIZE,
+ XDR_ENCODE);
+ (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_cred);
+ (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_verf);
+ ap->mcnt = XDR_GETPOS(xdrs);
+ XDR_DESTROY(xdrs);
+ }
+ return (&ap->no_client);
+}
+
+/*ARGSUSED*/
+static bool_t
+authnone_marshal(
+ AUTH *client,
+ XDR *xdrs)
+{
+ register struct authnone_private *ap = authnone_private;
+
+ if (ap == 0)
+ return (0);
+ return ((*xdrs->x_ops->x_putbytes)(xdrs,
+ ap->marshalled_client, ap->mcnt));
+}
+
+static void
+authnone_verf(void)
+{
+}
+
+static bool_t
+authnone_validate(void)
+{
+
+ return (TRUE);
+}
+
+static bool_t
+authnone_refresh(void)
+{
+
+ return (FALSE);
+}
+
+static void
+authnone_destroy(void)
+{
+}
--- /dev/null
+#ifndef lint
+static char sfs_auth_unix_c_id[] = "@(#)auth_unix.c 2.1 97/10/23";
+#endif
+/* @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * auth_unix.c, Implements UNIX style authentication parameters.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * The system is very weak. The client uses no encryption for it's
+ * credentials and only sends null verifiers. The server sends backs
+ * null verifiers or optionally a verifier that suggests a new short hand
+ * for the credentials.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <netdb.h>
+
+#include "rpc/rpc.h"
+
+extern getmyhostname(char *, int);
+
+/*
+ * Unix authenticator operations vector
+ */
+static void authunix_nextverf(AUTH *);
+static bool_t authunix_marshal(AUTH *, XDR *);
+static bool_t authunix_validate(AUTH *, struct opaque_auth);
+static bool_t authunix_refresh(AUTH *);
+static void authunix_destroy(AUTH *);
+
+static struct auth_ops auth_unix_ops = {
+ authunix_nextverf,
+ authunix_marshal,
+ authunix_validate,
+ authunix_refresh,
+ authunix_destroy
+};
+
+/*
+ * This struct is pointed to by the ah_private field of an auth_handle.
+ */
+struct audata {
+ struct opaque_auth au_origcred; /* original credentials */
+ struct opaque_auth au_shcred; /* short hand cred */
+ uint32_t au_shfaults; /* short hand cache faults */
+ char au_marshed[MAX_AUTH_BYTES];
+ uint_t au_mpos; /* xdr pos at end of marshed */
+};
+#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private)
+
+static bool_t marshal_new_auth(AUTH *);
+
+
+/*
+ * Create a unix style authenticator.
+ * Returns an auth handle with the given stuff in it.
+ */
+AUTH *
+authunix_create(
+ char *machname,
+ uid_t uid,
+ gid_t gid,
+ int len,
+ gid_t *aup_gids)
+{
+ struct authunix_parms aup;
+ char mymem[MAX_AUTH_BYTES];
+ struct timeval now;
+ XDR xdrs;
+ register AUTH *auth;
+ register struct audata *au;
+
+ /*
+ * Allocate and set up auth handle
+ */
+ auth = (AUTH *)mem_alloc(sizeof(AUTH));
+#ifndef KERNEL
+ if (auth == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ au = (struct audata *)mem_alloc(sizeof(struct audata));
+#ifndef KERNEL
+ if (au == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ auth->ah_ops = &auth_unix_ops;
+ auth->ah_private = (void *)au;
+ auth->ah_verf = au->au_shcred = _null_auth;
+ au->au_shfaults = 0;
+
+ /*
+ * fill in param struct from the given params
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ aup.aup_time = now.tv_sec;
+ aup.aup_machname = machname;
+ aup.aup_uid = uid;
+ aup.aup_gid = gid;
+ aup.aup_len = (uint_t)len;
+ aup.aup_gids = (int *)aup_gids;
+
+ /*
+ * Serialize the parameters into origcred
+ */
+ xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
+ if (! xdr_authunix_parms(&xdrs, &aup))
+ abort();
+ au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
+ au->au_origcred.oa_flavor = AUTH_UNIX;
+#ifdef KERNEL
+ au->au_origcred.oa_base = mem_alloc((uint_t) len);
+#else
+ if ((au->au_origcred.oa_base = mem_alloc((uint_t) len)) == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ memmove(au->au_origcred.oa_base, mymem, (uint_t)len);
+
+ /*
+ * set auth handle to reflect new cred.
+ */
+ auth->ah_cred = au->au_origcred;
+ marshal_new_auth(auth);
+ return (auth);
+}
+
+/*
+ * Returns an auth handle with parameters determined by doing lots of
+ * syscalls.
+ */
+AUTH *
+authunix_create_default(void)
+{
+ register int len;
+ char machname[MAX_MACHINE_NAME + 1];
+ uid_t uid;
+ gid_t gid;
+ gid_t gids[NGRPS];
+
+ if (getmyhostname(machname, MAX_MACHINE_NAME) == -1)
+ abort();
+ machname[MAX_MACHINE_NAME] = 0;
+ uid = geteuid();
+ gid = getegid();
+ if ((len = getgroups(NGRPS, gids)) < 0)
+ abort();
+ return (authunix_create(machname, uid, gid, len, gids));
+}
+
+/*
+ * authunix operations
+ */
+
+/* ARGSUSED */
+static void
+authunix_nextverf(
+ AUTH *auth)
+{
+ /* no action necessary */
+}
+
+static bool_t
+authunix_marshal(
+ AUTH *auth,
+ XDR *xdrs)
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos));
+}
+
+static bool_t
+authunix_validate(
+ AUTH *auth,
+ struct opaque_auth verf)
+{
+ register struct audata *au;
+ XDR xdrs;
+
+ if (verf.oa_flavor == AUTH_SHORT) {
+ au = AUTH_PRIVATE(auth);
+ xdrmem_create(&xdrs, verf.oa_base, verf.oa_length, XDR_DECODE);
+
+ if (au->au_shcred.oa_base != NULL) {
+ mem_free(au->au_shcred.oa_base,
+ au->au_shcred.oa_length);
+ au->au_shcred.oa_base = NULL;
+ }
+ if (xdr_opaque_auth(&xdrs, &au->au_shcred)) {
+ auth->ah_cred = au->au_shcred;
+ } else {
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_opaque_auth(&xdrs, &au->au_shcred);
+ au->au_shcred.oa_base = NULL;
+ auth->ah_cred = au->au_origcred;
+ }
+ marshal_new_auth(auth);
+ }
+ return (TRUE);
+}
+
+static bool_t
+authunix_refresh(
+ AUTH *auth)
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+ struct authunix_parms aup;
+ struct timeval now;
+ XDR xdrs;
+ register int stat;
+
+ if (auth->ah_cred.oa_base == au->au_origcred.oa_base) {
+ /* there is no hope. Punt */
+ return (FALSE);
+ }
+ au->au_shfaults ++;
+
+ /* first deserialize the creds back into a struct authunix_parms */
+ aup.aup_machname = NULL;
+ aup.aup_gids = (int *)NULL;
+ xdrmem_create(&xdrs, au->au_origcred.oa_base,
+ au->au_origcred.oa_length, XDR_DECODE);
+ stat = xdr_authunix_parms(&xdrs, &aup);
+ if (! stat)
+ goto done;
+
+ /* update the time and serialize in place */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ aup.aup_time = now.tv_sec;
+ xdrs.x_op = XDR_ENCODE;
+ XDR_SETPOS(&xdrs, 0);
+ stat = xdr_authunix_parms(&xdrs, &aup);
+ if (! stat)
+ goto done;
+ auth->ah_cred = au->au_origcred;
+ marshal_new_auth(auth);
+done:
+ /* free the struct authunix_parms created by deserializing */
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_authunix_parms(&xdrs, &aup);
+ XDR_DESTROY(&xdrs);
+ return (stat);
+}
+
+static void
+authunix_destroy(
+ AUTH *auth)
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
+
+ if (au->au_shcred.oa_base != NULL)
+ mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length);
+
+ mem_free(auth->ah_private, sizeof(struct audata));
+
+ if (auth->ah_verf.oa_base != NULL)
+ mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length);
+
+ mem_free((void *)auth, sizeof(AUTH));
+}
+
+/*
+ * Marshals (pre-serializes) an auth struct.
+ * sets private data, au_marshed and au_mpos
+ */
+static bool_t
+marshal_new_auth(
+ AUTH *auth)
+{
+ XDR xdr_stream;
+ register XDR *xdrs = &xdr_stream;
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
+ if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) ||
+ (! xdr_opaque_auth(xdrs, &(auth->ah_verf)))) {
+ perror("auth_none.c - Fatal marshalling problem");
+ } else {
+ au->au_mpos = XDR_GETPOS(xdrs);
+ }
+ XDR_DESTROY(xdrs);
+ return (TRUE);
+}
--- /dev/null
+/*
+ * @(#)auth_unix.h 2.1 97/10/23
+ */
+
+/* @(#)auth_unix.h 2.2 88/07/29 4.0 RPCSRC; from 1.8 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)auth_unix.h 1.5 86/07/16 SMI */
+
+/*
+ * auth_unix.h, Protocol for UNIX style authentication parameters for RPC
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+/*
+ * The system is very weak. The client uses no encryption for it
+ * credentials and only sends null verifiers. The server sends backs
+ * null verifiers or optionally a verifier that suggests a new short hand
+ * for the credentials.
+ */
+
+/* The machine name is part of a credential; it may not exceed 255 bytes */
+#define MAX_MACHINE_NAME 255
+
+/* gids compose part of a credential; there may not be more than 16 of them */
+#define NGRPS 16
+
+/*
+ * Unix style credentials.
+ */
+struct authunix_parms {
+ uint32_t aup_time;
+ char *aup_machname;
+ int aup_uid;
+ int aup_gid;
+ uint_t aup_len;
+ int *aup_gids;
+};
+
+extern bool_t xdr_authunix_parms();
+
+/*
+ * If a response verifier has flavor AUTH_SHORT,
+ * then the body of the response verifier encapsulates the following structure;
+ * again it is serialized in the obvious fashion.
+ */
+struct short_hand_verf {
+ struct opaque_auth new_cred;
+};
--- /dev/null
+#ifndef lint
+static char sfs_authunix_prot_c_id[] = "@(#)authunix_prot.c 2.1 97/10/23";
+#endif
+/* @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * authunix_prot.c
+ * XDR for UNIX style authentication parameters for RPC
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/auth.h"
+#include "rpc/auth_unix.h"
+
+/*
+ * XDR for unix authentication parameters.
+ */
+bool_t
+xdr_authunix_parms(
+ XDR *xdrs,
+ struct authunix_parms *p)
+{
+
+ if (xdr_uint32_t(xdrs, &(p->aup_time))
+ && xdr_string(xdrs, &(p->aup_machname), MAX_MACHINE_NAME)
+ && xdr_int(xdrs, &(p->aup_uid))
+ && xdr_int(xdrs, &(p->aup_gid))
+ && xdr_array(xdrs, (void **)&(p->aup_gids),
+ &(p->aup_len), NGRPS, sizeof(int), xdr_int) ) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
--- /dev/null
+#ifndef lint
+static char sfs_bindresvport_id[] = "@(#)bindresvport.c 2.1 97/10/23";
+#endif
+/* 2.2 88/07/29 4.0 RPCSRC 1.8 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Copyright (c) 1987 by Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "rpc/rpc.h"
+#include <sys/types.h>
+#include "rpc/osdep.h"
+
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport(
+ int sd,
+ struct sockaddr_in *sin)
+{
+ int res;
+ static int16_t port;
+ struct sockaddr_in myaddr;
+ int i;
+
+#define STARTPORT 600
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+ if (sin == (struct sockaddr_in *)0) {
+ sin = &myaddr;
+ memset(sin, '\0', sizeof (struct sockaddr_in));
+ sin->sin_family = AF_INET;
+ } else if (sin->sin_family != AF_INET) {
+ errno = EPFNOSUPPORT;
+ return (-1);
+ }
+ if (port == 0) {
+ port = (getpid() % NPORTS) + STARTPORT;
+ }
+ res = -1;
+ errno = EADDRINUSE;
+ for (i = 0; i < NPORTS && res < 0 && errno == EADDRINUSE; i++) {
+ sin->sin_port = htons(port++);
+ if (port > ENDPORT) {
+ port = STARTPORT;
+ }
+ res = bind(sd, (struct sockaddr *)sin, sizeof(struct sockaddr_in));
+ }
+ return (res);
+}
--- /dev/null
+/*
+ * @(#)clnt.h 2.1 97/10/23
+ */
+
+/* @(#)clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.31 88/02/08 SMI*/
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * clnt.h - Client side remote procedure call interface.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+#ifndef _CLNT_
+#define _CLNT_
+
+/*
+ * Rpc calls return an enum clnt_stat. This should be looked at more,
+ * since each implementation is required to live with this (implementation
+ * independent) list of errors.
+ */
+enum clnt_stat {
+ RPC_SUCCESS=0, /* call succeeded */
+ /*
+ * local errors
+ */
+ RPC_CANTENCODEARGS=1, /* can't encode arguments */
+ RPC_CANTDECODERES=2, /* can't decode results */
+ RPC_CANTSEND=3, /* failure in sending call */
+ RPC_CANTRECV=4, /* failure in receiving result */
+ RPC_TIMEDOUT=5, /* call timed out */
+ /*
+ * remote errors
+ */
+ RPC_VERSMISMATCH=6, /* rpc versions not compatible */
+ RPC_AUTHERROR=7, /* authentication error */
+ RPC_PROGUNAVAIL=8, /* program not available */
+ RPC_PROGVERSMISMATCH=9, /* program version mismatched */
+ RPC_PROCUNAVAIL=10, /* procedure unavailable */
+ RPC_CANTDECODEARGS=11, /* decode arguments error */
+ RPC_SYSTEMERROR=12, /* generic "other problem" */
+
+ /*
+ * callrpc & clnt_create errors
+ */
+ RPC_UNKNOWNHOST=13, /* unknown host name */
+ RPC_UNKNOWNPROTO=17, /* unkown protocol */
+
+ /*
+ * _ create errors
+ */
+ RPC_PMAPFAILURE=14, /* the pmapper failed in its call */
+ RPC_PROGNOTREGISTERED=15, /* remote program is not registered */
+ /*
+ * unspecified error
+ */
+ RPC_FAILED=16
+};
+
+
+/*
+ * Error info.
+ */
+struct rpc_err {
+ enum clnt_stat re_status;
+ union {
+ int RE_errno; /* realated system error */
+ enum auth_stat RE_why; /* why the auth error occurred */
+ struct {
+ uint32_t low; /* lowest verion supported */
+ uint32_t high; /* highest verion supported */
+ } RE_vers;
+ struct { /* maybe meaningful if RPC_FAILED */
+ int32_t s1;
+ int32_t s2;
+ } RE_lb; /* life boot & debugging only */
+ } ru;
+#define re_errno ru.RE_errno
+#define re_why ru.RE_why
+#define re_vers ru.RE_vers
+#define re_lb ru.RE_lb
+};
+
+
+/*
+ * Client rpc handle.
+ * Created by individual implementations, see e.g. rpc_udp.c.
+ * Client is responsible for initializing auth, see e.g. auth_none.c.
+ */
+typedef struct {
+ AUTH *cl_auth; /* authenticator */
+ struct clnt_ops *cl_ops;
+ void *cl_private; /* private stuff */
+} CLIENT;
+
+struct clnt_ops {
+ enum clnt_stat (*cl_call)(CLIENT *, uint32_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
+ void (*cl_abort)(CLIENT *);
+ void (*cl_geterr)(CLIENT *, struct rpc_err *);
+ bool_t (*cl_freeres)(CLIENT *, xdrproc_t, void *);
+ void (*cl_destroy)(CLIENT *);
+ bool_t (*cl_control)(CLIENT *, uint_t, void *);
+ bool_t (*cl_getreply)(CLIENT *, xdrproc_t, void *, int,
+ uint32_t *, uint32_t *, struct timeval *);
+ int (*cl_poll)(CLIENT *, uint32_t);
+};
+
+/*
+ * client side rpc interface ops
+ *
+ * Parameter types are:
+ *
+ */
+
+/*
+ * enum clnt_stat
+ * CLNT_CALL(rh, proc, xargs, argsp, xres, resp, timeout)
+ * CLIENT *rh;
+ * uint32_t proc;
+ * xdrproc_t xargs;
+ * void * argsp;
+ * xdrproc_t xres;
+ * void * resp;
+ * struct timeval timeout;
+ */
+#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \
+ ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs))
+#define clnt_call(rh, proc, xargs, argsp, xres, resp, secs) \
+ ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs))
+
+/*
+ * void
+ * CLNT_ABORT(rh);
+ * CLIENT *rh;
+ */
+#define CLNT_ABORT(rh) ((*(rh)->cl_ops->cl_abort)(rh))
+#define clnt_abort(rh) ((*(rh)->cl_ops->cl_abort)(rh))
+
+/*
+ * struct rpc_err
+ * CLNT_GETERR(rh);
+ * CLIENT *rh;
+ */
+#define CLNT_GETERR(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp))
+#define clnt_geterr(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp))
+
+
+/*
+ * bool_t
+ * CLNT_FREERES(rh, xres, resp);
+ * CLIENT *rh;
+ * xdrproc_t xres;
+ * void * resp;
+ */
+#define CLNT_FREERES(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp))
+#define clnt_freeres(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp))
+
+/*
+ * bool_t
+ * CLNT_CONTROL(cl, request, info)
+ * CLIENT *cl;
+ * uint_t request;
+ * void *info;
+ */
+#define CLNT_CONTROL(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in))
+#define clnt_control(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in))
+
+/*
+ * control operations that apply to both udp and tcp transports
+ */
+#define CLSET_TIMEOUT 1 /* set timeout (timeval) */
+#define CLGET_TIMEOUT 2 /* get timeout (timeval) */
+#define CLGET_SERVER_ADDR 3 /* get server's address (sockaddr) */
+/*
+ * udp only control operations
+ */
+#define CLSET_RETRY_TIMEOUT 4 /* set retry timeout (timeval) */
+#define CLGET_RETRY_TIMEOUT 5 /* get retry timeout (timeval) */
+
+/*
+ * void
+ * CLNT_DESTROY(rh);
+ * CLIENT *rh;
+ */
+#define CLNT_DESTROY(rh) ((*(rh)->cl_ops->cl_destroy)(rh))
+#define clnt_destroy(rh) ((*(rh)->cl_ops->cl_destroy)(rh))
+
+/*
+ * bool_t
+ * CLNT_GETREPLY(rh,xres,xresp,xid,tv)
+ * CLIENT *rh;
+ * xdrproc_t xres;
+ * void * resp;
+ * int cnt;
+ * uint32_t *xids;
+ * uint32_t *xid;
+ * struct timeval *tv;
+ */
+#define CLNT_GETREPLY(rh,xres,xresp,cnt,xids,xid,tv) ((*(rh)->cl_ops->cl_getreply)(rh,xres,xresp,cnt,xids,xid,tv))
+#define clnt_getreply(rh,xres,xresp,cnt,xids,xid,tv) ((*(rh)->cl_ops->cl_getreply)(rh,xres,xresp,cnt,xids,xid,tv))
+
+/*
+ * bool_t
+ * CLNT_POLL(rh,xusec)
+ * CLIENT *rh;
+ * uint32_t xusec;
+ */
+#define CLNT_POLL(rh,xid) ((*(rh)->cl_ops->cl_poll)(rh,xid))
+#define clnt_poll(rh,xid) ((*(rh)->cl_ops->cl_poll)(rh,xid))
+
+/*
+ * RPCTEST is a test program which is accessable on every rpc
+ * transport/port. It is used for testing, performance evaluation,
+ * and network administration.
+ */
+
+#define RPCTEST_PROGRAM ((uint32_t)1)
+#define RPCTEST_VERSION ((uint32_t)1)
+#define RPCTEST_NULL_PROC ((uint32_t)2)
+#define RPCTEST_NULL_BATCH_PROC ((uint32_t)3)
+
+/*
+ * By convention, procedure 0 takes null arguments and returns them
+ */
+
+#define NULLPROC ((uint32_t)0)
+
+/*
+ * Below are the client handle creation routines for the various
+ * implementations of client side rpc. They can return NULL if a
+ * creation failure occurs.
+ */
+
+/*
+ * Memory based rpc (for speed check and testing)
+ * CLIENT *
+ * clntraw_create(prog, vers)
+ * uint32_t prog;
+ * uint32_t vers;
+ */
+extern CLIENT *clntraw_create(uint32_t, uint32_t);
+
+/*
+ * Generic client creation routine. Supported protocols are "udp" and "tcp"
+ */
+extern CLIENT *clnt_create(char *, uint32_t, uint32_t, char *);
+
+
+/*
+ * TCP based rpc
+ * CLIENT *
+ * sfs_ctcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
+ * struct sockaddr_in *raddr;
+ * uint32_t prog;
+ * uint32_t version;
+ * register int *sockp;
+ * uint_t sendsz;
+ * uint_t recvsz;
+ */
+extern CLIENT *sfs_ctcp_create(struct sockaddr_in *, uint32_t, uint32_t,
+ int *, uint_t, uint_t);
+extern CLIENT *clnttcp_create(struct sockaddr_in *, uint32_t, uint32_t,
+ int *, uint_t, uint_t);
+
+/*
+ * UDP based rpc.
+ * CLIENT *
+ * sfs_cudp_create(raddr, program, version, wait, sockp)
+ * struct sockaddr_in *raddr;
+ * uint32_t program;
+ * uint32_t version;
+ * struct timeval wait;
+ * int *sockp;
+ *
+ * Same as above, but you specify max packet sizes.
+ * CLIENT *
+ * sfs_cudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
+ * struct sockaddr_in *raddr;
+ * uint32_t program;
+ * uint32_t version;
+ * struct timeval wait;
+ * int *sockp;
+ * uint_t sendsz;
+ * uint_t recvsz;
+ */
+extern CLIENT *clntudp_create(struct sockaddr_in *, uint32_t, uint32_t,
+ struct timeval, int *);
+extern CLIENT *clntudp_bufcreate(struct sockaddr_in *, uint32_t, uint32_t,
+ struct timeval, int *, uint_t, uint_t);
+extern CLIENT *sfs_cudp_create(struct sockaddr_in *, uint32_t, uint32_t,
+ struct timeval, int *);
+extern CLIENT *sfs_cudp_bufcreate(struct sockaddr_in *, uint32_t, uint32_t,
+ struct timeval, int *, uint_t, uint_t);
+
+/*
+ * Print why creation failed
+ */
+extern void clnt_pcreateerror(char *);
+extern char *clnt_spcreateerror(char *);
+
+/*
+ * Like clnt_perror(), but is more verbose in its output
+ */
+extern void clnt_perrno(enum clnt_stat);
+
+/*
+ * Print an English error message, given the client error code
+ */
+extern void clnt_perror(CLIENT *, char *);
+extern char *clnt_sperror(CLIENT *, char *);
+
+/*
+ * If a creation fails, the following allows the user to figure out why.
+ */
+struct rpc_createerr {
+ enum clnt_stat cf_stat;
+ struct rpc_err cf_error; /* useful when cf_stat == RPC_PMAPFAILURE */
+};
+
+extern struct rpc_createerr rpc_createerr;
+
+/*
+ * Copy error message to buffer.
+ */
+extern char *clnt_sperrno(enum clnt_stat);
+
+extern int callrpc(char *, int, int, int, xdrproc_t, char *, xdrproc_t, char *);
+
+extern int bindresvport(int sd, struct sockaddr_in *sin);
+
+#define UDPMSGSIZE (63 * 1024) /* rpc imposed limit on udp msg size */
+#define RPCSMALLMSGSIZE 400 /* a more reasonable packet size */
+
+#if !defined(RPC_ANYSOCK)
+#define RPC_ANYSOCK -1
+#endif
+
+#endif /*!_CLNT_*/
--- /dev/null
+#ifndef lint
+static char sfs_clnt_generic_id[] = "@(#)clnt_generic.c 2.1 97/10/23";
+#endif
+
+/* @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
+#endif
+/*
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <errno.h>
+#include <netdb.h>
+
+/*
+ * Generic client creation: takes (hostname, program-number, protocol) and
+ * returns client handle. Default options are set, which the user can
+ * change using the rpc equivalent of ioctl()'s.
+ */
+CLIENT *
+clnt_create(char *hostname,
+ uint32_t prog,
+ uint32_t vers,
+ char *proto)
+{
+ struct hostent *h;
+ struct protoent *p;
+ struct sockaddr_in sin;
+ int sock;
+ struct timeval tv;
+ CLIENT *client;
+
+ h = gethostbyname(hostname);
+ if (h == NULL) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
+ return (NULL);
+ }
+ if (h->h_addrtype != AF_INET) {
+ /*
+ * Only support INET for now
+ */
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ sin.sin_family = h->h_addrtype;
+ sin.sin_port = 0;
+ memset(sin.sin_zero, '\0', sizeof(sin.sin_zero));
+ memmove((char*)&sin.sin_addr, h->h_addr, h->h_length);
+ p = getprotobyname(proto);
+ if (p == NULL) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ rpc_createerr.cf_error.re_errno = EPFNOSUPPORT;
+ return (NULL);
+ }
+ sock = RPC_ANYSOCK;
+ switch (p->p_proto) {
+ case IPPROTO_UDP:
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ client = sfs_cudp_create(&sin, prog, vers, tv, &sock);
+ if (client == NULL) {
+ return (NULL);
+ }
+ tv.tv_sec = 25;
+ clnt_control(client, CLSET_TIMEOUT, &tv);
+ break;
+ case IPPROTO_TCP:
+ client = sfs_ctcp_create(&sin, prog, vers, &sock, 0, 0);
+ if (client == NULL) {
+ return (NULL);
+ }
+ tv.tv_sec = 25;
+ tv.tv_usec = 0;
+ clnt_control(client, CLSET_TIMEOUT, &tv);
+ break;
+ default:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = EPFNOSUPPORT;
+ return (NULL);
+ }
+ return (client);
+}
--- /dev/null
+#ifndef lint
+static char sfs_clnt_perror_c_id[] = "@(#)clnt_perror.c 2.1 97/10/23";
+#endif
+/* @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_perror.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rpc/rpc.h"
+
+static char *auth_errmsg(enum auth_stat);
+
+static char *
+_buf()
+{
+ static char *buf = NULL;
+ if (buf == NULL)
+ buf = (char *)malloc(256);
+ return (buf);
+}
+
+/*
+ * Print reply error info
+ */
+char *
+clnt_sperror(
+ CLIENT *rpch,
+ char *s)
+{
+ struct rpc_err e;
+ char *err;
+ char *str = _buf();
+ char *strstart = str;
+
+ if (str == 0)
+ return (0);
+ CLNT_GETERR(rpch, &e);
+
+ (void) sprintf(str, "%s: ", s);
+ str += strlen(str);
+
+ (void) strcpy(str, clnt_sperrno(e.re_status));
+ str += strlen(str);
+
+ switch (e.re_status) {
+ case RPC_SUCCESS:
+ case RPC_CANTENCODEARGS:
+ case RPC_CANTDECODERES:
+ case RPC_TIMEDOUT:
+ case RPC_PROGUNAVAIL:
+ case RPC_PROCUNAVAIL:
+ case RPC_CANTDECODEARGS:
+ case RPC_SYSTEMERROR:
+ case RPC_UNKNOWNHOST:
+ case RPC_UNKNOWNPROTO:
+ case RPC_PMAPFAILURE:
+ case RPC_PROGNOTREGISTERED:
+ case RPC_FAILED:
+ break;
+
+ case RPC_CANTSEND:
+ case RPC_CANTRECV:
+ (void) sprintf(str, "; errno = %s",
+ strerror(e.re_errno));
+ str += strlen(str);
+ break;
+
+ case RPC_VERSMISMATCH:
+ (void) sprintf(str,
+ "; low version = %lu, high version = %lu",
+ e.re_vers.low, e.re_vers.high);
+ str += strlen(str);
+ break;
+
+ case RPC_AUTHERROR:
+ err = auth_errmsg(e.re_why);
+ (void) sprintf(str,"; why = ");
+ str += strlen(str);
+ if (err != NULL) {
+ (void) sprintf(str, "%s",err);
+ } else {
+ (void) sprintf(str,
+ "(unknown authentication error - %d)",
+ (int) e.re_why);
+ }
+ str += strlen(str);
+ break;
+
+ case RPC_PROGVERSMISMATCH:
+ (void) sprintf(str,
+ "; low version = %lu, high version = %lu",
+ e.re_vers.low, e.re_vers.high);
+ str += strlen(str);
+ break;
+
+ default: /* unknown */
+ (void) sprintf(str,
+ "; s1 = %lu, s2 = %lu",
+ e.re_lb.s1, e.re_lb.s2);
+ str += strlen(str);
+ break;
+ }
+ (void) sprintf(str, "\n");
+ return(strstart) ;
+}
+
+void
+clnt_perror(
+ CLIENT *rpch,
+ char *s)
+{
+ (void) fprintf(stderr,"%s",clnt_sperror(rpch,s));
+}
+
+
+struct rpc_errtab {
+ enum clnt_stat status;
+ char *message;
+};
+
+static struct rpc_errtab rpc_errlist[] = {
+ { RPC_SUCCESS,
+ "RPC: Success" },
+ { RPC_CANTENCODEARGS,
+ "RPC: Can't encode arguments" },
+ { RPC_CANTDECODERES,
+ "RPC: Can't decode result" },
+ { RPC_CANTSEND,
+ "RPC: Unable to send" },
+ { RPC_CANTRECV,
+ "RPC: Unable to receive" },
+ { RPC_TIMEDOUT,
+ "RPC: Timed out" },
+ { RPC_VERSMISMATCH,
+ "RPC: Incompatible versions of RPC" },
+ { RPC_AUTHERROR,
+ "RPC: Authentication error" },
+ { RPC_PROGUNAVAIL,
+ "RPC: Program unavailable" },
+ { RPC_PROGVERSMISMATCH,
+ "RPC: Program/version mismatch" },
+ { RPC_PROCUNAVAIL,
+ "RPC: Procedure unavailable" },
+ { RPC_CANTDECODEARGS,
+ "RPC: Server can't decode arguments" },
+ { RPC_SYSTEMERROR,
+ "RPC: Remote system error" },
+ { RPC_UNKNOWNHOST,
+ "RPC: Unknown host" },
+ { RPC_UNKNOWNPROTO,
+ "RPC: Unknown protocol" },
+ { RPC_PMAPFAILURE,
+ "RPC: Port mapper failure" },
+ { RPC_PROGNOTREGISTERED,
+ "RPC: Program not registered"},
+ { RPC_FAILED,
+ "RPC: Failed (unspecified error)"}
+};
+
+
+/*
+ * This interface for use by clntrpc
+ */
+char *
+clnt_sperrno(
+ enum clnt_stat stat)
+{
+ int i;
+
+ for (i = 0; i < sizeof(rpc_errlist)/sizeof(struct rpc_errtab); i++) {
+ if (rpc_errlist[i].status == stat) {
+ return (rpc_errlist[i].message);
+ }
+ }
+ return ("RPC: (unknown error code)");
+}
+
+void
+clnt_perrno(
+ enum clnt_stat num)
+{
+ (void) fprintf(stderr,"%s",clnt_sperrno(num));
+}
+
+
+char *
+clnt_spcreateerror(char *s)
+{
+ char *sp;
+ char *str = _buf();
+
+ if (str == 0)
+ return(0);
+ (void) sprintf(str, "%s: ", s);
+ (void) strcat(str, clnt_sperrno(rpc_createerr.cf_stat));
+ switch (rpc_createerr.cf_stat) {
+ case RPC_PMAPFAILURE:
+ (void) strcat(str, " - ");
+ (void) strcat(str,
+ clnt_sperrno(rpc_createerr.cf_error.re_status));
+ break;
+
+ case RPC_SYSTEMERROR:
+ sp = strerror(rpc_createerr.cf_error.re_errno);
+ (void) strcat(str, " - ");
+ if (rpc_createerr.cf_error.re_errno > 0
+ && sp != NULL)
+ (void) strcat(str, sp);
+ else
+ (void) sprintf(&str[strlen(str)], "Error %d",
+ rpc_createerr.cf_error.re_errno);
+ break;
+ }
+ (void) strcat(str, "\n");
+ return (str);
+}
+
+void
+clnt_pcreateerror(char *s)
+{
+ (void) fprintf(stderr, "%s", clnt_spcreateerror(s));
+}
+
+struct auth_errtab {
+ enum auth_stat status;
+ char *message;
+};
+
+static struct auth_errtab auth_errlist[] = {
+ { AUTH_OK,
+ "Authentication OK" },
+ { AUTH_BADCRED,
+ "Invalid client credential" },
+ { AUTH_REJECTEDCRED,
+ "Server rejected credential" },
+ { AUTH_BADVERF,
+ "Invalid client verifier" },
+ { AUTH_REJECTEDVERF,
+ "Server rejected verifier" },
+ { AUTH_TOOWEAK,
+ "Client credential too weak" },
+ { AUTH_INVALIDRESP,
+ "Invalid server verifier" },
+ { AUTH_FAILED,
+ "Failed (unspecified error)" },
+};
+
+static char *
+auth_errmsg(
+ enum auth_stat stat)
+{
+ int i;
+
+ for (i = 0; i < sizeof(auth_errlist)/sizeof(struct auth_errtab); i++) {
+ if (auth_errlist[i].status == stat) {
+ return(auth_errlist[i].message);
+ }
+ }
+ return(NULL);
+}
--- /dev/null
+#ifndef lint
+static char sfs_clnt_simple_id[] = "@(#)clnt_simple.c 2.1 97/10/23";
+#endif
+/* @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_simple.c
+ * Simplified front end to rpc.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+#include <string.h>
+
+static struct callrpc_private {
+ CLIENT *client;
+ int socket;
+ int oldprognum, oldversnum, valid;
+ char *oldhost;
+} *callrpc_private;
+
+callrpc(
+ char *host,
+ int prognum,
+ int versnum,
+ int procnum,
+ xdrproc_t inproc,
+ char *in,
+ xdrproc_t outproc,
+ char *out)
+{
+ register struct callrpc_private *crp = callrpc_private;
+ struct sockaddr_in server_addr;
+ enum clnt_stat clnt_stat;
+ struct hostent *hp;
+ struct timeval timeout, tottimeout;
+
+ if (crp == 0) {
+ crp = (struct callrpc_private *)calloc(1,
+ sizeof (struct callrpc_private));
+ if (crp == 0)
+ return (0);
+ callrpc_private = crp;
+ }
+ if (crp->oldhost == NULL) {
+ crp->oldhost = malloc(256);
+ crp->oldhost[0] = 0;
+ crp->socket = RPC_ANYSOCK;
+ }
+ if (crp->valid && crp->oldprognum == prognum && crp->oldversnum == versnum
+ && strcmp(crp->oldhost, host) == 0) {
+ /* reuse old client */
+ } else {
+ crp->valid = 0;
+ (void)close(crp->socket);
+ crp->socket = RPC_ANYSOCK;
+ if (crp->client) {
+ clnt_destroy(crp->client);
+ crp->client = NULL;
+ }
+ if ((hp = gethostbyname(host)) == NULL)
+ return ((int) RPC_UNKNOWNHOST);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 5;
+ memmove((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = 0;
+ if ((crp->client = clntudp_create(&server_addr, (uint32_t)prognum,
+ (uint32_t)versnum, timeout, &crp->socket)) == NULL)
+ return ((int) rpc_createerr.cf_stat);
+ crp->valid = 1;
+ crp->oldprognum = prognum;
+ crp->oldversnum = versnum;
+ (void) strcpy(crp->oldhost, host);
+ }
+ tottimeout.tv_sec = 25;
+ tottimeout.tv_usec = 0;
+ clnt_stat = clnt_call(crp->client, procnum, inproc, in,
+ outproc, out, tottimeout);
+ /*
+ * if call failed, empty cache
+ */
+ if (clnt_stat != RPC_SUCCESS)
+ crp->valid = 0;
+ return ((int) clnt_stat);
+}
--- /dev/null
+#ifndef lint
+static char sfs_clnt_tcp_c_id[] = "@(#)clnt_tcp.c 2.1 97/10/23";
+#endif
+/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+#include <errno.h>
+#include "rpc/pmap_clnt.h"
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct sockaddr_in ct_addr;
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ uint_t ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+};
+
+static int readtcp(struct ct_data *, char *, int);
+static int writetcp(struct ct_data *, char *, int);
+
+static enum clnt_stat clnttcp_call(CLIENT *, uint32_t, xdrproc_t, void *,
+ xdrproc_t, void *, struct timeval);
+static void clnttcp_abort(CLIENT *);
+static void clnttcp_geterr(CLIENT *, struct rpc_err *);
+static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *);
+static bool_t clnttcp_control(CLIENT *, uint_t, void *);
+static void clnttcp_destroy(CLIENT *h);
+static bool_t clnttcp_getreply(CLIENT *, xdrproc_t, void *,
+ int, uint32_t *, uint32_t *, struct timeval *);
+static int clnttcp_poll(CLIENT *, uint32_t);
+
+
+static struct clnt_ops tcp_ops = {
+ clnttcp_call,
+ clnttcp_abort,
+ clnttcp_geterr,
+ clnttcp_freeres,
+ clnttcp_destroy,
+ clnttcp_control,
+ clnttcp_getreply,
+ clnttcp_poll
+};
+
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clnttcp_create(
+ struct sockaddr_in *raddr,
+ uint32_t prog,
+ uint32_t vers,
+ int *sockp,
+ uint_t sendsz,
+ uint_t recvsz)
+{
+ CLIENT *h;
+ struct ct_data *ct;
+ struct timeval now;
+ struct rpc_msg call_msg;
+
+ h = (CLIENT *)mem_alloc(sizeof(CLIENT));
+ if (h == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ ct = (struct ct_data *)mem_alloc(sizeof(struct ct_data));
+ if (ct == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (raddr->sin_port == 0) {
+ uint16_t port;
+ if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
+ mem_free((void *)ct, sizeof(struct ct_data));
+ mem_free((void *)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+ }
+ raddr->sin_port = htons(port);
+ }
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp < 0) {
+ *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ if ((*sockp < 0)
+ || (connect(*sockp, (struct sockaddr *)raddr,
+ sizeof(struct sockaddr_in)) < 0)) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(*sockp);
+ goto fooy;
+ }
+ ct->ct_closeit = TRUE;
+ } else {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+ ct->ct_addr = *raddr;
+
+ /*
+ * Initialize call message
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the staic part of the call msg and stash it away
+ */
+ xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
+ if (ct->ct_closeit) {
+ (void)close(*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
+ (void *)ct, readtcp, writetcp);
+ h->cl_ops = &tcp_ops;
+ h->cl_private = (char *) ct;
+ h->cl_auth = authnone_create();
+ return (h);
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ mem_free((void *)ct, sizeof(struct ct_data));
+ mem_free((void *)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+static enum clnt_stat
+clnttcp_call(
+ CLIENT *h,
+ uint32_t proc,
+ xdrproc_t xdr_args,
+ void * args_ptr,
+ xdrproc_t xdr_results,
+ void * results_ptr,
+ struct timeval timeout)
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ uint32_t x_id;
+ uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */
+ bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset) {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
+ && timeout.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl(--(*msg_x_id));
+ if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (! XDR_PUTLONG(xdrs, (int32_t *)&proc)) ||
+ (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
+ (! (*xdr_args)(xdrs, args_ptr))) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (! xdrrec_endofrecord(xdrs, shipnow))
+ return (ct->ct_error.re_status = RPC_CANTSEND);
+ if (! shipnow)
+ return (RPC_SUCCESS);
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ return(ct->ct_error.re_status = RPC_TIMEDOUT);
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ /* CONSTCOND */
+ while (TRUE) {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = xdr_void;
+ if (! xdrrec_skiprecord(xdrs))
+ return (ct->ct_error.re_status);
+ /* now decode and validate the response header */
+ if (! xdr_replymsg(xdrs, &reply_msg)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return (ct->ct_error.re_status);
+ }
+ if (reply_msg.rm_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply(&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ } else if (! (*xdr_results)(xdrs, results_ptr)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH(h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return (ct->ct_error.re_status);
+}
+
+static void
+clnttcp_geterr(
+ CLIENT *h,
+ struct rpc_err *errp)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clnttcp_freeres(
+ CLIENT *cl,
+ xdrproc_t xdr_res,
+ void * res_ptr)
+{
+ struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+/* ARGSUSED */
+static void
+clnttcp_abort(CLIENT *c)
+{
+}
+
+static bool_t
+clnttcp_control(
+ CLIENT *cl,
+ uint_t request,
+ void *info)
+{
+ struct ct_data *ct = (struct ct_data *)cl->cl_private;
+
+ switch (request) {
+ case CLSET_TIMEOUT:
+ ct->ct_wait = *(struct timeval *)info;
+ ct->ct_waitset = TRUE;
+ break;
+ case CLGET_TIMEOUT:
+ *(struct timeval *)info = ct->ct_wait;
+ break;
+ case CLGET_SERVER_ADDR:
+ *(struct sockaddr_in *)info = ct->ct_addr;
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+static void
+clnttcp_destroy(
+ CLIENT *h)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit) {
+ (void)close(ct->ct_sock);
+ }
+ XDR_DESTROY(&(ct->ct_xdrs));
+ mem_free((void *)ct, sizeof(struct ct_data));
+ mem_free((void *)h, sizeof(CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp(
+ struct ct_data *ct,
+ char *buf,
+ int len)
+{
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+
+ if (len == 0)
+ return (0);
+ FD_ZERO(&mask);
+ FD_SET(ct->ct_sock, &mask);
+#else
+ int mask = 1 << (ct->ct_sock);
+ int readfds;
+
+ if (len == 0)
+ return (0);
+
+#endif /* def FD_SETSIZE */
+ /* CONSTCOND */
+ while (TRUE) {
+ readfds = mask;
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL,
+ &(ct->ct_wait))) {
+ case 0:
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return (-1);
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = errno;
+ return (-1);
+ }
+ break;
+ }
+ switch (len = read(ct->ct_sock, buf, len)) {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return (len);
+}
+
+static int
+writetcp(
+ struct ct_data *ct,
+ char *buf,
+ int len)
+{
+ int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return (-1);
+ }
+ }
+ return (len);
+}
+
+/* ARGSUSED */
+static bool_t
+clnttcp_getreply(
+ CLIENT *cl,
+ xdrproc_t xproc,
+ void *xres,
+ int cnt,
+ uint32_t *xids,
+ uint32_t *xid,
+ struct timeval *tv)
+{
+ return (FALSE);
+}
+
+/* ARGSUSED */
+static int
+clnttcp_poll(
+ CLIENT *cl,
+ uint32_t usec)
+{
+ return (-1);
+}
+
--- /dev/null
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)clnt_udp.c 2.1 97/10/23";
+#endif
+/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifndef FreeBSD
+#include <stropts.h>
+#endif /* ndef Free BSD */
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+#include <errno.h>
+#include "rpc/pmap_clnt.h"
+
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_sock;
+ bool_t cu_closeit;
+ struct sockaddr_in cu_raddr;
+ int cu_rlen;
+ struct timeval cu_wait;
+ struct timeval cu_total;
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ uint_t cu_xdrpos;
+ uint_t cu_sendsz;
+ char *cu_outbuf;
+ uint_t cu_recvsz;
+ char cu_inbuf[1];
+};
+
+/*
+ * UDP bases client side rpc operations
+ */
+static enum clnt_stat clntudp_call(CLIENT *, uint32_t, xdrproc_t, void *,
+ xdrproc_t, void *, struct timeval);
+static void clntudp_abort(CLIENT *);
+static void clntudp_geterr(CLIENT *, struct rpc_err *);
+static bool_t clntudp_freeres(CLIENT *, xdrproc_t, void *);
+static bool_t clntudp_control(CLIENT *, uint_t, void *);
+static void clntudp_destroy(CLIENT *);
+static bool_t clntudp_getreply(CLIENT *, xdrproc_t, void *,
+ int, uint32_t *, uint32_t *, struct timeval *);
+static int clntudp_poll(CLIENT *, uint32_t);
+
+static struct clnt_ops udp_ops = {
+ clntudp_call,
+ clntudp_abort,
+ clntudp_geterr,
+ clntudp_freeres,
+ clntudp_destroy,
+ clntudp_control,
+ clntudp_getreply,
+ clntudp_poll
+};
+
+/*
+ * Create a UDP based client handle.
+ * If *sockp<0, *sockp is set to a newly created UPD socket.
+ * If raddr->sin_port is 0 a binder on the remote machine
+ * is consulted for the correct port number.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is initialized to null authentication.
+ * Caller may wish to set this something more useful.
+ *
+ * wait is the amount of time used between retransmitting a call if
+ * no response has been heard; retransmition occurs until the actual
+ * rpc call times out.
+ *
+ * sendsz and recvsz are the maximum allowable packet sizes that can be
+ * sent and received.
+ */
+CLIENT *
+clntudp_bufcreate(
+ struct sockaddr_in *raddr,
+ uint32_t program,
+ uint32_t version,
+ struct timeval wait,
+ int *sockp,
+ uint_t sendsz,
+ uint_t recvsz)
+{
+ CLIENT *cl;
+ struct cu_data *cu;
+ struct timeval now;
+ struct rpc_msg call_msg;
+
+ cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
+ if (cl == NULL) {
+ (void) fprintf(stderr, "clntudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ sendsz = ((sendsz + 3) / 4) * 4;
+ recvsz = ((recvsz + 3) / 4) * 4;
+ cu = (struct cu_data *)mem_alloc(sizeof(struct cu_data) +
+ sendsz + recvsz);
+ if (cu == NULL) {
+ (void) fprintf(stderr, "clntudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ cu->cu_outbuf = &cu->cu_inbuf[recvsz];
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (raddr->sin_port == 0) {
+ uint16_t port;
+ if ((port =
+ pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
+ goto fooy;
+ }
+ raddr->sin_port = htons(port);
+ }
+ cl->cl_ops = &udp_ops;
+ cl->cl_private = (char *)cu;
+ cu->cu_raddr = *raddr;
+ cu->cu_rlen = sizeof (cu->cu_raddr);
+ cu->cu_wait = wait;
+ cu->cu_total.tv_sec = -1;
+ cu->cu_total.tv_usec = -1;
+ cu->cu_sendsz = sendsz;
+ cu->cu_recvsz = recvsz;
+ call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = program;
+ call_msg.rm_call.cb_vers = version;
+ xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
+ sendsz, XDR_ENCODE);
+ if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
+ goto fooy;
+ }
+ cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
+ if (*sockp < 0) {
+#if defined(O_NONBLOCK)
+ int flags;
+#elif defined(FIONBIO)
+ int dontblock = 1;
+#endif
+
+ *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (*sockp < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ /* attempt to bind to prov port */
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ /* the sockets rpc controls are non-blocking */
+#if defined(O_NONBLOCK)
+ flags = fcntl(*sockp, F_GETFL, 0) | O_NONBLOCK;
+ (void)fcntl(*sockp, F_SETFL, flags);
+#elif defined(FIONBIO)
+ (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
+#endif
+ cu->cu_closeit = TRUE;
+ } else {
+ cu->cu_closeit = FALSE;
+ }
+ cu->cu_sock = *sockp;
+ cl->cl_auth = authnone_create();
+ return (cl);
+fooy:
+ if (cu)
+ mem_free((void *)cu, sizeof(struct cu_data) + sendsz + recvsz);
+ if (cl)
+ mem_free((void *)cl, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+CLIENT *
+clntudp_create(
+ struct sockaddr_in *raddr,
+ uint32_t program,
+ uint32_t version,
+ struct timeval wait,
+ int *sockp)
+{
+
+ return(clntudp_bufcreate(raddr, program, version, wait, sockp,
+ UDPMSGSIZE, UDPMSGSIZE));
+}
+
+static enum clnt_stat
+clntudp_call(
+ CLIENT *cl,
+ uint32_t proc,
+ xdrproc_t xargs,
+ void * argsp,
+ xdrproc_t xresults,
+ void * resultsp,
+ struct timeval utimeout)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ XDR *xdrs;
+ int outlen;
+ int inlen;
+#if defined(AIX)
+ size_t fromlen;
+#else
+ int fromlen;
+#endif
+#ifdef FD_SETSIZE
+ fd_set readfds;
+ fd_set mask;
+#else
+ int readfds;
+ int mask;
+#endif /* def FD_SETSIZE */
+ struct sockaddr_in from;
+ struct rpc_msg reply_msg;
+ XDR reply_xdrs;
+ struct timeval time_waited;
+ bool_t ok;
+ int nrefreshes = 2; /* number of times to refresh cred */
+ struct timeval timeout;
+
+ if (cu->cu_total.tv_usec == -1) {
+ timeout = utimeout; /* use supplied timeout */
+ } else {
+ timeout = cu->cu_total; /* use default timeout */
+ }
+
+ time_waited.tv_sec = 0;
+ time_waited.tv_usec = 0;
+call_again:
+ xdrs = &(cu->cu_outxdrs);
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, cu->cu_xdrpos);
+ /*
+ * the transaction is the first thing in the out buffer
+ */
+ (*(uint32_t *)(cu->cu_outbuf))++;
+ if ((! XDR_PUTLONG(xdrs, (int32_t *)&proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp)))
+ return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
+ outlen = (int)XDR_GETPOS(xdrs);
+
+send_again:
+ if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
+ (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
+ != outlen) {
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ }
+
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+ /*
+ * sub-optimal code appears here because we have
+ * some clock time to spare while the packets are in flight.
+ * (We assume that this is actually only executed once.)
+ */
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = resultsp;
+ reply_msg.acpted_rply.ar_results.proc = xresults;
+#ifdef FD_SETSIZE
+ FD_ZERO(&mask);
+ FD_SET(cu->cu_sock, &mask);
+#else
+ mask = 1 << cu->cu_sock;
+#endif /* def FD_SETSIZE */
+ for (;;) {
+ readfds = mask;
+ switch (select(_rpc_dtablesize(), &readfds, NULL,
+ NULL, &(cu->cu_wait))) {
+
+ case 0:
+ time_waited.tv_sec += cu->cu_wait.tv_sec;
+ time_waited.tv_usec += cu->cu_wait.tv_usec;
+ while (time_waited.tv_usec >= 1000000) {
+ time_waited.tv_sec++;
+ time_waited.tv_usec -= 1000000;
+ }
+ if ((time_waited.tv_sec < timeout.tv_sec) ||
+ ((time_waited.tv_sec == timeout.tv_sec) &&
+ (time_waited.tv_usec < timeout.tv_usec)))
+ goto send_again;
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+
+ /*
+ * buggy in other cases because time_waited is not being
+ * updated.
+ */
+ case -1:
+ if (errno == EINTR)
+ continue;
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ do {
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
+ (int) cu->cu_recvsz, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ if (inlen < sizeof(uint32_t))
+ continue;
+ /* see if reply transaction id matches sent id */
+ if (*((uint32_t *)(cu->cu_inbuf)) != *((uint32_t *)(cu->cu_outbuf)))
+ continue;
+ /* we now assume we have the proper reply */
+ break;
+ }
+
+ /*
+ * now decode and validate the response
+ */
+ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (uint_t)inlen, XDR_DECODE);
+ ok = xdr_replymsg(&reply_xdrs, &reply_msg);
+ /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
+ if (ok) {
+ _seterr_reply(&reply_msg, &(cu->cu_error));
+ if (cu->cu_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(cl->cl_auth,
+ &reply_msg.acpted_rply.ar_verf)) {
+ cu->cu_error.re_status = RPC_AUTHERROR;
+ cu->cu_error.re_why = AUTH_INVALIDRESP;
+ }
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
+ nrefreshes--;
+ goto call_again;
+ }
+ } /* end of unsuccessful completion */
+ } /* end of valid reply message */
+ else {
+ cu->cu_error.re_status = RPC_CANTDECODERES;
+ }
+ return (cu->cu_error.re_status);
+}
+
+static void
+clntudp_geterr(
+ CLIENT *cl,
+ struct rpc_err *errp)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ *errp = cu->cu_error;
+}
+
+
+static bool_t
+clntudp_freeres(
+ CLIENT *cl,
+ xdrproc_t xdr_res,
+ void * res_ptr)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ XDR *xdrs = &(cu->cu_outxdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+/* ARGSUSED */
+static void
+clntudp_abort(
+ CLIENT *h)
+{
+}
+
+static bool_t
+clntudp_control(
+ CLIENT *cl,
+ uint_t request,
+ void *info)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ switch (request) {
+ case CLSET_TIMEOUT:
+ cu->cu_total = *(struct timeval *)info;
+ break;
+ case CLGET_TIMEOUT:
+ *(struct timeval *)info = cu->cu_total;
+ break;
+ case CLSET_RETRY_TIMEOUT:
+ cu->cu_wait = *(struct timeval *)info;
+ break;
+ case CLGET_RETRY_TIMEOUT:
+ *(struct timeval *)info = cu->cu_wait;
+ break;
+ case CLGET_SERVER_ADDR:
+ *(struct sockaddr_in *)info = cu->cu_raddr;
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+static void
+clntudp_destroy(
+ CLIENT *cl)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ if (cu->cu_closeit) {
+ (void)close(cu->cu_sock);
+ }
+ XDR_DESTROY(&(cu->cu_outxdrs));
+ mem_free((void *)cu, (sizeof(struct cu_data) + cu->cu_sendsz + cu->cu_recvsz));
+ mem_free((void *)cl, sizeof(CLIENT));
+}
+
+/* ARGSUSED */
+static bool_t
+clntudp_getreply(
+ CLIENT *cl,
+ xdrproc_t xproc,
+ void *xres,
+ int cnt,
+ uint32_t *xids,
+ uint32_t *xid,
+ struct timeval *tv)
+{
+ return (FALSE);
+}
+
+/* ARGSUSED */
+static int
+clntudp_poll(
+ CLIENT *cl,
+ uint32_t usec)
+{
+ return (-1);
+}
--- /dev/null
+#ifndef lint
+static char sfs_get_myaddress_id[] = "@(#)get_myaddress.c 2.1 97/10/23";
+#endif
+/* @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * get_myaddress.c
+ *
+ * Get client's IP address via ioctl. This avoids using the yellowpages.
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifndef FreeBSD
+#include <stropts.h>
+#endif /* ndef FreeBSD */
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_prot.h"
+#include "rpc/osdep.h"
+#include <sys/utsname.h>
+
+#ifdef FreeBSD
+#include <netdb.h>
+#define MY_NAMELEN 256
+static char myhostname[MY_NAMELEN];
+#endif /* def FreeBSD */
+
+void
+get_myaddress(
+ struct sockaddr_in *addr)
+{
+ int s;
+ char buf[BUFSIZ];
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ int len;
+ struct sockaddr_in tmp_addr;
+#ifdef _AIX
+ char *cp, *cplim;
+#endif
+
+#ifdef FreeBSD
+ static struct in_addr *my_addr;
+ struct hostent *ent;
+
+ /* In FreeBSD, SIOCGIFCONF provides AF_LINK information, not AF_INET. */
+ gethostname(myhostname, MY_NAMELEN);
+ ent = gethostbyname(myhostname);
+ if (ent == NULL) {
+ fprintf(stderr, "lookup on server's name failed\n");
+ exit(1);
+ }
+ bzero(addr, sizeof(struct sockaddr_in));
+ my_addr = (struct in_addr *)(*(ent->h_addr_list));
+ bcopy(my_addr, &(addr->sin_addr.s_addr), sizeof(struct in_addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(PMAPPORT);
+ return;
+#endif /* def FreeBSD */
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("get_myaddress: socket");
+ exit(1);
+ }
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("get_myaddress: ioctl (get interface configuration)");
+ exit(1);
+ }
+ ifr = ifc.ifc_req;
+#ifdef _AIX
+ cplim = buf + ifc.ifc_len;
+ for (cp = buf; cp < cplim;
+ cp += MAX(sizeof(struct ifreq),
+ (sizeof (ifr->ifr_name) +
+ MAX((ifr->ifr_addr).sa_len, sizeof(ifr->ifr_addr))))) {
+ ifr = (struct ifreq *)cp;
+#else
+ for (len = ifc.ifc_len; len; len -= sizeof(ifreq), ifr++) {
+#endif
+ ifreq = *ifr;
+ /* Save address, since SIOCGIFFLAGS may scribble over *ifr. */
+ tmp_addr = *((struct sockaddr_in *)&ifr->ifr_addr);
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ perror("get_myaddress: ioctl");
+ exit(1);
+ }
+ if ((ifreq.ifr_flags & IFF_UP) &&
+ ifr->ifr_addr.sa_family == AF_INET) {
+ *addr = tmp_addr;
+ addr->sin_port = htons(PMAPPORT);
+ break;
+ }
+ }
+ (void) close(s);
+}
+
+/*
+ * A generic gethostname
+ */
+int
+getmyhostname(char *name, int namelen)
+{
+#if !defined(HAS_GETHOSTNAME)
+ struct utsname utsname;
+ int ret;
+
+ ret = uname(&utsname);
+ if (ret == -1)
+ return (-1);
+ (void) strncpy(name, utsname.nodename, namelen);
+ return (0);
+#else
+ return(gethostname(name, namelen));
+#endif
+}
--- /dev/null
+#ifndef lint
+static char sfs_getrpcent_id[] = "@(#)getrpcent.c 2.1 97/10/23";
+#endif
+/* @(#)getrpcent.c 2.2 88/07/29 4.0 RPCSRC */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)getrpcent.c 1.9 87/08/11 Copyr 1984 Sun Micro";
+#endif
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Copyright (c) 1985 by Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "rpc/rpc.h"
+#include "rpc/netdb.h"
+#include "rpc/osdep.h"
+
+/*
+ * Internet version.
+ */
+struct rpcdata {
+ FILE *rpcf;
+ char *current;
+ int currentlen;
+ int stayopen;
+#define MAXALIASES 35
+ char *rpc_aliases[MAXALIASES];
+ struct rpcent rpc;
+ char line[BUFSIZ+1];
+ char *domain;
+} *rpcdata;
+
+extern void setrpcent(int);
+extern void endrpcent(void);
+static struct rpcent *interpret(char *, int);
+
+static char RPCDB[] = "/etc/rpc";
+
+static struct rpcdata *
+_rpcdata(void)
+{
+ register struct rpcdata *d = rpcdata;
+
+ if (d == 0) {
+ d = (struct rpcdata *)calloc(1, sizeof (struct rpcdata));
+ rpcdata = d;
+ }
+ return (d);
+}
+
+struct rpcent *
+getrpcbynumber(
+ int number)
+{
+ register struct rpcdata *d = _rpcdata();
+ register struct rpcent *p;
+
+ if (d == 0)
+ return (0);
+ setrpcent(0);
+ while ((p = getrpcent()) != NULL) {
+ if (p->r_number == number)
+ break;
+ }
+ endrpcent();
+ return (p);
+}
+
+struct rpcent *
+getrpcbyname(
+ char *name)
+{
+ struct rpcent *rpc;
+ char **rp;
+
+ setrpcent(0);
+ while((rpc = getrpcent()) != NULL) {
+ if (strcmp(rpc->r_name, name) == 0)
+ return (rpc);
+ for (rp = rpc->r_aliases; *rp != NULL; rp++) {
+ if (strcmp(*rp, name) == 0)
+ return (rpc);
+ }
+ }
+ endrpcent();
+ return (NULL);
+}
+
+void
+setrpcent(
+ int f)
+{
+ register struct rpcdata *d = _rpcdata();
+
+ if (d == 0)
+ return;
+ if (d->rpcf == NULL)
+ d->rpcf = fopen(RPCDB, "r");
+ else
+ rewind(d->rpcf);
+ if (d->current)
+ free(d->current);
+ d->current = NULL;
+ d->stayopen |= f;
+}
+
+void
+endrpcent(void)
+{
+ register struct rpcdata *d = _rpcdata();
+
+ if (d == 0)
+ return;
+ if (d->current && !d->stayopen) {
+ free(d->current);
+ d->current = NULL;
+ }
+ if (d->rpcf && !d->stayopen) {
+ fclose(d->rpcf);
+ d->rpcf = NULL;
+ }
+}
+
+struct rpcent *
+getrpcent(void)
+{
+ register struct rpcdata *d = _rpcdata();
+
+ if (d == 0)
+ return(NULL);
+ if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL)
+ return (NULL);
+ if (fgets(d->line, BUFSIZ, d->rpcf) == NULL)
+ return (NULL);
+ return (interpret(d->line, strlen(d->line)));
+}
+
+static struct rpcent *
+interpret(
+ char *val,
+ int len)
+{
+ register struct rpcdata *d = _rpcdata();
+ char *p;
+ register char *cp, **q;
+
+ if (d == 0)
+ return (NULL);
+ strncpy(d->line, val, len);
+ p = d->line;
+ d->line[len] = '\n';
+ if (*p == '#')
+ return (getrpcent());
+ cp = strchr(p, '#');
+ if (cp == NULL) {
+ cp = strchr(p, '\n');
+ if (cp == NULL)
+ return (getrpcent());
+ }
+ *cp = '\0';
+ cp = strchr(p, ' ');
+ if (cp == NULL) {
+ cp = strchr(p, '\t');
+ if (cp == NULL)
+ return (getrpcent());
+ }
+ *cp++ = '\0';
+ /* THIS STUFF IS INTERNET SPECIFIC */
+ d->rpc.r_name = d->line;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ d->rpc.r_number = atoi(cp);
+ q = d->rpc.r_aliases = d->rpc_aliases;
+ cp = strchr(p, ' ');
+ if (cp != NULL)
+ *cp++ = '\0';
+ else {
+ cp = strchr(p, '\t');
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &(d->rpc_aliases[MAXALIASES - 1]))
+ *q++ = cp;
+ cp = strchr(p, ' ');
+ if (cp != NULL)
+ *cp++ = '\0';
+ else {
+ cp = strchr(p, '\t');
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ }
+ *q = NULL;
+ return (&d->rpc);
+}
--- /dev/null
+#ifndef lint
+static char sfs_getrpcport_id[] = "@(#)getrpcport.c 2.1 97/10/23";
+#endif
+/* @(#)getrpcport.c 2.1 88/07/29 4.0 RPCSRC */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)getrpcport.c 1.3 87/08/11 SMI";
+#endif
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Copyright (c) 1985 by Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include <netdb.h>
+#include "rpc/osdep.h"
+
+getrpcport(host, prognum, versnum, proto)
+ char *host;
+{
+ struct sockaddr_in addr;
+ struct hostent *hp;
+
+ if ((hp = gethostbyname(host)) == NULL)
+ return (0);
+ memmove((char *) &addr.sin_addr, hp->h_addr, hp->h_length);
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ return (pmap_getport(&addr, prognum, versnum, proto));
+}
--- /dev/null
+/*
+ * @(#)netdb.h 2.1 97/10/23
+ */
+/* @(#)netdb.h 2.1 88/07/29 3.9 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)rpc.h 1.8 87/07/24 SMI */
+
+/* Really belongs in <netdb.h> */
+
+struct rpcent {
+ char *r_name; /* name of server for this rpc program */
+ char **r_aliases; /* alias list */
+ int r_number; /* rpc program number */
+};
+
+extern struct rpcent *getrpcbynumber(int);
+extern struct rpcent *getrpcbyname(char *);
+extern struct rpcent *getrpcent(void);
--- /dev/null
+/*
+ * @(#)osdep.h 2.1 97/10/23
+ */
+/* @(#)types.h 1.18 87/07/24 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+#ifndef __RPC_OSDEP_H__
+#define __RPC_OSDEP_H__
+
+/*
+ * OS dependancies
+ *
+ * These are non-XPG4.2 standard include files, if not compiling in a
+ * strict environment simply #include them, otherwise we must define
+ * our own missing pieces. The definitions below are specific to
+ * Solaris 2.X and may be different on other systems.
+ */
+
+#if !defined(_XOPEN_SOURCE) || defined (OSF1) || defined(AIX)
+#if defined(SVR4)
+#define BSD_COMP
+#endif
+#if defined(AIX)
+#include <sys/param.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#else
+#if defined(_BIG_ENDIAN) && !defined(ntohl) && !defined(lint)
+/* big-endian */
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+
+#elif !defined(ntohl) /* little-endian */
+
+extern unsigned short ntohs(unsigned short ns);
+extern unsigned short htons(unsigned short hs);
+extern unsigned long ntohl(unsigned long nl);
+extern unsigned long htonl(unsigned long hl);
+
+#endif
+/*
+ * Internet address
+ * This definition contains obsolete fields for compatibility
+ * with SunOS 3.x and 4.2bsd. The presence of subnets renders
+ * divisions into fixed fields misleading at best. New code
+ * should use only the s_addr field.
+ */
+struct in_addr {
+ union {
+ struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b;
+ struct { unsigned short s_w1, s_w2; } S_un_w;
+ unsigned long S_addr;
+ } S_un;
+#define s_addr S_un.S_addr /* should be used for all code */
+#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */
+#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */
+#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */
+#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */
+#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */
+};
+
+struct sockaddr_in {
+ short sin_family;
+ unsigned short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Structure used by kernel to store most
+ * addresses.
+ */
+struct sockaddr {
+ uint16_t sa_family; /* address family */
+ char sa_data[14]; /* up to 14 bytes of direct address */
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+struct ifreq {
+#define IFNAMSIZ 16
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ char ifru_oname[IFNAMSIZ]; /* other if name */
+ struct sockaddr ifru_broadaddr;
+ short ifru_flags;
+ int ifru_metric;
+ char ifru_data[1]; /* interface dependent data */
+ char ifru_enaddr[6];
+ int if_muxid[2]; /* mux id's for arp and ip */
+
+ /* Struct for FDDI ioctl's */
+ struct ifr_dnld_reqs {
+ void *v_addr;
+ void *m_addr;
+ void *ex_addr;
+ uint_t size;
+ } ifru_dnld_req;
+
+ /* Struct for FDDI stats */
+ struct ifr_fddi_stats {
+ uint_t stat_size;
+ void *fddi_stats;
+ } ifru_fddi_stat;
+
+ struct ifr_netmapents {
+ uint_t map_ent_size, /* size of netmap structure */
+ entry_number; /* index into netmap list */
+ void *fddi_map_ent; /* pointer to user structure */
+ } ifru_netmapent;
+
+ /* Field for generic ioctl for fddi */
+
+ struct ifr_fddi_gen_struct {
+ int ifru_fddi_gioctl; /* field for gen ioctl */
+ void *ifru_fddi_gaddr; /* Generic ptr to a field */
+ } ifru_fddi_gstruct;
+
+ } ifr_ifru;
+
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
+#define ifr_oname ifr_ifru.ifru_oname /* other if name */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+#define ifr_enaddr ifr_ifru.ifru_enaddr /* ethernet address */
+
+/* FDDI specific */
+#define ifr_dnld_req ifr_ifru.ifru_dnld_req
+#define ifr_fddi_stat ifr_ifru.ifru_fddi_stat
+#define ifr_fddi_netmap ifr_ifru.ifru_netmapent /* FDDI network map entries */
+#define ifr_fddi_gstruct ifr_ifru.ifru_fddi_gstruct
+
+#define ifr_ip_muxid ifr_ifru.if_muxid[0]
+#define ifr_arp_muxid ifr_ifru.if_muxid[1]
+};
+
+struct ifconf {
+ int ifc_len; /* size of associated buffer */
+ union {
+ void *ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */
+};
+
+#define AF_INET 2
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+#define IPPORT_RESERVED 1024
+#define SOCK_DGRAM 1
+#define SOCK_STREAM 2
+#define SO_SNDBUF 0x1001
+#define SO_RCVBUF 0x1002
+#define SOL_SOCKET 0xffff
+#define INADDR_ANY (uint32_t)0x00000000
+#define IFF_BROADCAST 0x2
+#define IFF_UP 0x1
+
+#define IOCPARM_MASK 0xff
+#define IOC_OUT 0x40000000
+#define IOC_IN 0x80000000
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+#define _IOWR(x, y, t) \
+ (IOC_INOUT|((((int)sizeof (t))&IOCPARM_MASK)<<16)|(x<<8)|y)
+#define SIOCGIFCONF _IOWR('i', 20, struct ifconf)
+#define SIOCGIFFLAGS _IOWR('i', 17, struct ifreq)
+#define SIOCGIFBRDADDR _IOWR('i', 23, struct ifreq)
+
+extern int accept(int, struct sockaddr *, int *);
+extern int bind(int, struct sockaddr *, int);
+extern int connect(int, struct sockaddr *, int);
+extern int getsockname(int, struct sockaddr *, int *);
+extern int getsockopt(int, int, int, char *, int *);
+extern int listen(int, int);
+extern int recvfrom(int, char *, int, int, struct sockaddr *, int *);
+extern int sendto(int, const char *, int, int, const struct sockaddr *, int);
+extern int setsockopt(int, int, int, const char *, int);
+extern int socket(int, int, int);
+extern unsigned long inet_netof(struct in_addr);
+extern struct in_addr inet_makeaddr(int, int);
+
+#endif /* _XOPEN_SOURCE */
+
+#endif /* __RPC_OSDEP_H__ */
--- /dev/null
+#ifndef lint
+static char sfs_pmap_clnt_id[] = "@(#)pmap_clnt.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_clnt.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_prot.h"
+#include "rpc/pmap_clnt.h"
+
+static struct timeval timeout = { 5, 0 };
+static struct timeval tottimeout = { 60, 0 };
+
+extern void get_myaddress(struct sockaddr_in *addr);
+
+/*
+ * Set a mapping between program,version and port.
+ * Calls the pmap service remotely to do the mapping.
+ */
+bool_t
+pmap_set(
+ uint32_t program,
+ uint32_t version,
+ int protocol,
+ uint16_t port)
+{
+ struct sockaddr_in myaddress;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+ bool_t rslt;
+
+ get_myaddress(&myaddress);
+ client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
+ timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client == (CLIENT *)NULL)
+ return (FALSE);
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = port;
+ if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt,
+ tottimeout) != RPC_SUCCESS) {
+ clnt_perror(client, "Cannot register service");
+ return (FALSE);
+ }
+ CLNT_DESTROY(client);
+ (void)close(socket);
+ return (rslt);
+}
+
+/*
+ * Remove the mapping between program,version and port.
+ * Calls the pmap service remotely to do the un-mapping.
+ */
+bool_t
+pmap_unset(
+ uint32_t program,
+ uint32_t version)
+{
+ struct sockaddr_in myaddress;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+ bool_t rslt;
+
+ get_myaddress(&myaddress);
+ client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
+ timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client == (CLIENT *)NULL)
+ return (FALSE);
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_port = parms.pm_prot = 0;
+ CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt,
+ tottimeout);
+ CLNT_DESTROY(client);
+ (void)close(socket);
+ return (rslt);
+}
--- /dev/null
+/*
+ * @(#)pmap_clnt.h 2.1 97/10/23
+ */
+/* @(#)pmap_clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.11 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * pmap_clnt.h
+ * Supplies C routines to get to portmap services.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+#ifndef _SRPC_PMAP_CLNT_H
+#define _SRPC_PMAP_CLNT_H
+
+
+/*
+ * Usage:
+ * success = pmap_set(program, version, protocol, port);
+ * success = pmap_unset(program, version);
+ * port = pmap_getport(address, program, version, protocol);
+ * head = pmap_getmaps(address);
+ * clnt_stat = pmap_rmtcall(address, program, version, procedure,
+ * xdrargs, argsp, xdrres, resp, tout, port_ptr)
+ * (works for udp only.)
+ * clnt_stat = clnt_broadcast(program, version, procedure,
+ * xdrargs, argsp, xdrres, resp, eachresult)
+ * (like pmap_rmtcall, except the call is broadcasted to all
+ * locally connected nets. For each valid response received,
+ * the procedure eachresult is called. Its form is:
+ * done = eachresult(resp, raddr)
+ * bool_t done;
+ * void * resp;
+ * struct sockaddr_in raddr;
+ * where resp points to the results of the call and raddr is the
+ * address if the responder to the broadcast.
+ */
+
+typedef bool_t (*resultproc_t)();
+extern bool_t pmap_set(uint32_t, uint32_t, int, uint16_t);
+extern bool_t pmap_unset(uint32_t, uint32_t);
+extern struct pmaplist *pmap_getmaps(struct sockaddr_in *);
+extern enum clnt_stat pmap_rmtcall(struct sockaddr_in *, uint32_t, uint32_t,
+ uint32_t, xdrproc_t, void *,
+ xdrproc_t, void *,
+ struct timeval, uint32_t *);
+extern enum clnt_stat clnt_broadcast(uint32_t, uint32_t, uint32_t, xdrproc_t,
+ void *, xdrproc_t,
+ void *, resultproc_t);
+extern uint16_t pmap_getport(struct sockaddr_in *, uint32_t, uint32_t,
+ uint_t);
+#endif /* _SRPC_PMAP_CLNT_H */
--- /dev/null
+#ifndef lint
+static char sfs_pmap_getmaps_id[] = "@(#)pmap_getmaps.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_getmap.c
+ * Client interface to pmap rpc service.
+ * contains pmap_getmaps, which is only tcp service involved
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_prot.h"
+#include "rpc/pmap_clnt.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+
+#define NAMELEN 255
+#define MAX_BROADCAST_SIZE 1400
+
+
+/*
+ * Get a copy of the current port maps.
+ * Calls the pmap service remotely to do get the maps.
+ */
+struct pmaplist *
+pmap_getmaps(
+ struct sockaddr_in *address)
+{
+ struct pmaplist *head = (struct pmaplist *)NULL;
+ int socket = -1;
+ struct timeval minutetimeout;
+ register CLIENT *client;
+
+ minutetimeout.tv_sec = 60;
+ minutetimeout.tv_usec = 0;
+ address->sin_port = htons(PMAPPORT);
+ client = clnttcp_create(address, PMAPPROG,
+ PMAPVERS, &socket, 50, 500);
+ if (client != (CLIENT *)NULL) {
+ if (CLNT_CALL(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist,
+ &head, minutetimeout) != RPC_SUCCESS) {
+ clnt_perror(client, "pmap_getmaps rpc problem");
+ }
+ CLNT_DESTROY(client);
+ }
+ (void)close(socket);
+ address->sin_port = 0;
+ return (head);
+}
--- /dev/null
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)pmap_getport.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_prot.h"
+#include "rpc/pmap_clnt.h"
+#include "rpc/osdep.h"
+
+static struct timeval timeout = { 5, 0 };
+static struct timeval tottimeout = { 60, 0 };
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+uint16_t
+pmap_getport(
+ struct sockaddr_in *address,
+ uint32_t program,
+ uint32_t version,
+ uint_t protocol)
+{
+ uint16_t port = 0;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+
+ address->sin_port = htons(PMAPPORT);
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
+ xdr_uint16_t, &port, tottimeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ CLNT_DESTROY(client);
+ }
+ (void)close(socket);
+ address->sin_port = 0;
+ return (port);
+}
--- /dev/null
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)pmap_prot.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_prot.c
+ * Protocol for the local binder service, or pmap.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/pmap_prot.h"
+
+
+bool_t
+xdr_pmap(
+ XDR *xdrs,
+ struct pmap *regs)
+{
+
+ if (xdr_uint32_t(xdrs, ®s->pm_prog) &&
+ xdr_uint32_t(xdrs, ®s->pm_vers) &&
+ xdr_uint32_t(xdrs, ®s->pm_prot))
+ return (xdr_uint32_t(xdrs, ®s->pm_port));
+ return (FALSE);
+}
--- /dev/null
+/*
+ * @(#)pmap_prot.h 2.1 97/10/23
+ */
+/* @(#)pmap_prot.h 2.1 88/07/29 4.0 RPCSRC; from 1.14 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * pmap_prot.h
+ * Protocol for the local binder service, or pmap.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * The following procedures are supported by the protocol:
+ *
+ * PMAPPROC_NULL() returns ()
+ * takes nothing, returns nothing
+ *
+ * PMAPPROC_SET(struct pmap) returns (bool_t)
+ * TRUE is success, FALSE is failure. Registers the tuple
+ * [prog, vers, prot, port].
+ *
+ * PMAPPROC_UNSET(struct pmap) returns (bool_t)
+ * TRUE is success, FALSE is failure. Un-registers pair
+ * [prog, vers]. prot and port are ignored.
+ *
+ * PMAPPROC_GETPORT(struct pmap) returns (int32_t unsigned).
+ * 0 is failure. Otherwise returns the port number where the pair
+ * [prog, vers] is registered. It may lie!
+ *
+ * PMAPPROC_DUMP() RETURNS (struct pmaplist *)
+ *
+ * PMAPPROC_CALLIT(unsigned, unsigned, unsigned, string<>)
+ * RETURNS (port, string<>);
+ * usage: encapsulatedresults = PMAPPROC_CALLIT(prog, vers, proc, encapsulatedargs);
+ * Calls the procedure on the local machine. If it is not registered,
+ * this procedure is quite; ie it does not return error information!!!
+ * This procedure only is supported on rpc/udp and calls via
+ * rpc/udp. This routine only passes null authentication parameters.
+ * This file has no interface to xdr routines for PMAPPROC_CALLIT.
+ *
+ * The service supports remote procedure calls on udp/ip or tcp/ip socket 111.
+ */
+
+#define PMAPPORT ((uint16_t)111)
+#define PMAPPROG ((uint32_t)100000)
+#define PMAPVERS ((uint32_t)2)
+#define PMAPVERS_PROTO ((uint32_t)2)
+#define PMAPVERS_ORIG ((uint32_t)1)
+#define PMAPPROC_NULL ((uint32_t)0)
+#define PMAPPROC_SET ((uint32_t)1)
+#define PMAPPROC_UNSET ((uint32_t)2)
+#define PMAPPROC_GETPORT ((uint32_t)3)
+#define PMAPPROC_DUMP ((uint32_t)4)
+#define PMAPPROC_CALLIT ((uint32_t)5)
+
+struct pmap {
+ uint32_t pm_prog;
+ uint32_t pm_vers;
+ uint32_t pm_prot;
+ uint32_t pm_port;
+};
+
+extern bool_t xdr_pmap();
+
+struct pmaplist {
+ struct pmap pml_map;
+ struct pmaplist *pml_next;
+};
+
+extern bool_t xdr_pmaplist();
--- /dev/null
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)pmap_prot2.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_prot2.c
+ * Protocol for the local binder service, or pmap.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/pmap_prot.h"
+
+
+/*
+ * What is going on with linked lists? (!)
+ * First recall the link list declaration from pmap_prot.h:
+ *
+ * struct pmaplist {
+ * struct pmap pml_map;
+ * struct pmaplist *pml_map;
+ * };
+ *
+ * Compare that declaration with a corresponding xdr declaration that
+ * is (a) pointer-less, and (b) recursive:
+ *
+ * typedef union switch (bool_t) {
+ *
+ * case TRUE: struct {
+ * struct pmap;
+ * pmaplist_t foo;
+ * };
+ *
+ * case FALSE: struct {};
+ * } pmaplist_t;
+ *
+ * Notice that the xdr declaration has no nxt pointer while
+ * the C declaration has no bool_t variable. The bool_t can be
+ * interpreted as ``more data follows me''; if FALSE then nothing
+ * follows this bool_t; if TRUE then the bool_t is followed by
+ * an actual struct pmap, and then (recursively) by the
+ * xdr union, pamplist_t.
+ *
+ * This could be implemented via the xdr_union primitive, though this
+ * would cause a one recursive call per element in the list. Rather than do
+ * that we can ``unwind'' the recursion
+ * into a while loop and do the union arms in-place.
+ *
+ * The head of the list is what the C programmer wishes to past around
+ * the net, yet is the data that the pointer points to which is interesting;
+ * this sounds like a job for xdr_reference!
+ */
+bool_t
+xdr_pmaplist(
+ XDR *xdrs,
+ struct pmaplist **rp)
+{
+ /*
+ * more_elements is pre-computed in case the direction is
+ * XDR_ENCODE or XDR_FREE. more_elements is overwritten by
+ * xdr_bool when the direction is XDR_DECODE.
+ */
+ bool_t more_elements;
+ register int freeing = (xdrs->x_op == XDR_FREE);
+ register struct pmaplist **next;
+
+ /* CONSTCOND */
+ while (TRUE) {
+ more_elements = (bool_t)(*rp != NULL);
+ if (! xdr_bool(xdrs, &more_elements))
+ return (FALSE);
+ if (! more_elements)
+ return (TRUE); /* we are done */
+ /*
+ * the unfortunate side effect of non-recursion is that in
+ * the case of freeing we must remember the next object
+ * before we free the current object ...
+ */
+ if (freeing)
+ next = &((*rp)->pml_next);
+ if (! xdr_reference(xdrs, (void **)rp,
+ (uint_t)sizeof(struct pmaplist), xdr_pmap))
+ return (FALSE);
+ rp = (freeing) ? next : &((*rp)->pml_next);
+ }
+}
--- /dev/null
+#ifndef lint
+static char sfs_pmap_rmt_id[] = "@(#)pmap_rmt.c 2.1 97/10/23";
+#endif
+/* @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * pmap_rmt.c
+ * Client interface to pmap rpc service.
+ * remote call and broadcast service
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifndef FreeBSD
+#include <stropts.h>
+#endif /* ndef FreeBSD */
+#include <string.h>
+#include <errno.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_prot.h"
+#include "rpc/pmap_clnt.h"
+#include "rpc/pmap_rmt.h"
+#include "rpc/osdep.h"
+
+#define MAX_BROADCAST_SIZE 1400
+
+static struct timeval timeout = { 3, 0 };
+
+
+/*
+ * pmapper remote-call-service interface.
+ * This routine is used to call the pmapper remote call service
+ * which will look up a service program in the port maps, and then
+ * remotely call that routine with the given parameters. This allows
+ * programs to do a lookup and call in one step.
+*/
+enum clnt_stat
+pmap_rmtcall(
+ struct sockaddr_in *addr,
+ uint32_t prog,
+ uint32_t vers,
+ uint32_t proc,
+ xdrproc_t xdrargs,
+ void *argsp,
+ xdrproc_t xdrres,
+ void *resp,
+ struct timeval tout,
+ uint32_t *port_ptr)
+{
+ int socket = -1;
+ register CLIENT *client;
+ struct rmtcallargs a;
+ struct rmtcallres r;
+ enum clnt_stat stat;
+
+ addr->sin_port = htons(PMAPPORT);
+ client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket);
+ if (client != (CLIENT *)NULL) {
+ a.prog = prog;
+ a.vers = vers;
+ a.proc = proc;
+ a.args_ptr = argsp;
+ a.xdr_args = xdrargs;
+ r.port_ptr = port_ptr;
+ r.results_ptr = resp;
+ r.xdr_results = xdrres;
+ stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a,
+ xdr_rmtcallres, &r, tout);
+ CLNT_DESTROY(client);
+ } else {
+ stat = RPC_FAILED;
+ }
+ (void)close(socket);
+ addr->sin_port = 0;
+ return (stat);
+}
+
+
+/*
+ * XDR remote call arguments
+ * written for XDR_ENCODE direction only
+ */
+bool_t
+xdr_rmtcall_args(
+ XDR *xdrs,
+ struct rmtcallargs *cap)
+{
+ uint_t lenposition, argposition, position;
+
+ if (xdr_uint32_t(xdrs, &(cap->prog)) &&
+ xdr_uint32_t(xdrs, &(cap->vers)) &&
+ xdr_uint32_t(xdrs, &(cap->proc))) {
+ lenposition = XDR_GETPOS(xdrs);
+ if (! xdr_uint32_t(xdrs, &(cap->arglen)))
+ return (FALSE);
+ argposition = XDR_GETPOS(xdrs);
+ if (! (*(cap->xdr_args))(xdrs, cap->args_ptr))
+ return (FALSE);
+ position = XDR_GETPOS(xdrs);
+ cap->arglen = (uint32_t)position - (uint32_t)argposition;
+ XDR_SETPOS(xdrs, lenposition);
+ if (! xdr_uint32_t(xdrs, &(cap->arglen)))
+ return (FALSE);
+ XDR_SETPOS(xdrs, position);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR remote call results
+ * written for XDR_DECODE direction only
+ */
+bool_t
+xdr_rmtcallres(
+ XDR *xdrs,
+ struct rmtcallres *crp)
+{
+ void *port_ptr;
+
+ port_ptr = (void *)crp->port_ptr;
+ if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
+ xdr_uint32_t) && xdr_uint32_t(xdrs, &crp->resultslen)) {
+ crp->port_ptr = (uint32_t *)port_ptr;
+ return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
+ }
+ return (FALSE);
+}
+
+
+/*
+ * The following is kludged-up support for simple rpc broadcasts.
+ * Someday a large, complicated system will replace these trivial
+ * routines which only support udp/ip .
+ */
+
+static int
+getbroadcastnets(
+ struct in_addr *addrs,
+ int sock,
+ char *buf)
+{
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ struct sockaddr_in *sin;
+ int n, i;
+
+ ifc.ifc_len = UDPMSGSIZE;
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("broadcast: ioctl (get interface configuration)");
+ return (0);
+ }
+ ifr = ifc.ifc_req;
+ for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
+ ifreq = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ perror("broadcast: ioctl (get interface flags)");
+ continue;
+ }
+ if ((ifreq.ifr_flags & IFF_BROADCAST) &&
+ (ifreq.ifr_flags & IFF_UP) &&
+ ifr->ifr_addr.sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+#ifdef SIOCGIFBRDADDR /* 4.3BSD */
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ addrs[i++] = inet_makeaddr((int)inet_netof
+ (sin->sin_addr), INADDR_ANY);
+ } else {
+ addrs[i++] = ((struct sockaddr_in*)
+ &ifreq.ifr_addr)->sin_addr;
+ }
+#else /* 4.2 BSD */
+ addrs[i++] = inet_makeaddr(inet_netof
+ (sin->sin_addr.s_addr), INADDR_ANY);
+#endif
+ }
+ }
+ return (i);
+}
+
+enum clnt_stat
+clnt_broadcast(
+ uint32_t prog, /* program number */
+ uint32_t vers, /* version number */
+ uint32_t proc, /* procedure number */
+ xdrproc_t xargs, /* xdr routine for args */
+ void * argsp, /* pointer to args */
+ xdrproc_t xresults, /* xdr routine for results */
+ void * resultsp, /* pointer to results */
+ resultproc_t eachresult) /* call with each result obtained */
+{
+ enum clnt_stat stat;
+ AUTH *unix_auth = authunix_create_default();
+ XDR xdr_stream;
+ register XDR *xdrs = &xdr_stream;
+ int outlen, inlen, nets;
+#if defined(AIX)
+ size_t fromlen;
+#else
+ int fromlen;
+#endif /* AIX */
+ register int sock;
+ int on = 1;
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+#else
+ int readfds;
+ register int mask;
+#endif /* def FD_SETSIZE */
+ register int i;
+ bool_t done = FALSE;
+ register uint32_t xid;
+ uint32_t port;
+ struct in_addr addrs[20];
+ struct sockaddr_in baddr, raddr; /* broadcast and response addresses */
+ struct rmtcallargs a;
+ struct rmtcallres r;
+ struct rpc_msg msg;
+ struct timeval t;
+ char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE];
+
+ /*
+ * initialization: create a socket, a broadcast address, and
+ * preserialize the arguments into a send buffer.
+ */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("Cannot create socket for broadcast rpc");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+#ifdef SO_BROADCAST
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof (on)) < 0) {
+ perror("Cannot set socket option SO_BROADCAST");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+#endif /* def SO_BROADCAST */
+#ifdef FD_SETSIZE
+ FD_ZERO(&mask);
+ FD_SET(sock, &mask);
+#else
+ mask = (1 << sock);
+#endif /* def FD_SETSIZE */
+ nets = getbroadcastnets(addrs, sock, inbuf);
+ memset((char *)&baddr, '\0', sizeof (baddr));
+ baddr.sin_family = AF_INET;
+ baddr.sin_port = htons(PMAPPORT);
+ baddr.sin_addr.s_addr = htonl(INADDR_ANY);
+/* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */
+ (void)gettimeofday(&t, (struct timezone *)0);
+ msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec;
+ t.tv_usec = 0;
+ msg.rm_direction = CALL;
+ msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ msg.rm_call.cb_prog = PMAPPROG;
+ msg.rm_call.cb_vers = PMAPVERS;
+ msg.rm_call.cb_proc = PMAPPROC_CALLIT;
+ msg.rm_call.cb_cred = unix_auth->ah_cred;
+ msg.rm_call.cb_verf = unix_auth->ah_verf;
+ a.prog = prog;
+ a.vers = vers;
+ a.proc = proc;
+ a.xdr_args = xargs;
+ a.args_ptr = argsp;
+ r.port_ptr = &port;
+ r.xdr_results = xresults;
+ r.results_ptr = resultsp;
+ xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE);
+ if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) {
+ stat = RPC_CANTENCODEARGS;
+ goto done_broad;
+ }
+ outlen = (int)xdr_getpos(xdrs);
+ xdr_destroy(xdrs);
+ /*
+ * Basic loop: broadcast a packet and wait a while for response(s).
+ * The response timeout grows larger per iteration.
+ */
+ for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) {
+ for (i = 0; i < nets; i++) {
+ baddr.sin_addr = addrs[i];
+ if (sendto(sock, outbuf, outlen, 0,
+ (struct sockaddr *)&baddr,
+ sizeof (struct sockaddr)) != outlen) {
+ perror("Cannot send broadcast packet");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+ }
+ if (eachresult == NULL) {
+ stat = RPC_SUCCESS;
+ goto done_broad;
+ }
+ recv_again:
+ msg.acpted_rply.ar_verf = _null_auth;
+ msg.acpted_rply.ar_results.where = (void *)&r;
+ msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
+ readfds = mask;
+ switch (select(_rpc_dtablesize(), &readfds, NULL,
+ NULL, &t)) {
+
+ case 0: /* timed out */
+ stat = RPC_TIMEDOUT;
+ continue;
+
+ case -1: /* some kind of error */
+ if (errno == EINTR)
+ goto recv_again;
+ perror("Broadcast select problem");
+ stat = RPC_CANTRECV;
+ goto done_broad;
+
+ } /* end of select results switch */
+ try_again:
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0,
+ (struct sockaddr *)&raddr, &fromlen);
+ if (inlen < 0) {
+ if (errno == EINTR)
+ goto try_again;
+ perror("Cannot receive reply to broadcast");
+ stat = RPC_CANTRECV;
+ goto done_broad;
+ }
+ if (inlen < sizeof(uint32_t))
+ goto recv_again;
+ /*
+ * see if reply transaction id matches sent id.
+ * If so, decode the results.
+ */
+ xdrmem_create(xdrs, inbuf, (uint_t)inlen, XDR_DECODE);
+ if (xdr_replymsg(xdrs, &msg)) {
+ if ((msg.rm_xid == xid) &&
+ (msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
+ (msg.acpted_rply.ar_stat == SUCCESS)) {
+ raddr.sin_port = htons((uint16_t)port);
+ done = (*eachresult)(resultsp, &raddr);
+ }
+ /* otherwise, we just ignore the errors ... */
+ } else {
+#ifdef notdef
+ /* some kind of deserialization problem ... */
+ if (msg.rm_xid == xid)
+ fprintf(stderr, "Broadcast deserialization problem");
+ /* otherwise, just random garbage */
+#endif
+ }
+ xdrs->x_op = XDR_FREE;
+ msg.acpted_rply.ar_results.proc = xdr_void;
+ (void)xdr_replymsg(xdrs, &msg);
+ (void)(*xresults)(xdrs, resultsp);
+ xdr_destroy(xdrs);
+ if (done) {
+ stat = RPC_SUCCESS;
+ goto done_broad;
+ } else {
+ goto recv_again;
+ }
+ }
+done_broad:
+ (void)close(sock);
+ AUTH_DESTROY(unix_auth);
+ return (stat);
+}
+
--- /dev/null
+/*
+ * @(#)pmap_rmt.h 2.1 97/10/23
+ */
+/* @(#)pmap_rmt.h 2.1 88/07/29 4.0 RPCSRC; from 1.2 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Structures and XDR routines for parameters to and replies from
+ * the portmapper remote-call-service.
+ *
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+struct rmtcallargs {
+ uint32_t prog, vers, proc, arglen;
+ void *args_ptr;
+ xdrproc_t xdr_args;
+};
+
+bool_t xdr_rmtcall_args();
+
+struct rmtcallres {
+ uint32_t *port_ptr;
+ uint32_t resultslen;
+ caddr_t results_ptr;
+ xdrproc_t xdr_results;
+};
+
+bool_t xdr_rmtcallres();
--- /dev/null
+/*
+ * @(#)rpc.h 2.1 97/10/23
+ */
+
+/* @(#)rpc.h 2.4 89/07/11 4.0 RPCSRC; from 1.9 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * rpc.h, Just includes the billions of rpc header files necessary to
+ * do remote procedure calling.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#ifndef __RPC_HEADER__
+#define __RPC_HEADER__
+
+#include "rpc/types.h" /* some typedefs */
+
+#include "rpc/osdep.h"
+
+/* external data representation interfaces */
+#include "rpc/xdr.h" /* generic (de)serializer */
+
+/* Client side only authentication */
+#include "rpc/auth.h" /* generic authenticator (client side) */
+
+/* Client side (mostly) remote procedure call */
+#include "rpc/clnt.h" /* generic rpc stuff */
+
+#include "rpc/pmap_clnt.h"
+
+/* semi-private protocol headers */
+#include "rpc/rpc_msg.h" /* protocol for rpc messages */
+#include "rpc/auth_unix.h" /* protocol for unix style cred */
+/*
+ * Uncomment-out the next line if you are building the rpc library with
+ * DES Authentication (see the README file in the secure_rpc/ directory).
+ */
+#ifdef notdef
+#include "rpc/auth_des.h" /* protocol for des style cred */
+#endif
+
+/* Server side only remote procedure callee */
+#include "rpc/svc.h" /* service manager and multiplexer */
+#include "rpc/svc_auth.h" /* service side authenticator */
+
+#endif /* ndef __RPC_HEADER__ */
--- /dev/null
+#ifndef lint
+static char sfs_rpc_callmsg_c_id[] = "@(#)rpc_callmsg.c 2.1 97/10/23";
+#endif
+/* @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * rpc_callmsg.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "rpc/rpc.h"
+
+/*
+ * XDR a call message
+ */
+bool_t
+xdr_callmsg(
+ XDR *xdrs,
+ struct rpc_msg *cmsg)
+{
+ int32_t *buf;
+ struct opaque_auth *oa;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT
+ + RNDUP(cmsg->rm_call.cb_cred.oa_length)
+ + 2 * BYTES_PER_XDR_UNIT
+ + RNDUP(cmsg->rm_call.cb_verf.oa_length));
+ if (buf != NULL) {
+ IXDR_PUT_LONG(buf, cmsg->rm_xid);
+ IXDR_PUT_ENUM(buf, cmsg->rm_direction);
+ if (cmsg->rm_direction != CALL) {
+ return (FALSE);
+ }
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_rpcvers);
+ if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
+ return (FALSE);
+ }
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_prog);
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_vers);
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_proc);
+ oa = &cmsg->rm_call.cb_cred;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_LONG(buf, oa->oa_length);
+ if (oa->oa_length) {
+ memmove((caddr_t)buf, oa->oa_base, oa->oa_length);
+ buf += RNDUP(oa->oa_length) / sizeof (int32_t);
+ }
+ oa = &cmsg->rm_call.cb_verf;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_LONG(buf, oa->oa_length);
+ if (oa->oa_length) {
+ memmove((caddr_t)buf, oa->oa_base, oa->oa_length);
+ /* no real need....
+ buf += RNDUP(oa->oa_length) / sizeof (int32_t);
+ */
+ }
+ return (TRUE);
+ }
+ }
+ if (xdrs->x_op == XDR_DECODE) {
+ buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT);
+ if (buf != NULL) {
+ cmsg->rm_xid = IXDR_GET_LONG(buf);
+ cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
+ if (cmsg->rm_direction != CALL) {
+ return (FALSE);
+ }
+ cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG(buf);
+ if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
+ return (FALSE);
+ }
+ cmsg->rm_call.cb_prog = IXDR_GET_LONG(buf);
+ cmsg->rm_call.cb_vers = IXDR_GET_LONG(buf);
+ cmsg->rm_call.cb_proc = IXDR_GET_LONG(buf);
+ oa = &cmsg->rm_call.cb_cred;
+ oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
+ oa->oa_length = IXDR_GET_LONG(buf);
+ if (oa->oa_length) {
+ if (oa->oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (oa->oa_base == NULL) {
+ oa->oa_base = (caddr_t)
+ mem_alloc(oa->oa_length);
+ }
+ buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
+ if (buf == NULL) {
+ if (xdr_opaque(xdrs, oa->oa_base,
+ oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ memmove(oa->oa_base, (caddr_t)buf,
+ oa->oa_length);
+ /* no real need....
+ buf += RNDUP(oa->oa_length) /
+ sizeof (int32_t);
+ */
+ }
+ }
+ oa = &cmsg->rm_call.cb_verf;
+ buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE ||
+ xdr_u_int(xdrs, &oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
+ oa->oa_length = IXDR_GET_LONG(buf);
+ }
+ if (oa->oa_length) {
+ if (oa->oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (oa->oa_base == NULL) {
+ oa->oa_base = (caddr_t)
+ mem_alloc(oa->oa_length);
+ }
+ buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
+ if (buf == NULL) {
+ if (xdr_opaque(xdrs, oa->oa_base,
+ oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ memmove(oa->oa_base, (caddr_t)buf,
+ oa->oa_length);
+ /* no real need...
+ buf += RNDUP(oa->oa_length) /
+ sizeof (int32_t);
+ */
+ }
+ }
+ return (TRUE);
+ }
+ }
+ if (
+ xdr_uint32_t(xdrs, &(cmsg->rm_xid)) &&
+ xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) &&
+ (cmsg->rm_direction == CALL) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
+ (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_prog)) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_vers)) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_proc)) &&
+ xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_cred)) )
+ return (xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_verf)));
+ return (FALSE);
+}
+
--- /dev/null
+#ifndef lint
+static char sfs_rpc_commondata_id[] = "@(#)rpc_commondata.c 2.1 97/10/23";
+#endif
+/* @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#include "rpc/rpc.h"
+/*
+ * This file should only contain common data (global data) that is exported
+ * by public interfaces
+ */
+struct opaque_auth _null_auth;
+#ifdef FD_SETSIZE
+fd_set svc_fdset;
+#else
+int svc_fds;
+#endif /* def FD_SETSIZE */
+struct rpc_createerr rpc_createerr;
--- /dev/null
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)rpc_dtablesize.c 2.1 97/10/23";
+#endif
+/* @(#)rpc_dtablesize.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)rpc_dtablesize.c 1.2 87/08/11 Copyr 1987 Sun Micro";
+#endif
+
+
+/*
+ * Cache the result of getdtablesize(), so we don't have to do an
+ * expensive system call every time.
+ */
+
+#if !(defined(USE_GETDTABLESIZE) || defined(USE_GETRLIMIT) || defined(USE_NOFILE))
+#define USE_GETDTABLESIZE
+#endif
+
+#ifdef USE_GETDTABLESIZE
+#include <unistd.h>
+
+int
+_rpc_dtablesize(void)
+{
+ static int size = 0;
+
+ if (size == 0) {
+ size = getdtablesize();
+#ifdef FD_SETSIZE
+ /*
+ * FreeBSD select() produces a segmentation violation if
+ * the argument is larger than FD_SETSIZE.
+ */
+ if (size > FD_SETSIZE) {
+ size = FD_SETSIZE;
+ }
+#endif /* def FD_SETSIZE */
+ }
+ return (size);
+}
+#endif
+
+#ifdef USE_GETRLIMIT
+#include <sys/resource.h>
+
+int
+_rpc_dtablesize(void)
+{
+ static int size = 0;
+ struct rlimit rlimit;
+
+ if (size == 0) {
+ (void)getrlimit(RLIMIT_NOFILE, &rlimit);
+ size = (int) rlimit.rlim_cur;
+ }
+ return (size);
+}
+#endif
+
+#ifdef USE_NOFILE
+#include <sys/param.h>
+
+int
+_rpc_dtablesize(void)
+{
+ return (NOFILE);
+}
+#endif
--- /dev/null
+/*
+ * @(#)rpc_msg.h 2.1 97/10/23
+ */
+/* @(#)rpc_msg.h 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)rpc_msg.h 1.7 86/07/16 SMI */
+
+/*
+ * rpc_msg.h
+ * rpc message definition
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#ifndef __RPC_MSG_H
+#define __RPC_MSG_H
+
+#define RPC_MSG_VERSION ((uint32_t) 2)
+#define RPC_SERVICE_PORT ((uint16_t) 2048)
+
+/*
+ * Bottom up definition of an rpc message.
+ * NOTE: call and reply use the same overall stuct but
+ * different parts of unions within it.
+ */
+
+enum msg_type {
+ CALL=0,
+ REPLY=1
+};
+
+enum reply_stat {
+ MSG_ACCEPTED=0,
+ MSG_DENIED=1
+};
+
+enum accept_stat {
+ SUCCESS=0,
+ PROG_UNAVAIL=1,
+ PROG_MISMATCH=2,
+ PROC_UNAVAIL=3,
+ GARBAGE_ARGS=4,
+ SYSTEM_ERR=5
+};
+
+enum reject_stat {
+ RPC_MISMATCH=0,
+ AUTH_ERROR=1
+};
+
+/*
+ * Reply part of an rpc exchange
+ */
+
+/*
+ * Reply to an rpc request that was accepted by the server.
+ * Note: there could be an error even though the request was
+ * accepted.
+ */
+struct accepted_reply {
+ struct opaque_auth ar_verf;
+ enum accept_stat ar_stat;
+ union {
+ struct {
+ uint32_t low;
+ uint32_t high;
+ } AR_versions;
+ struct {
+ void * where;
+ xdrproc_t proc;
+ } AR_results;
+ /* and many other null cases */
+ } ru;
+#define ar_results ru.AR_results
+#define ar_vers ru.AR_versions
+};
+
+/*
+ * Reply to an rpc request that was rejected by the server.
+ */
+struct rejected_reply {
+ enum reject_stat rj_stat;
+ union {
+ struct {
+ uint32_t low;
+ uint32_t high;
+ } RJ_versions;
+ enum auth_stat RJ_why; /* why authentication did not work */
+ } ru;
+#define rj_vers ru.RJ_versions
+#define rj_why ru.RJ_why
+};
+
+/*
+ * Body of a reply to an rpc request.
+ */
+struct reply_body {
+ enum reply_stat rp_stat;
+ union {
+ struct accepted_reply RP_ar;
+ struct rejected_reply RP_dr;
+ } ru;
+#define rp_acpt ru.RP_ar
+#define rp_rjct ru.RP_dr
+};
+
+/*
+ * Body of an rpc request call.
+ */
+struct call_body {
+ uint32_t cb_rpcvers; /* must be equal to two */
+ uint32_t cb_prog;
+ uint32_t cb_vers;
+ uint32_t cb_proc;
+ struct opaque_auth cb_cred;
+ struct opaque_auth cb_verf; /* protocol specific - provided by client */
+};
+
+/*
+ * The rpc message
+ */
+struct rpc_msg {
+ uint32_t rm_xid;
+ enum msg_type rm_direction;
+ union {
+ struct call_body RM_cmb;
+ struct reply_body RM_rmb;
+ } ru;
+#define rm_call ru.RM_cmb
+#define rm_reply ru.RM_rmb
+};
+#define acpted_rply ru.RM_rmb.ru.RP_ar
+#define rjcted_rply ru.RM_rmb.ru.RP_dr
+
+
+/*
+ * XDR routine to handle a rpc message.
+ * xdr_callmsg(xdrs, cmsg)
+ * XDR *xdrs;
+ * struct rpc_msg *cmsg;
+ */
+extern bool_t xdr_callmsg(XDR *, struct rpc_msg *);
+
+/*
+ * XDR routine to pre-serialize the static part of a rpc message.
+ * xdr_callhdr(xdrs, cmsg)
+ * XDR *xdrs;
+ * struct rpc_msg *cmsg;
+ */
+extern bool_t xdr_callhdr(XDR *, struct rpc_msg *);
+
+/*
+ * XDR routine to handle a rpc reply.
+ * xdr_replymsg(xdrs, rmsg)
+ * XDR *xdrs;
+ * struct rpc_msg *rmsg;
+ */
+extern bool_t xdr_replymsg(XDR *, struct rpc_msg *);
+
+/*
+ * Fills in the error part of a reply message.
+ * _seterr_reply(msg, error)
+ * struct rpc_msg *msg;
+ * struct rpc_err *error;
+ */
+extern void _seterr_reply(struct rpc_msg *, struct rpc_err *);
+
+extern int _rpc_dtablesize(void);
+extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
+#endif /* __RPC_MSG_H */
--- /dev/null
+#ifndef lint
+static char sfs_rpc_prot_id[] = "@(#)rpc_prot.c 2.1 97/10/23";
+#endif
+/* @(#)rpc_prot.c 2.3 88/08/07 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)rpc_prot.c 1.36 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * rpc_prot.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * This set of routines implements the rpc message definition,
+ * its serializer and some common rpc utility routines.
+ * The routines are meant for various implementations of rpc -
+ * they are NOT for the rpc client or rpc service implementations!
+ * Because authentication stuff is easy and is part of rpc, the opaque
+ * routines are also in this program.
+ */
+
+#include "rpc/rpc.h"
+
+/* * * * * * * * * * * * * * XDR Authentication * * * * * * * * * * * */
+
+struct opaque_auth _null_auth;
+
+/*
+ * XDR an opaque authentication struct
+ * (see auth.h)
+ */
+bool_t
+xdr_opaque_auth(
+ XDR *xdrs,
+ struct opaque_auth *ap)
+{
+
+ if (xdr_enum(xdrs, &(ap->oa_flavor)))
+ return (xdr_bytes(xdrs, (void *)&ap->oa_base,
+ &ap->oa_length, MAX_AUTH_BYTES));
+ return (FALSE);
+}
+
+/*
+ * XDR a DES block
+ */
+bool_t
+xdr_des_block(
+ XDR *xdrs,
+ des_block *blkp)
+{
+ return (xdr_opaque(xdrs, (void *)blkp, sizeof(des_block)));
+}
+
+/* * * * * * * * * * * * * * XDR RPC MESSAGE * * * * * * * * * * * * * * * */
+
+/*
+ * XDR the MSG_ACCEPTED part of a reply message union
+ */
+bool_t
+xdr_accepted_reply(
+ XDR *xdrs,
+ struct accepted_reply *ar)
+{
+
+ /* personalized union, rather than calling xdr_union */
+ if (! xdr_opaque_auth(xdrs, &(ar->ar_verf)))
+ return (FALSE);
+ if (! xdr_enum(xdrs, (enum_t *)&(ar->ar_stat)))
+ return (FALSE);
+ switch (ar->ar_stat) {
+
+ case SUCCESS:
+ return ((*(ar->ar_results.proc))(xdrs, ar->ar_results.where));
+
+ case PROG_MISMATCH:
+ if (! xdr_uint32_t(xdrs, &(ar->ar_vers.low)))
+ return (FALSE);
+ return (xdr_uint32_t(xdrs, &(ar->ar_vers.high)));
+ }
+ return (TRUE); /* TRUE => open ended set of problems */
+}
+
+/*
+ * XDR the MSG_DENIED part of a reply message union
+ */
+bool_t
+xdr_rejected_reply(
+ XDR *xdrs,
+ struct rejected_reply *rr)
+{
+
+ /* personalized union, rather than calling xdr_union */
+ if (! xdr_enum(xdrs, (enum_t *)&(rr->rj_stat)))
+ return (FALSE);
+ switch (rr->rj_stat) {
+
+ case RPC_MISMATCH:
+ if (! xdr_uint32_t(xdrs, &(rr->rj_vers.low)))
+ return (FALSE);
+ return (xdr_uint32_t(xdrs, &(rr->rj_vers.high)));
+
+ case AUTH_ERROR:
+ return (xdr_enum(xdrs, (enum_t *)&(rr->rj_why)));
+ }
+ return (FALSE);
+}
+
+static struct xdr_discrim reply_dscrm[3] = {
+ { (int)MSG_ACCEPTED, xdr_accepted_reply },
+ { (int)MSG_DENIED, xdr_rejected_reply },
+ { __dontcare__, NULL_xdrproc_t } };
+
+/*
+ * XDR a reply message
+ */
+bool_t
+xdr_replymsg(
+ XDR *xdrs,
+ struct rpc_msg *rmsg)
+{
+ if (
+ xdr_uint32_t(xdrs, &(rmsg->rm_xid)) &&
+ xdr_enum(xdrs, (enum_t *)&(rmsg->rm_direction)) &&
+ (rmsg->rm_direction == REPLY) )
+ return (xdr_union(xdrs, (enum_t *)&(rmsg->rm_reply.rp_stat),
+ (void *)&(rmsg->rm_reply.ru), reply_dscrm, NULL_xdrproc_t));
+ return (FALSE);
+}
+
+
+/*
+ * Serializes the "static part" of a call message header.
+ * The fields include: rm_xid, rm_direction, rpcvers, prog, and vers.
+ * The rm_xid is not really static, but the user can easily munge on the fly.
+ */
+bool_t
+xdr_callhdr(
+ XDR *xdrs,
+ struct rpc_msg *cmsg)
+{
+
+ cmsg->rm_direction = CALL;
+ cmsg->rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ if (
+ (xdrs->x_op == XDR_ENCODE) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_xid)) &&
+ xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
+ xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_prog)) )
+ return (xdr_uint32_t(xdrs, &(cmsg->rm_call.cb_vers)));
+ return (FALSE);
+}
+
+/* ************************** Client utility routine ************* */
+
+static void
+accepted(
+ enum accept_stat acpt_stat,
+ struct rpc_err *error)
+{
+
+ switch (acpt_stat) {
+
+ case PROG_UNAVAIL:
+ error->re_status = RPC_PROGUNAVAIL;
+ return;
+
+ case PROG_MISMATCH:
+ error->re_status = RPC_PROGVERSMISMATCH;
+ return;
+
+ case PROC_UNAVAIL:
+ error->re_status = RPC_PROCUNAVAIL;
+ return;
+
+ case GARBAGE_ARGS:
+ error->re_status = RPC_CANTDECODEARGS;
+ return;
+
+ case SYSTEM_ERR:
+ error->re_status = RPC_SYSTEMERROR;
+ return;
+
+ case SUCCESS:
+ error->re_status = RPC_SUCCESS;
+ return;
+ }
+ /* something's wrong, but we don't know what ... */
+ error->re_status = RPC_FAILED;
+ error->re_lb.s1 = (int32_t)MSG_ACCEPTED;
+ error->re_lb.s2 = (int32_t)acpt_stat;
+}
+
+static void
+rejected(
+ enum reject_stat rjct_stat,
+ struct rpc_err *error)
+{
+
+ if ((int)rjct_stat == (int)RPC_VERSMISMATCH) {
+ error->re_status = RPC_VERSMISMATCH;
+ return;
+ }
+ if (rjct_stat == AUTH_ERROR) {
+ error->re_status = RPC_AUTHERROR;
+ return;
+ }
+
+ /* something's wrong, but we don't know what ... */
+ error->re_status = RPC_FAILED;
+ error->re_lb.s1 = (int32_t)MSG_DENIED;
+ error->re_lb.s2 = (int32_t)rjct_stat;
+}
+
+/*
+ * given a reply message, fills in the error
+ */
+void
+_seterr_reply(
+ struct rpc_msg *msg,
+ struct rpc_err *error)
+{
+
+ /* optimized for normal, SUCCESSful case */
+ switch (msg->rm_reply.rp_stat) {
+
+ case MSG_ACCEPTED:
+ if (msg->acpted_rply.ar_stat == SUCCESS) {
+ error->re_status = RPC_SUCCESS;
+ return;
+ };
+ accepted(msg->acpted_rply.ar_stat, error);
+ break;
+
+ case MSG_DENIED:
+ rejected(msg->rjcted_rply.rj_stat, error);
+ break;
+
+ default:
+ error->re_status = RPC_FAILED;
+ error->re_lb.s1 = (int32_t)(msg->rm_reply.rp_stat);
+ break;
+ }
+ switch (error->re_status) {
+
+ case RPC_VERSMISMATCH:
+ error->re_vers.low = msg->rjcted_rply.rj_vers.low;
+ error->re_vers.high = msg->rjcted_rply.rj_vers.high;
+ break;
+
+ case RPC_AUTHERROR:
+ error->re_why = msg->rjcted_rply.rj_why;
+ break;
+
+ case RPC_PROGVERSMISMATCH:
+ error->re_vers.low = msg->acpted_rply.ar_vers.low;
+ error->re_vers.high = msg->acpted_rply.ar_vers.high;
+ break;
+ }
+}
--- /dev/null
+#ifndef lint
+static char sfs_ctcp_id[] = "@(#)sfs_ctcp.c 2.1 97/10/23";
+#endif
+/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+#include <errno.h>
+#include "rpc/pmap_clnt.h"
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ struct sockaddr_in ct_addr;
+ struct sockaddr_in ct_oaddr;
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ uint_t ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+ int ct_first;
+ uint32_t ct_prog;
+ uint32_t ct_vers;
+ uint_t ct_sendsz;
+ uint_t ct_recvsz;
+};
+
+static int readtcp(struct ct_data *, char *, int);
+static int writetcp(struct ct_data *ct, char * buf, int len);
+
+static enum clnt_stat sfs_ctcp_call(CLIENT *, uint32_t, xdrproc_t, void *,
+ xdrproc_t, void *, struct timeval);
+static void sfs_ctcp_abort(CLIENT *);
+static void sfs_ctcp_geterr(CLIENT *, struct rpc_err *);
+static bool_t sfs_ctcp_freeres(CLIENT *, xdrproc_t, void *);
+static bool_t sfs_ctcp_control(CLIENT *, uint_t, void *);
+static void sfs_ctcp_destroy(CLIENT *);
+static bool_t sfs_ctcp_getreply(CLIENT *, xdrproc_t, void *,
+ int, uint32_t *, uint32_t *, struct timeval *);
+static int sfs_ctcp_poll(CLIENT *, uint32_t);
+
+static struct clnt_ops tcp_ops = {
+ sfs_ctcp_call,
+ sfs_ctcp_abort,
+ sfs_ctcp_geterr,
+ sfs_ctcp_freeres,
+ sfs_ctcp_destroy,
+ sfs_ctcp_control,
+ sfs_ctcp_getreply,
+ sfs_ctcp_poll
+};
+
+static int
+sfs_ctcp_make_conn(
+ struct ct_data *ct,
+ struct sockaddr_in *raddr,
+ int *sockp)
+{
+ int i;
+ int min_buf_sz;
+ int new_buf_sz;
+ int type;
+ int error;
+ int setopt = 1;
+#if defined(UNIXWARE) || defined(AIX)
+ size_t optlen;
+#else
+ int optlen;
+#endif
+
+ if (ct->ct_first == 0)
+ ct->ct_first++;
+
+#ifdef DEBUG
+ if (ct->ct_first)
+ (void) fprintf(stderr, "Re-establishing connection.\n");
+#endif
+
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (raddr->sin_port == 0) {
+ uint16_t port;
+ if ((port = pmap_getport(raddr, ct->ct_prog, ct->ct_vers,
+ IPPROTO_TCP)) == 0) {
+ return (-1);
+ }
+ raddr->sin_port = htons(port);
+ }
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp >= 0) {
+ ct->ct_closeit = FALSE;
+ return (*sockp);
+ }
+
+ ct->ct_closeit = TRUE;
+
+ *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ if ((*sockp < 0)
+ || (connect(*sockp, (struct sockaddr *)raddr,
+ sizeof(struct sockaddr_in)) < 0)) {
+ return (-1);
+ }
+
+ /*
+ * Need to try to size the socket buffers based on the number of
+ * outstanding requests desired. NFS reads and writes can do as
+ * much as 8K per request which can quickly run us out of space
+ * on the socket buffer queue. Use the maximum number of bio style
+ * requests * NFS_MAXDATA plus a pad as a starting point for desired
+ * socket buffer size and then back off by NFS_MAXDATA until the buffer
+ * sizes are successfully set. Note, the algorithm never sets the
+ * buffer size to less than the OS default.
+ */
+ type = SO_SNDBUF;
+ for (i = 0; i < 2; i++) {
+ optlen = sizeof(min_buf_sz);
+#ifdef UNIXWARE
+ if (getsockopt(*sockp, SOL_SOCKET, type,
+ (void *)&min_buf_sz, &optlen) < 0) {
+ /* guess the default */
+ min_buf_sz = 18 * 1024;
+ }
+#else
+ if (getsockopt(*sockp, SOL_SOCKET, type,
+ (char *)&min_buf_sz, &optlen) < 0) {
+ /* guess the default */
+ min_buf_sz = 18 * 1024;
+ }
+#endif
+
+ new_buf_sz = 512 * 1024;
+ if (new_buf_sz > min_buf_sz) {
+ do {
+ error = setsockopt(*sockp, SOL_SOCKET,
+ type, (char *)&new_buf_sz,
+ sizeof(int));
+ new_buf_sz -= (8 * 1024);
+ } while (error != 0 && new_buf_sz > min_buf_sz);
+ }
+
+ type = SO_RCVBUF;
+ }
+
+#ifdef TCP_NODELAY
+ setsockopt(*sockp, IPPROTO_TCP, TCP_NODELAY, (char *) &setopt,
+ sizeof(setopt));
+#endif /* TCP_NODELAY */
+
+ return (*sockp);
+}
+
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+sfs_ctcp_create(
+ struct sockaddr_in *raddr,
+ uint32_t prog,
+ uint32_t vers,
+ int *sockp,
+ uint_t sendsz,
+ uint_t recvsz)
+{
+ CLIENT *h;
+ struct ct_data *ct;
+ struct timeval now;
+ struct rpc_msg call_msg;
+
+ h = (CLIENT *)mem_alloc(sizeof(CLIENT));
+ if (h == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ ct = (struct ct_data *)mem_alloc(sizeof(struct ct_data));
+ if (ct == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ (void) memset(ct, '\0', sizeof (struct ct_data));
+
+ ct->ct_oaddr = *raddr;
+ ct->ct_prog = prog;
+ ct->ct_vers = vers;
+
+ if (sfs_ctcp_make_conn(ct, raddr, sockp) < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(*sockp);
+ goto fooy;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_sec = 0;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_addr = *raddr;
+
+ /*
+ * Initialize call message
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
+ if (ct->ct_closeit) {
+ (void)close(*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
+ (void *)ct, readtcp, writetcp);
+ ct->ct_sendsz = sendsz;
+ ct->ct_recvsz = recvsz;
+ h->cl_ops = &tcp_ops;
+ h->cl_private = (void *) ct;
+ h->cl_auth = authnone_create();
+ return (h);
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ mem_free((void *)ct, sizeof(struct ct_data));
+ mem_free((void *)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+static enum clnt_stat
+get_areply(
+ CLIENT *h,
+ struct rpc_msg *reply_msg)
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ /* CONSTCOND */
+ while (TRUE) {
+ reply_msg->acpted_rply.ar_verf = _null_auth;
+ reply_msg->acpted_rply.ar_results.where = NULL;
+ reply_msg->acpted_rply.ar_results.proc = xdr_void;
+ if (! xdrrec_skiprecord(xdrs)) {
+ return (ct->ct_error.re_status);
+ }
+ /* now decode and validate the response header */
+ if (! xdr_replymsg(xdrs, reply_msg)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ }
+ return (ct->ct_error.re_status);
+ }
+}
+
+static enum clnt_stat
+proc_header(
+ CLIENT *h,
+ struct rpc_msg *reply_msg,
+ xdrproc_t xdr_results,
+ void * results_ptr)
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ /*
+ * process header
+ */
+ _seterr_reply(reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth,
+ &reply_msg->acpted_rply.ar_verf)) {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ } else if (! (*xdr_results)(xdrs, results_ptr)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg->acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg->acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ return (ct->ct_error.re_status);
+}
+
+static enum clnt_stat
+sfs_ctcp_call(
+ CLIENT *h,
+ uint32_t proc,
+ xdrproc_t xdr_args,
+ void * args_ptr,
+ xdrproc_t xdr_results,
+ void * results_ptr,
+ struct timeval timeout)
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ uint32_t x_id;
+ uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */
+ bool_t shipnow;
+
+ ct->ct_wait = timeout;
+
+ shipnow =
+ (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
+ && timeout.tv_usec == 0) ? FALSE : TRUE;
+
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl(--(*msg_x_id));
+ if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (! XDR_PUTLONG(xdrs, (int32_t *)&proc)) ||
+ (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
+ (! (*xdr_args)(xdrs, args_ptr))) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (! xdrrec_endofrecord(xdrs, TRUE))
+ return (ct->ct_error.re_status = RPC_CANTSEND);
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ if (! shipnow && results_ptr == NULL)
+ return (RPC_SUCCESS);
+
+ /*
+ * Double hack, send back xid in results_prt if non-NULL
+ */
+ *(uint32_t *)results_ptr = x_id;
+ return(ct->ct_error.re_status = RPC_TIMEDOUT);
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ /* CONSTCOND */
+ while (TRUE) {
+ enum clnt_stat res;
+
+ if ((res = get_areply(h, &reply_msg)) != RPC_SUCCESS)
+ return (res);
+
+ if (reply_msg.rm_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ return (proc_header(h, &reply_msg, xdr_results, results_ptr));
+}
+
+static void
+sfs_ctcp_geterr(
+ CLIENT *h,
+ struct rpc_err *errp)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+sfs_ctcp_freeres(
+ CLIENT *cl,
+ xdrproc_t xdr_res,
+ void * res_ptr)
+{
+ struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+/* ARGSUSED */
+static void
+sfs_ctcp_abort(CLIENT *h)
+{
+}
+
+static bool_t
+sfs_ctcp_control(
+ CLIENT *cl,
+ uint_t request,
+ void *info)
+{
+ struct ct_data *ct = (struct ct_data *)cl->cl_private;
+
+ switch (request) {
+ case CLGET_SERVER_ADDR:
+ *(struct sockaddr_in *)info = ct->ct_addr;
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+static void
+sfs_ctcp_destroy(
+ CLIENT *h)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit) {
+ (void)close(ct->ct_sock);
+ }
+ XDR_DESTROY(&(ct->ct_xdrs));
+ mem_free((void *)ct, sizeof(struct ct_data));
+ mem_free((void *)h, sizeof(CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp(struct ct_data *ct,
+ char * buf,
+ int len)
+{
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+
+ FD_ZERO(&mask);
+ FD_SET(ct->ct_sock, &mask);
+#else
+ int mask = 1 << (ct->ct_sock);
+ int readfds;
+#endif /* def FD_SETSIZE */
+
+ if (len == 0)
+ return (0);
+
+ /* CONSTCOND */
+ while (TRUE) {
+ readfds = mask;
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL,
+ &(ct->ct_wait))) {
+ case 0:
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return (-1);
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = errno;
+ goto lost;
+ }
+ break;
+ }
+ switch (len = read(ct->ct_sock, buf, len)) {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ goto lost;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ goto lost;
+ }
+ return (len);
+lost:
+ /*
+ * We have lost our connection to the server. Try and
+ * reestablish it.
+ */
+ (void) close(ct->ct_sock);
+ ct->ct_addr = ct->ct_oaddr;
+ ct->ct_sock = -1;
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ (void) sfs_ctcp_make_conn(ct, &ct->ct_addr, &ct->ct_sock);
+ /*
+ * Create a client handle which uses xdrrec for
+ * serialization and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), ct->ct_sendsz, ct->ct_recvsz,
+ (void *)ct, readtcp, writetcp);
+ return (-1);
+}
+
+static int
+writetcp(
+ struct ct_data *ct,
+ char * buf,
+ int len)
+{
+ int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
+ /*
+ * We have lost our connection to the server. Try and
+ * reestablish it.
+ */
+ (void) close(ct->ct_sock);
+ ct->ct_addr = ct->ct_oaddr;
+ ct->ct_sock = -1;
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ (void) sfs_ctcp_make_conn(ct, &ct->ct_addr,
+ &ct->ct_sock);
+ /*
+ * Create a client handle which uses xdrrec for
+ * serialization and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), ct->ct_sendsz,
+ ct->ct_recvsz,
+ (void *)ct, readtcp, writetcp);
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return (-1);
+ }
+ }
+ return (len);
+}
+
+/* ARGSUSED */
+static bool_t
+sfs_ctcp_getreply(
+ CLIENT *cl,
+ xdrproc_t xdr_results,
+ void *results_ptr,
+ int cnt,
+ uint32_t *xids,
+ uint32_t *xid,
+ struct timeval *tv)
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ enum clnt_stat res;
+ int i;
+
+ /*
+ * Receive just one returning transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ ct->ct_wait.tv_sec = tv->tv_sec;
+ ct->ct_wait.tv_usec = tv->tv_usec;
+
+ if ((res = get_areply(cl, &reply_msg)) != RPC_SUCCESS)
+ return (res);
+
+ *xid = reply_msg.rm_xid;
+
+ /*
+ * Check to make sure xid matchs one that we are interested in
+ */
+ for (i = 0; i < cnt; i++) {
+ if (xids[i] == *xid)
+ break;
+ }
+
+ if (i == cnt)
+ return (RPC_CANTDECODERES);
+
+ /*
+ * process header
+ */
+ return (proc_header(cl, &reply_msg, xdr_results, results_ptr));
+}
+
+/* ARGSUSED */
+static int
+sfs_ctcp_poll(
+ CLIENT *cl,
+ uint32_t usecs)
+{
+ struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct timeval tv;
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+
+ FD_ZERO(&mask);
+ FD_SET(ct->ct_sock, &mask);
+#else
+ int mask = 1 << (ct->ct_sock);
+ int readfds;
+#endif /* def FD_SETSIZE */
+
+ if (xdrrec_eof(xdrs) == FALSE)
+ return (1);
+
+ tv.tv_sec = 0;
+ if (usecs > 1000000)
+ tv.tv_sec = usecs / 1000000;
+ tv.tv_usec = usecs % 1000000;
+
+ readfds = mask;
+ return (select(_rpc_dtablesize(), &readfds, NULL, NULL, &tv));
+}
--- /dev/null
+#ifndef lint
+static char sfs_cudp_id[] = "@(#)sfs_cudp.c 2.1 97/10/23";
+#endif
+
+/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef FreeBSD
+#include <stropts.h>
+#endif /* ndef FreeBSD */
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+#include "rpc/pmap_clnt.h"
+#include <errno.h>
+#include "rfs_c_def.h" /* Just for the define of RFS */
+/*
+ * UDP bases client side rpc operations
+ */
+static enum clnt_stat sfs_cudp_call(CLIENT *, uint32_t, xdrproc_t,
+ void *, xdrproc_t, void *,
+ struct timeval);
+static void sfs_cudp_abort(CLIENT *h);
+static void sfs_cudp_geterr(CLIENT *, struct rpc_err *);
+static bool_t sfs_cudp_freeres(CLIENT *, xdrproc_t, void *);
+static bool_t sfs_cudp_control(CLIENT *, uint_t, void *);
+static void sfs_cudp_destroy(CLIENT *);
+static bool_t sfs_cudp_getreply(CLIENT *, xdrproc_t, void *,
+ int, uint32_t *, uint32_t *, struct timeval *);
+static int sfs_cudp_poll(CLIENT *, uint32_t);
+
+static struct clnt_ops sfs_cudp_ops = {
+ sfs_cudp_call,
+ sfs_cudp_abort,
+ sfs_cudp_geterr,
+ sfs_cudp_freeres,
+ sfs_cudp_destroy,
+ sfs_cudp_control,
+ sfs_cudp_getreply,
+ sfs_cudp_poll
+};
+
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_sock;
+ bool_t cu_closeit;
+ struct sockaddr_in cu_raddr;
+ int cu_rlen;
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ uint_t cu_xdrpos;
+ uint_t cu_sendsz;
+ char *cu_outbuf;
+ uint_t cu_recvsz;
+ char cu_inbuf[1];
+};
+
+/*
+ * Create a UDP based client handle.
+ * If *sockp<0, *sockp is set to a newly created UPD socket.
+ * If raddr->sin_port is 0 a binder on the remote machine
+ * is consulted for the correct port number.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is initialized to null authentication.
+ * Caller may wish to set this something more useful.
+ *
+ * wait is the amount of time used between retransmitting a call if
+ * no response has been heard; retransmition occurs until the actual
+ * rpc call times out.
+ *
+ * sendsz and recvsz are the maximum allowable packet sizes that can be
+ * sent and received.
+ */
+/* ARGSUSED */
+CLIENT *
+sfs_cudp_bufcreate(
+ struct sockaddr_in *raddr,
+ uint32_t program,
+ uint32_t version,
+ struct timeval wait,
+ int *sockp,
+ uint_t sendsz,
+ uint_t recvsz)
+{
+ CLIENT *cl;
+ struct cu_data *cu;
+ struct timeval now;
+ struct rpc_msg call_msg;
+ int min_buf_sz;
+ int new_buf_sz;
+ int type;
+ int i;
+ int error;
+#if defined(UNIXWARE) || defined(AIX)
+ size_t optlen;
+#else
+ int optlen;
+#endif
+
+ cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
+ if (cl == NULL) {
+ (void) fprintf(stderr, "sfs_cudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ sendsz = ((sendsz + 3) / 4) * 4;
+ recvsz = ((recvsz + 3) / 4) * 4;
+ cu = (struct cu_data *)mem_alloc(sizeof(struct cu_data) +
+ sendsz + recvsz);
+ if (cu == NULL) {
+ (void) fprintf(stderr, "sfs_cudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ cu->cu_outbuf = &cu->cu_inbuf[recvsz];
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (raddr->sin_port == 0) {
+ uint16_t port;
+ if ((port =
+ pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
+ goto fooy;
+ }
+ raddr->sin_port = htons(port);
+ }
+ cl->cl_ops = &sfs_cudp_ops;
+ cl->cl_private = (void *)cu;
+ cu->cu_raddr = *raddr;
+ cu->cu_rlen = sizeof (cu->cu_raddr);
+ cu->cu_sendsz = sendsz;
+ cu->cu_recvsz = recvsz;
+ call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = program;
+ call_msg.rm_call.cb_vers = version;
+ xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
+ sendsz, XDR_ENCODE);
+ if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
+ goto fooy;
+ }
+ cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
+ if (*sockp < 0) {
+#if defined(O_NONBLOCK)
+ int flags;
+#elif defined(FIONBIO)
+ int dontblock = 1;
+#endif
+
+ *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (*sockp < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ /* attempt to bind to prov port */
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ /* the sockets rpc controls are non-blocking */
+#if defined(O_NONBLOCK)
+ flags = fcntl(*sockp, F_GETFL, 0) | O_NONBLOCK;
+ (void)fcntl(*sockp, F_SETFL, flags);
+#elif defined(FIONBIO)
+ (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
+#endif
+ cu->cu_closeit = TRUE;
+ } else {
+ cu->cu_closeit = FALSE;
+ }
+ cu->cu_sock = *sockp;
+ /*
+ * Need to try to size the socket buffers based on the number of
+ * outstanding requests desired. NFS reads and writes can do as
+ * much as 8K per request which can quickly run us out of space
+ * on the socket buffer queue. Use the maximum number of bio style
+ * requests * NFS_MAXDATA plus a pad as a starting point for desired
+ * socket buffer size and then back off by NFS_MAXDATA until the buffer
+ * sizes are successfully set. Note, the algorithm never sets the
+ * buffer size to less than the OS default.
+ */
+ type = SO_SNDBUF;
+ for (i = 0; i < 2; i++) {
+ optlen = sizeof(min_buf_sz);
+#if defined(UNIXWARE)
+ if (getsockopt(cu->cu_sock, SOL_SOCKET, type,
+ (void *)&min_buf_sz, &optlen) < 0) {
+ /* guess the default */
+ min_buf_sz = 18 * 1024;
+ }
+#else
+ if (getsockopt(cu->cu_sock, SOL_SOCKET, type,
+ (char *)&min_buf_sz, &optlen) < 0) {
+ /* guess the default */
+ min_buf_sz = 18 * 1024;
+ }
+#endif
+
+ new_buf_sz = 512 * 1024;
+ if (new_buf_sz > min_buf_sz) {
+ do {
+ error = setsockopt(cu->cu_sock, SOL_SOCKET,
+ type, (char *)&new_buf_sz,
+ sizeof(int));
+ new_buf_sz -= (8 * 1024);
+ } while (error != 0 && new_buf_sz > min_buf_sz);
+ }
+
+ type = SO_RCVBUF;
+ }
+
+ cl->cl_auth = authnone_create();
+ return (cl);
+fooy:
+ if (cu)
+ mem_free((void *)cu, sizeof(struct cu_data) + sendsz + recvsz);
+ if (cl)
+ mem_free((void *)cl, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+CLIENT *
+sfs_cudp_create(
+ struct sockaddr_in *raddr,
+ uint32_t program,
+ uint32_t version,
+ struct timeval wait,
+ int *sockp)
+{
+
+ return(sfs_cudp_bufcreate(raddr, program, version, wait, sockp,
+ UDPMSGSIZE, UDPMSGSIZE));
+}
+
+#ifdef RFS
+enum clnt_stat get_areply_udp (
+ CLIENT * cl,
+ uint32_t *xid,
+ struct timeval *timeout)
+{
+ return get_areply (cl, xid, timeout);
+}
+#endif
+
+static enum clnt_stat
+get_areply(
+ CLIENT *cl,
+ uint32_t *xid,
+ struct timeval *timeout)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ int inlen;
+#if defined(AIX)
+ size_t fromlen;
+#else
+ int fromlen;
+#endif
+ struct sockaddr_in from;
+#ifdef FD_SETSIZE
+ fd_set readfds;
+ fd_set mask;
+#else
+ int readfds;
+ int mask;
+#endif /* def FD_SETSIZE */
+
+#ifdef FD_SETSIZE
+ FD_ZERO(&mask);
+ FD_SET(cu->cu_sock, &mask);
+#else
+ mask = 1 << cu->cu_sock;
+#endif /* def FD_SETSIZE */
+
+ for (;;) {
+ readfds = mask;
+ switch (select(_rpc_dtablesize(), &readfds, NULL,
+ NULL, timeout)) {
+
+ case 0:
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ do {
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
+ (int) cu->cu_recvsz, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+
+ if (inlen < sizeof(uint32_t))
+ continue;
+
+ *xid = ntohl(*((uint32_t *)(cu->cu_inbuf)));
+ return (RPC_SUCCESS);
+ }
+}
+
+enum clnt_stat
+proc_header(
+ CLIENT *cl,
+ xdrproc_t xdr_results,
+ void *results_ptr)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ XDR *xdrs = &(cu->cu_outxdrs);
+ struct rpc_msg reply_msg;
+ XDR reply_xdrs;
+ bool_t ok;
+
+ /*
+ * now decode and validate the response
+ */
+ xdrmem_create(&reply_xdrs, cu->cu_inbuf, cu->cu_recvsz, XDR_DECODE);
+
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = results_ptr;
+ reply_msg.acpted_rply.ar_results.proc = xdr_results;
+
+ ok = xdr_replymsg(&reply_xdrs, &reply_msg);
+ /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
+ if (!ok) {
+ return (cu->cu_error.re_status = RPC_CANTDECODERES);
+ }
+
+ _seterr_reply(&reply_msg, &(cu->cu_error));
+
+ if (cu->cu_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(cl->cl_auth,
+ &reply_msg.acpted_rply.ar_verf)) {
+ cu->cu_error.re_status = RPC_AUTHERROR;
+ cu->cu_error.re_why = AUTH_INVALIDRESP;
+ }
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ }
+
+ return (cu->cu_error.re_status);
+}
+
+/*
+ * Non-standard changes. Make a call an at-most-once with a per call
+ * timer. Ignore the timeout set at creation. Never retransmit.
+ */
+static enum clnt_stat
+sfs_cudp_call(
+ CLIENT *cl, /* client handle */
+ uint32_t proc, /* procedure number */
+ xdrproc_t xargs, /* xdr routine for args */
+ void * argsp, /* pointer to args */
+ xdrproc_t xresults, /* xdr routine for results */
+ void * resultsp, /* pointer to results */
+ struct timeval timeout) /* seconds to wait before giving up */
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ XDR *xdrs = &(cu->cu_outxdrs);
+ int outlen;
+ uint32_t x_id, r_xid;
+
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, cu->cu_xdrpos);
+
+ /*
+ * the transaction is the first thing in the out buffer
+ */
+ (*(uint32_t *)(cu->cu_outbuf))++;
+ x_id = ntohl(*(uint32_t *)(cu->cu_outbuf));
+
+ if ((! XDR_PUTLONG(xdrs, (int32_t *)&proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp)))
+ return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
+
+ outlen = (int)XDR_GETPOS(xdrs);
+
+ if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
+ (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
+ cu->cu_error.re_errno = errno;
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ }
+
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ /*
+ * Double hack, send back xid in results_prt if non-NULL
+ */
+ if (resultsp != NULL)
+ *(uint32_t *)resultsp = x_id;
+
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+
+ /* CONSTCOND */
+ while (TRUE) {
+ enum clnt_stat res;
+
+ if ((res = get_areply(cl, &r_xid, &timeout)) != RPC_SUCCESS)
+ return (res);
+
+ if (r_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ return (proc_header(cl, xresults, resultsp));
+}
+
+static void
+sfs_cudp_geterr(CLIENT *cl, struct rpc_err *errp)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ *errp = cu->cu_error;
+}
+
+
+static bool_t
+sfs_cudp_freeres(
+ CLIENT *cl,
+ xdrproc_t xdr_res,
+ void * res_ptr)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ XDR *xdrs = &(cu->cu_outxdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+/* ARGSUSED */
+static void
+sfs_cudp_abort(CLIENT *h)
+{
+}
+
+static bool_t
+sfs_cudp_control(
+ CLIENT *cl,
+ uint_t request,
+ void *info)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ switch (request) {
+ case CLGET_SERVER_ADDR:
+ *(struct sockaddr_in *)info = cu->cu_raddr;
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+static void
+sfs_cudp_destroy(CLIENT *cl)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ if (cu->cu_closeit) {
+ (void)close(cu->cu_sock);
+ }
+ XDR_DESTROY(&(cu->cu_outxdrs));
+ mem_free((void *)cu, (sizeof(struct cu_data) + cu->cu_sendsz + cu->cu_recvsz));
+ mem_free((void *)cl, sizeof(CLIENT));
+}
+
+/* ARGSUSED */
+static bool_t
+sfs_cudp_getreply(
+ CLIENT *cl,
+ xdrproc_t xproc,
+ void *xres,
+ int cnt,
+ uint32_t *xids,
+ uint32_t *xid,
+ struct timeval *tv)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ bool_t res;
+ int i;
+
+ cu->cu_error.re_status = RPC_SUCCESS;
+
+ if ((res = get_areply(cl, xid, tv)) != RPC_SUCCESS)
+ return (res);
+
+ /*
+ * Check to make sure xid matchs one that we are interested in
+ */
+ for (i = 0; i < cnt; i++) {
+ if (xids[i] == *xid)
+ break;
+ }
+
+ if (i == cnt)
+ return (RPC_CANTDECODERES);
+
+ /*
+ * process header
+ */
+ return (proc_header(cl, xproc, xres));
+}
+
+static int
+sfs_cudp_poll(
+ CLIENT *cl,
+ uint32_t usecs)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ struct timeval tv;
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+
+ FD_ZERO(&mask);
+ FD_SET(cu->cu_sock, &mask);
+#else
+ int mask = 1 << (cu->cu_sock);
+ int readfds;
+#endif /* def FD_SETSIZE */
+
+ tv.tv_sec = 0;
+ if (usecs > 1000000)
+ tv.tv_sec = usecs / 1000000;
+ tv.tv_usec = usecs % 1000000;
+
+ readfds = mask;
+ return (select(_rpc_dtablesize(), &readfds, NULL, NULL, &tv));
+}
--- /dev/null
+#ifndef lint
+static char sfs_svc_c_id[] = "@(#)svc.c 2.1 97/10/23";
+#endif
+/* @(#)svc.c 2.4 88/08/11 4.0 RPCSRC; from 1.44 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc.c 1.41 87/10/13 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc.c, Server-side remote procedure call interface.
+ *
+ * There are two sets of procedures here. The xprt routines are
+ * for handling transport handles. The svc routines handle the
+ * list of service routines.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "rpc/rpc.h"
+#include "rpc/pmap_clnt.h"
+
+#ifdef FD_SETSIZE
+static SVCXPRT **xports;
+#else
+#define NOFILE 32
+
+static SVCXPRT *xports[NOFILE];
+#endif /* def FD_SETSIZE */
+
+#define NULL_SVC ((struct svc_callout *)0)
+#define RQCRED_SIZE 400 /* this size is excessive */
+
+/*
+ * The services list
+ * Each entry represents a set of procedures (an rpc program).
+ * The dispatch routine takes request structs and runs the
+ * apropriate procedure.
+ */
+static struct svc_callout {
+ struct svc_callout *sc_next;
+ uint32_t sc_prog;
+ uint32_t sc_vers;
+ void (*sc_dispatch)();
+} *svc_head;
+
+static struct svc_callout *svc_find();
+
+/* *************** SVCXPRT related stuff **************** */
+
+/*
+ * Activate a transport handle.
+ */
+void
+xprt_register(
+ SVCXPRT *xprt)
+{
+ int sock = xprt->xp_sock;
+
+#ifdef FD_SETSIZE
+ if (xports == NULL) {
+ xports = (SVCXPRT **)
+ mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *));
+ }
+ if (sock < _rpc_dtablesize()) {
+ xports[sock] = xprt;
+ FD_SET(sock, &svc_fdset);
+ }
+#else
+ if (sock < NOFILE) {
+ xports[sock] = xprt;
+ svc_fds |= (1 << sock);
+ }
+#endif /* def FD_SETSIZE */
+
+}
+
+/*
+ * De-activate a transport handle.
+ */
+void
+xprt_unregister(
+ SVCXPRT *xprt)
+{
+ int sock = xprt->xp_sock;
+
+#ifdef FD_SETSIZE
+ if ((sock < _rpc_dtablesize()) && (xports[sock] == xprt)) {
+ xports[sock] = (SVCXPRT *)0;
+ FD_CLR(sock, &svc_fdset);
+ }
+#else
+ if ((sock < NOFILE) && (xports[sock] == xprt)) {
+ xports[sock] = (SVCXPRT *)0;
+ svc_fds &= ~(1 << sock);
+ }
+#endif /* def FD_SETSIZE */
+}
+
+
+/* ********************** CALLOUT list related stuff ************* */
+
+/*
+ * Add a service program to the callout list.
+ * The dispatch routine will be called when a rpc request for this
+ * program number comes in.
+ */
+bool_t
+svc_register(
+ SVCXPRT *xprt,
+ uint32_t prog,
+ uint32_t vers,
+ void (*dispatch)(),
+ int protocol)
+{
+ struct svc_callout *prev;
+ struct svc_callout *s;
+
+ if ((s = svc_find(prog, vers, &prev)) != NULL_SVC) {
+ if (s->sc_dispatch == dispatch)
+ goto pmap_it; /* he is registering another xptr */
+ return (FALSE);
+ }
+ s = (struct svc_callout *)mem_alloc(sizeof(struct svc_callout));
+ if (s == (struct svc_callout *)0) {
+ return (FALSE);
+ }
+ s->sc_prog = prog;
+ s->sc_vers = vers;
+ s->sc_dispatch = dispatch;
+ s->sc_next = svc_head;
+ svc_head = s;
+pmap_it:
+ /* now register the information with the local binder service */
+ if (protocol) {
+ return (pmap_set(prog, vers, protocol, xprt->xp_port));
+ }
+ return (TRUE);
+}
+
+/*
+ * Remove a service program from the callout list.
+ */
+void
+svc_unregister(
+ uint32_t prog,
+ uint32_t vers)
+{
+ struct svc_callout *prev;
+ struct svc_callout *s;
+
+ if ((s = svc_find(prog, vers, &prev)) == NULL_SVC)
+ return;
+ if (prev == NULL_SVC) {
+ svc_head = s->sc_next;
+ } else {
+ prev->sc_next = s->sc_next;
+ }
+ s->sc_next = NULL_SVC;
+ mem_free((char *) s, (uint_t) sizeof(struct svc_callout));
+ /* now unregister the information with the local binder service */
+ (void)pmap_unset(prog, vers);
+}
+
+/*
+ * Search the callout list for a program number, return the callout
+ * struct.
+ */
+static struct svc_callout *
+svc_find(
+ uint32_t prog,
+ uint32_t vers,
+ struct svc_callout **prev)
+{
+ struct svc_callout *s, *p;
+
+ p = NULL_SVC;
+ for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
+ if ((s->sc_prog == prog) && (s->sc_vers == vers))
+ goto done;
+ p = s;
+ }
+done:
+ *prev = p;
+ return (s);
+}
+
+/* ******************* REPLY GENERATION ROUTINES ************ */
+
+/*
+ * Send a reply to an rpc request
+ */
+bool_t
+svc_sendreply(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_results,
+ void *xdr_location)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = SUCCESS;
+ rply.acpted_rply.ar_results.where = xdr_location;
+ rply.acpted_rply.ar_results.proc = xdr_results;
+ return (SVC_REPLY(xprt, &rply));
+}
+
+/*
+ * No procedure error reply
+ */
+void
+svcerr_noproc(
+ SVCXPRT *xprt)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROC_UNAVAIL;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Can't decode args error reply
+ */
+void
+svcerr_decode(
+ SVCXPRT *xprt)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = GARBAGE_ARGS;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Some system error
+ */
+void
+svcerr_systemerr(
+ SVCXPRT *xprt)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = SYSTEM_ERR;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Authentication error reply
+ */
+void
+svcerr_auth(
+ SVCXPRT *xprt,
+ enum auth_stat why)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_DENIED;
+ rply.rjcted_rply.rj_stat = AUTH_ERROR;
+ rply.rjcted_rply.rj_why = why;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Auth too weak error reply
+ */
+void
+svcerr_weakauth(
+ SVCXPRT *xprt)
+{
+
+ svcerr_auth(xprt, AUTH_TOOWEAK);
+}
+
+/*
+ * Program unavailable error reply
+ */
+void
+svcerr_noprog(
+ SVCXPRT *xprt)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROG_UNAVAIL;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Program version mismatch error reply
+ */
+void
+svcerr_progvers(
+ SVCXPRT *xprt,
+ uint32_t low_vers,
+ uint32_t high_vers)
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROG_MISMATCH;
+ rply.acpted_rply.ar_vers.low = low_vers;
+ rply.acpted_rply.ar_vers.high = high_vers;
+ SVC_REPLY(xprt, &rply);
+}
+
+/* ******************* SERVER INPUT STUFF ******************* */
+
+/*
+ * Get server side input from some transport.
+ *
+ * Statement of authentication parameters management:
+ * This function owns and manages all authentication parameters, specifically
+ * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
+ * the "cooked" credentials (rqst->rq_clntcred).
+ * However, this function does not know the structure of the cooked
+ * credentials, so it make the following assumptions:
+ * a) the structure is contiguous (no pointers), and
+ * b) the cred structure size does not exceed RQCRED_SIZE bytes.
+ * In all events, all three parameters are freed upon exit from this routine.
+ * The storage is trivially management on the call stack in user land, but
+ * is mallocated in kernel land.
+ */
+
+void
+svc_getreqset(
+#ifdef FD_SETSIZE
+ fd_set *readfds)
+{
+#else
+ int *readfds)
+{
+ int readfds_local = *readfds;
+#endif /* def FD_SETSIZE */
+ enum xprt_stat stat;
+ struct rpc_msg msg;
+ int prog_found;
+ uint32_t low_vers;
+ uint32_t high_vers;
+ struct svc_req r;
+ SVCXPRT *xprt;
+ int setsize;
+ int sock;
+ char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
+ msg.rm_call.cb_cred.oa_base = cred_area;
+ msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
+ r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
+
+
+#ifdef FD_SETSIZE
+ setsize = _rpc_dtablesize();
+ for (sock = 0; sock < setsize; sock++) {
+ if (FD_ISSET(sock, readfds)) {
+#else
+ for (sock = 0; readfds_local != 0; sock++, readfds_local >>= 1) {
+ if ((readfds_local & 1) != 0) {
+#endif /* def FD_SETSIZE */
+ /* sock has input waiting */
+ xprt = xports[sock];
+ /* now receive msgs from xprtprt (support batch calls) */
+ do {
+ if (SVC_RECV(xprt, &msg)) {
+
+ /* now find the exported program and call it */
+ struct svc_callout *s;
+ enum auth_stat why;
+
+ r.rq_xprt = xprt;
+ r.rq_prog = msg.rm_call.cb_prog;
+ r.rq_vers = msg.rm_call.cb_vers;
+ r.rq_proc = msg.rm_call.cb_proc;
+ r.rq_cred = msg.rm_call.cb_cred;
+ /* first authenticate the message */
+ if ((why= _authenticate(&r, &msg)) != AUTH_OK) {
+ svcerr_auth(xprt, why);
+ goto call_done;
+ }
+ /* now match message with a registered service*/
+ prog_found = FALSE;
+ low_vers = 0 - 1;
+ high_vers = 0;
+ for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
+ if (s->sc_prog == r.rq_prog) {
+ if (s->sc_vers == r.rq_vers) {
+ (*s->sc_dispatch)(&r, xprt);
+ goto call_done;
+ } /* found correct version */
+ prog_found = TRUE;
+ if (s->sc_vers < low_vers)
+ low_vers = s->sc_vers;
+ if (s->sc_vers > high_vers)
+ high_vers = s->sc_vers;
+ } /* found correct program */
+ }
+ /*
+ * if we got here, the program or version
+ * is not served ...
+ */
+ if (prog_found)
+ svcerr_progvers(xprt,
+ low_vers, high_vers);
+ else
+ svcerr_noprog(xprt);
+ /* Fall through to ... */
+ }
+ call_done:
+ if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
+ SVC_DESTROY(xprt);
+ break;
+ }
+ } while (stat == XPRT_MOREREQS);
+ }
+ }
+}
--- /dev/null
+/*
+ * @(#)svc.h 2.1 97/10/23
+ */
+/* @(#)svc.h 2.2 88/07/29 4.0 RPCSRC; from 1.20 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * svc.h, Server-side remote procedure call interface.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#ifndef __SVC_HEADER__
+#define __SVC_HEADER__
+
+#include "rpc/rpc_msg.h"
+#include "rpc/xdr.h"
+
+/*
+ * This interface must manage two items concerning remote procedure calling:
+ *
+ * 1) An arbitrary number of transport connections upon which rpc requests
+ * are received. The two most notable transports are TCP and UDP; they are
+ * created and registered by routines in svc_tcp.c and svc_udp.c, respectively;
+ * they in turn call xprt_register and xprt_unregister.
+ *
+ * 2) An arbitrary number of locally registered services. Services are
+ * described by the following four data: program number, version number,
+ * "service dispatch" function, a transport handle, and a boolean that
+ * indicates whether or not the exported program should be registered with a
+ * local binder service; if true the program's number and version and the
+ * port number from the transport handle are registered with the binder.
+ * These data are registered with the rpc svc system via svc_register.
+ *
+ * A service's dispatch function is called whenever an rpc request comes in
+ * on a transport. The request's program and version numbers must match
+ * those of the registered service. The dispatch function is passed two
+ * parameters, struct svc_req * and SVCXPRT *, defined below.
+ */
+
+enum xprt_stat {
+ XPRT_DIED,
+ XPRT_MOREREQS,
+ XPRT_IDLE
+};
+
+/*
+ * Server side transport handle
+ */
+typedef struct SVCXPRT {
+ int xp_sock;
+ uint16_t xp_port; /* associated port number */
+ struct xp_ops *xp_ops;
+#if defined(AIX)
+ size_t xp_addrlen; /* length of remote address */
+#else
+ int xp_addrlen; /* length of remote address */
+#endif
+ struct sockaddr_in xp_raddr; /* remote address */
+ struct opaque_auth xp_verf; /* raw response verifier */
+ void * xp_p1; /* private */
+ void * xp_p2; /* private */
+} SVCXPRT;
+
+struct xp_ops {
+ bool_t (*xp_recv)(SVCXPRT *, struct rpc_msg *);
+ enum xprt_stat (*xp_stat)(SVCXPRT *);
+ bool_t (*xp_getargs)(SVCXPRT *, xdrproc_t, void *);
+ bool_t (*xp_reply)(SVCXPRT *, struct rpc_msg *);
+ bool_t (*xp_freeargs)(SVCXPRT *, xdrproc_t, void *);
+ void (*xp_destroy)(SVCXPRT *);
+};
+/*
+ * Approved way of getting address of caller
+ */
+#define svc_getcaller(x) (&(x)->xp_raddr)
+
+/*
+ * Operations defined on an SVCXPRT handle
+ *
+ * SVCXPRT *xprt;
+ * struct rpc_msg *msg;
+ * xdrproc_t xargs;
+ * void * argsp;
+ */
+#define SVC_RECV(xprt, msg) \
+ (*(xprt)->xp_ops->xp_recv)((xprt), (msg))
+#define svc_recv(xprt, msg) \
+ (*(xprt)->xp_ops->xp_recv)((xprt), (msg))
+
+#define SVC_STAT(xprt) \
+ (*(xprt)->xp_ops->xp_stat)(xprt)
+#define svc_stat(xprt) \
+ (*(xprt)->xp_ops->xp_stat)(xprt)
+
+#define SVC_GETARGS(xprt, xargs, argsp) \
+ (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp))
+#define svc_getargs(xprt, xargs, argsp) \
+ (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp))
+
+#define SVC_REPLY(xprt, msg) \
+ (*(xprt)->xp_ops->xp_reply) ((xprt), (msg))
+#define svc_reply(xprt, msg) \
+ (*(xprt)->xp_ops->xp_reply) ((xprt), (msg))
+
+#define SVC_FREEARGS(xprt, xargs, argsp) \
+ (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp))
+#define svc_freeargs(xprt, xargs, argsp) \
+ (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp))
+
+#define SVC_DESTROY(xprt) \
+ (*(xprt)->xp_ops->xp_destroy)(xprt)
+#define svc_destroy(xprt) \
+ (*(xprt)->xp_ops->xp_destroy)(xprt)
+
+
+/*
+ * Service request
+ */
+struct svc_req {
+ uint32_t rq_prog; /* service program number */
+ uint32_t rq_vers; /* service protocol version */
+ uint32_t rq_proc; /* the desired procedure */
+ struct opaque_auth rq_cred; /* raw creds from the wire */
+ void * rq_clntcred; /* read only cooked cred */
+ SVCXPRT *rq_xprt; /* associated transport */
+};
+
+
+/*
+ * Service registration
+ *
+ * svc_register(xprt, prog, vers, dispatch, protocol)
+ * SVCXPRT *xprt;
+ * uint32_t prog;
+ * uint32_t vers;
+ * void (*dispatch)();
+ * int protocol;
+ */
+extern bool_t svc_register(SVCXPRT *, uint32_t, uint32_t, void (*)(), int);
+
+/*
+ * Service un-registration
+ *
+ * svc_unregister(prog, vers)
+ * uint32_t prog;
+ * uint32_t vers;
+ */
+extern void svc_unregister(uint32_t, uint32_t);
+
+/*
+ * Transport registration.
+ *
+ * xprt_register(xprt)
+ * SVCXPRT *xprt;
+ */
+extern void xprt_register(SVCXPRT *);
+
+/*
+ * Transport un-register
+ *
+ * xprt_unregister(xprt)
+ * SVCXPRT *xprt;
+ */
+extern void xprt_unregister(SVCXPRT *);
+
+
+
+
+/*
+ * When the service routine is called, it must first check to see if it
+ * knows about the procedure; if not, it should call svcerr_noproc
+ * and return. If so, it should deserialize its arguments via
+ * SVC_GETARGS (defined above). If the deserialization does not work,
+ * svcerr_decode should be called followed by a return. Successful
+ * decoding of the arguments should be followed the execution of the
+ * procedure's code and a call to svc_sendreply.
+ *
+ * Also, if the service refuses to execute the procedure due to too-
+ * weak authentication parameters, svcerr_weakauth should be called.
+ * Note: do not confuse access-control failure with weak authentication!
+ *
+ * NB: In pure implementations of rpc, the caller always waits for a reply
+ * msg. This message is sent when svc_sendreply is called.
+ * Therefore pure service implementations should always call
+ * svc_sendreply even if the function logically returns void; use
+ * xdr.h - xdr_void for the xdr routine. HOWEVER, tcp based rpc allows
+ * for the abuse of pure rpc via batched calling or pipelining. In the
+ * case of a batched call, svc_sendreply should NOT be called since
+ * this would send a return message, which is what batching tries to avoid.
+ * It is the service/protocol writer's responsibility to know which calls are
+ * batched and which are not. Warning: responding to batch calls may
+ * deadlock the caller and server processes!
+ */
+
+extern bool_t svc_sendreply(SVCXPRT *, xdrproc_t, void *);
+extern void svcerr_decode(SVCXPRT *);
+extern void svcerr_weakauth(SVCXPRT *);
+extern void svcerr_noproc(SVCXPRT *);
+extern void svcerr_progvers(SVCXPRT *, uint32_t, uint32_t);
+extern void svcerr_auth(SVCXPRT *, enum auth_stat);
+extern void svcerr_noprog(SVCXPRT *);
+extern void svcerr_systemerr(SVCXPRT *);
+
+/*
+ * Lowest level dispatching -OR- who owns this process anyway.
+ * Somebody has to wait for incoming requests and then call the correct
+ * service routine. The routine svc_run does infinite waiting; i.e.,
+ * svc_run never returns.
+ * Since another (co-existant) package may wish to selectively wait for
+ * incoming calls or other events outside of the rpc architecture, the
+ * routine svc_getreq is provided. It must be passed readfds, the
+ * "in-place" results of a select system call (see select, section 2).
+ */
+
+/*
+ * Global keeper of rpc service descriptors in use
+ * dynamic; must be inspected before each call to select
+ */
+#ifdef FD_SETSIZE
+extern fd_set svc_fdset;
+#define svc_fds svc_fdset.fds_bits[0] /* compatibility */
+#else
+extern int svc_fds;
+#endif /* def FD_SETSIZE */
+
+/*
+ * a small program implemented by the svc_rpc implementation itself;
+ * also see clnt.h for protocol numbers.
+ */
+extern void rpctest_service();
+
+extern void svc_getreq(int);
+#ifdef FD_SETSIZE
+extern void svc_getreqset(fd_set *);/* takes fdset instead of int */
+#else
+extern void svc_getreqset(int *);
+#endif
+extern void svc_run(void); /* never returns */
+
+/*
+ * Socket to use on svcxxx_create call to get default socket
+ */
+#define RPC_ANYSOCK -1
+
+/*
+ * These are the existing service side transport implementations
+ */
+
+/*
+ * Memory based rpc for testing and timing.
+ */
+extern SVCXPRT *svcraw_create(void);
+
+/*
+ * Udp based rpc.
+ */
+extern SVCXPRT *svcudp_create(int);
+extern SVCXPRT *svcudp_bufcreate(int, uint_t, uint_t);
+
+/*
+ * Tcp based rpc.
+ */
+extern SVCXPRT *svctcp_create(int, uint_t, uint_t);
+
+extern int svcudp_enablecache(SVCXPRT *, uint32_t);
+
+
+#endif /* !__SVC_HEADER__ */
--- /dev/null
+#ifndef lint
+static char sfs_svc_auth_id[] = "@(#)svc_auth.c 2.1 97/10/23";
+#endif
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC; from 1.19 87/08/11 Copyr 1984 Sun Micro";
+#endif
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * svc_auth_nodes.c, Server-side rpc authenticator interface,
+ * *WITHOUT* DES authentication.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include "rpc/rpc.h"
+
+/*
+ * svcauthsw is the bdevsw of server side authentication.
+ *
+ * Server side authenticators are called from authenticate by
+ * using the client auth struct flavor field to index into svcauthsw.
+ * The server auth flavors must implement a routine that looks
+ * like:
+ *
+ * enum auth_stat
+ * flavorx_auth(rqst, msg)
+ * register struct svc_req *rqst;
+ * register struct rpc_msg *msg;
+ *
+ */
+
+extern enum auth_stat _svcauth_null(struct svc_req *, struct rpc_msg *);
+extern enum auth_stat _svcauth_unix(struct svc_req *, struct rpc_msg *);
+extern enum auth_stat _svcauth_short(struct svc_req *, struct rpc_msg *);
+
+static struct {
+ enum auth_stat (*authenticator)(struct svc_req *, struct rpc_msg *);
+} svcauthsw[] = {
+ _svcauth_null, /* AUTH_NULL */
+ _svcauth_unix, /* AUTH_UNIX */
+ _svcauth_short, /* AUTH_SHORT */
+};
+#define AUTH_MAX 2 /* HIGHEST AUTH NUMBER */
+
+
+/*
+ * The call rpc message, msg has been obtained from the wire. The msg contains
+ * the raw form of credentials and verifiers. authenticate returns AUTH_OK
+ * if the msg is successfully authenticated. If AUTH_OK then the routine also
+ * does the following things:
+ * set rqst->rq_xprt->verf to the appropriate response verifier;
+ * sets rqst->rq_client_cred to the "cooked" form of the credentials.
+ *
+ * NB: rqst->rq_cxprt->verf must be pre-alloctaed;
+ * its length is set appropriately.
+ *
+ * The caller still owns and is responsible for msg->u.cmb.cred and
+ * msg->u.cmb.verf. The authentication system retains ownership of
+ * rqst->rq_client_cred, the cooked credentials.
+ *
+ * There is an assumption that any flavour less than AUTH_NULL is
+ * invalid.
+ */
+enum auth_stat
+_authenticate(
+ struct svc_req *rqst,
+ struct rpc_msg *msg)
+{
+ register int cred_flavor;
+
+ rqst->rq_cred = msg->rm_call.cb_cred;
+ rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ cred_flavor = rqst->rq_cred.oa_flavor;
+ if ((cred_flavor <= AUTH_MAX) && (cred_flavor >= AUTH_NULL)) {
+ return ((*(svcauthsw[cred_flavor].authenticator))(rqst, msg));
+ }
+
+ return (AUTH_REJECTEDCRED);
+}
+
+/* ARGSUSED */
+enum auth_stat
+_svcauth_null(
+ struct svc_req *rqst,
+ struct rpc_msg *msg)
+{
+
+ return (AUTH_OK);
+}
--- /dev/null
+/*
+ * @(#)svc_auth.h 2.1 97/10/23
+ */
+/* @(#)svc_auth.h 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)svc_auth.h 1.6 86/07/16 SMI */
+
+/*
+ * svc_auth.h, Service side of rpc authentication.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+/*
+ * Server side authenticator
+ */
+extern enum auth_stat _authenticate();
--- /dev/null
+#ifndef lint
+static char sfs_svc_auth_unix_id[] = "@(#)svc_auth_unix.c 2.1 97/10/23";
+#endif
+/* @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC; from 1.28 88/02/08 SMI */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Manassas, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_auth_unix.c
+ * Handles UNIX flavor authentication parameters on the service side of rpc.
+ * There are two svc auth implementations here: AUTH_UNIX and AUTH_SHORT.
+ * _svcauth_unix does full blown unix style uid,gid+gids auth,
+ * _svcauth_short uses a shorthand auth to index into a cache of longhand auths.
+ * Note: the shorthand has been gutted for efficiency.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "rpc/rpc.h"
+
+/*
+ * Unix longhand authenticator
+ */
+enum auth_stat
+_svcauth_unix(
+ struct svc_req *rqst,
+ struct rpc_msg *msg)
+{
+ enum auth_stat stat;
+ XDR xdrs;
+ struct authunix_parms *aup;
+ int32_t *buf;
+ struct area {
+ struct authunix_parms area_aup;
+ char area_machname[MAX_MACHINE_NAME+1];
+ int area_gids[NGRPS];
+ } *area;
+ uint_t auth_len;
+ int str_len, gid_len;
+ int i;
+
+ area = (struct area *) rqst->rq_clntcred;
+ aup = &area->area_aup;
+ aup->aup_machname = area->area_machname;
+ aup->aup_gids = area->area_gids;
+ auth_len = (uint_t)msg->rm_call.cb_cred.oa_length;
+ xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len,XDR_DECODE);
+ buf = XDR_INLINE(&xdrs, auth_len);
+ if (buf != NULL) {
+ aup->aup_time = IXDR_GET_LONG(buf);
+ str_len = IXDR_GET_U_LONG(buf);
+ if (str_len > MAX_MACHINE_NAME) {
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ memmove(aup->aup_machname, (void *)buf, (uint_t)str_len);
+ aup->aup_machname[str_len] = 0;
+ str_len = RNDUP(str_len);
+ buf += str_len / sizeof (int32_t);
+ aup->aup_uid = IXDR_GET_LONG(buf);
+ aup->aup_gid = IXDR_GET_LONG(buf);
+ gid_len = IXDR_GET_U_LONG(buf);
+ if (gid_len > NGRPS) {
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ aup->aup_len = gid_len;
+ for (i = 0; i < gid_len; i++) {
+ aup->aup_gids[i] = IXDR_GET_LONG(buf);
+ }
+ /*
+ * five is the smallest unix credentials structure -
+ * timestamp, hostname len (0), uid, gid, and gids len (0).
+ */
+ if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) {
+ (void) printf("bad auth_len gid %d str %d auth %d\n",
+ gid_len, str_len, auth_len);
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ } else if (! xdr_authunix_parms(&xdrs, aup)) {
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_authunix_parms(&xdrs, aup);
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ stat = AUTH_OK;
+done:
+ XDR_DESTROY(&xdrs);
+ return (stat);
+}
+
+
+/*
+ * Shorthand unix authenticator
+ * Looks up longhand in a cache.
+ */
+/*ARGSUSED*/
+enum auth_stat
+_svcauth_short(
+ struct svc_req *rqst,
+ struct rpc_msg *msg)
+{
+ return (AUTH_REJECTEDCRED);
+}
--- /dev/null
+#ifndef lint
+static char sfs_svc_raw_id[] = "@(#)svc_raw.c 2.1 97/10/23";
+#endif
+/* @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_raw.c, This a toy for simple testing and timing.
+ * Interface to create an rpc client and server in the same UNIX process.
+ * This lets us similate rpc and get rpc (round trip) overhead, without
+ * any interference from the kernal.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "rpc/rpc.h"
+
+
+/*
+ * This is the "network" that we will be moving data over
+ */
+static struct svcraw_private {
+ char _raw_buf[UDPMSGSIZE];
+ SVCXPRT server;
+ XDR xdr_stream;
+ char verf_body[MAX_AUTH_BYTES];
+} *svcraw_private;
+
+static bool_t svcraw_recv(SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svcraw_stat(SVCXPRT *);
+static bool_t svcraw_getargs(SVCXPRT *, xdrproc_t, void *);
+static bool_t svcraw_reply(SVCXPRT *, struct rpc_msg *);
+static bool_t svcraw_freeargs(SVCXPRT *, xdrproc_t, void *);
+static void svcraw_destroy(SVCXPRT *);
+
+static struct xp_ops server_ops = {
+ svcraw_recv,
+ svcraw_stat,
+ svcraw_getargs,
+ svcraw_reply,
+ svcraw_freeargs,
+ svcraw_destroy
+};
+
+SVCXPRT *
+svcraw_create(void)
+{
+ struct svcraw_private *srp = svcraw_private;
+
+ if (srp == 0) {
+ srp = (struct svcraw_private *)calloc(1, sizeof (struct svcraw_private));
+ if (srp == 0)
+ return (0);
+ }
+ srp->server.xp_sock = 0;
+ srp->server.xp_port = 0;
+ srp->server.xp_ops = &server_ops;
+ srp->server.xp_verf.oa_base = srp->verf_body;
+ xdrmem_create(&srp->xdr_stream, srp->_raw_buf, UDPMSGSIZE, XDR_FREE);
+ return (&srp->server);
+}
+
+/* ARGSUSED */
+static enum xprt_stat
+svcraw_stat(SVCXPRT *x)
+{
+
+ return (XPRT_IDLE);
+}
+
+/* ARGSUSED */
+static bool_t
+svcraw_recv(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct svcraw_private *srp = svcraw_private;
+ XDR *xdrs;
+
+ if (srp == 0)
+ return (0);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_DECODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_callmsg(xdrs, msg))
+ return (FALSE);
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+svcraw_reply(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct svcraw_private *srp = svcraw_private;
+ XDR *xdrs;
+
+ if (srp == 0)
+ return (FALSE);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_replymsg(xdrs, msg))
+ return (FALSE);
+ (void)XDR_GETPOS(xdrs); /* called just for overhead */
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+svcraw_getargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void *args_ptr)
+{
+ struct svcraw_private *srp = svcraw_private;
+
+ if (srp == 0)
+ return (FALSE);
+ return ((*xdr_args)(&srp->xdr_stream, args_ptr));
+}
+
+/* ARGSUSED */
+static bool_t
+svcraw_freeargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void *args_ptr)
+{
+ struct svcraw_private *srp = svcraw_private;
+ XDR *xdrs;
+
+ if (srp == 0)
+ return (FALSE);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+/* ARGSUSED */
+static void
+svcraw_destroy(SVCXPRT *x)
+{
+}
--- /dev/null
+#ifndef lint
+static char sfs_svc_run_id[] = "@(#)svc_run.c 2.1 97/10/23";
+#endif
+/* @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * This is the rpc server side idle loop
+ * Wait for input, call server program.
+ */
+#include "rpc/rpc.h"
+#include <errno.h>
+
+void
+svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int errno;
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL,
+ NULL)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ }
+ }
+}
--- /dev/null
+#ifndef lint
+static char sfs_svc_simple_id[] = "@(#)svc_simple.c 2.1 97/10/23";
+#endif
+/* @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_simple.c
+ * Simplified front end to rpc.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <netdb.h>
+
+static struct proglst {
+ char *(*p_progname)();
+ int p_prognum;
+ int p_procnum;
+ xdrproc_t p_inproc, p_outproc;
+ struct proglst *p_nxt;
+} *proglst;
+static void universal(struct svc_req *, SVCXPRT *);
+static SVCXPRT *transp;
+
+registerrpc(
+ int prognum,
+ int versnum,
+ int procnum,
+ char *(*progname)(),
+ xdrproc_t inproc,
+ xdrproc_t outproc)
+{
+ struct proglst *pl;
+
+ if (procnum == NULLPROC) {
+ (void) fprintf(stderr,
+ "can't reassign procedure number %d\n", NULLPROC);
+ return (-1);
+ }
+ if (transp == 0) {
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ (void) fprintf(stderr, "couldn't create an rpc server\n");
+ return (-1);
+ }
+ }
+ (void) pmap_unset((uint32_t)prognum, (uint32_t)versnum);
+ if (!svc_register(transp, (uint32_t)prognum, (uint32_t)versnum,
+ universal, IPPROTO_UDP)) {
+ (void) fprintf(stderr, "couldn't register prog %d vers %d\n",
+ prognum, versnum);
+ return (-1);
+ }
+ pl = (struct proglst *)malloc(sizeof(struct proglst));
+ if (pl == NULL) {
+ (void) fprintf(stderr, "registerrpc: out of memory\n");
+ return (-1);
+ }
+ pl->p_progname = progname;
+ pl->p_prognum = prognum;
+ pl->p_procnum = procnum;
+ pl->p_inproc = inproc;
+ pl->p_outproc = outproc;
+ pl->p_nxt = proglst;
+ proglst = pl;
+ return (0);
+}
+
+static void
+universal(
+ struct svc_req *rqstp,
+ SVCXPRT *transp)
+{
+ int prog, proc;
+ char *outdata;
+ char xdrbuf[UDPMSGSIZE];
+ struct proglst *pl;
+
+ /*
+ * enforce "procnum 0 is echo" convention
+ */
+ if (rqstp->rq_proc == NULLPROC) {
+ if (svc_sendreply(transp, xdr_void, (char *)NULL) == FALSE) {
+ (void) fprintf(stderr, "xxx\n");
+ exit(1);
+ }
+ return;
+ }
+ prog = rqstp->rq_prog;
+ proc = rqstp->rq_proc;
+ for (pl = proglst; pl != NULL; pl = pl->p_nxt)
+ if (pl->p_prognum == prog && pl->p_procnum == proc) {
+ /* decode arguments into a CLEAN buffer */
+ memset(xdrbuf, '\0', sizeof(xdrbuf)); /* required ! */
+ if (!svc_getargs(transp, pl->p_inproc, xdrbuf)) {
+ svcerr_decode(transp);
+ return;
+ }
+ outdata = (*(pl->p_progname))(xdrbuf);
+ if (outdata == NULL && pl->p_outproc != xdr_void)
+ /* there was an error */
+ return;
+ if (!svc_sendreply(transp, pl->p_outproc, outdata)) {
+ (void) fprintf(stderr,
+ "trouble replying to prog %d\n",
+ pl->p_prognum);
+ exit(1);
+ }
+ /* free the decoded arguments */
+ (void)svc_freeargs(transp, pl->p_inproc, xdrbuf);
+ return;
+ }
+ (void) fprintf(stderr, "never registered prog %d\n", prog);
+ exit(1);
+}
+
--- /dev/null
+#ifndef lint
+static char sfs_svc_tcp_id[] = "@(#)svc_tcp.c 2.1 97/10/23";
+#endif
+/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_tcp.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a tcp rendezvouser (a listner and connection establisher)
+ * and a record/tcp stream.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <errno.h>
+
+/*
+ * Ops vector for TCP/IP based rpc service handle
+ */
+static bool_t svctcp_recv(SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svctcp_stat(SVCXPRT *);
+static bool_t svctcp_getargs(SVCXPRT *, xdrproc_t, void *);
+static bool_t svctcp_reply(SVCXPRT *, struct rpc_msg *);
+static bool_t svctcp_freeargs(SVCXPRT *, xdrproc_t, void *);
+static void svctcp_destroy(SVCXPRT *);
+static bool_t svctcp_abortrop(SVCXPRT *, struct rpc_msg *);
+static bool_t svctcp_abortgop(SVCXPRT *, xdrproc_t, void *);
+
+static struct xp_ops svctcp_op = {
+ svctcp_recv,
+ svctcp_stat,
+ svctcp_getargs,
+ svctcp_reply,
+ svctcp_freeargs,
+ svctcp_destroy
+};
+
+/*
+ * Ops vector for TCP/IP rendezvous handler
+ */
+static bool_t rendezvous_request(SVCXPRT *xprt, struct rpc_msg *);
+static enum xprt_stat rendezvous_stat(SVCXPRT *);
+
+static struct xp_ops svctcp_rendezvous_op = {
+ rendezvous_request,
+ rendezvous_stat,
+ svctcp_abortgop,
+ svctcp_abortrop,
+ svctcp_abortgop,
+ svctcp_destroy
+};
+
+static int readtcp(), writetcp();
+static int readtcp(SVCXPRT *, char *, int);
+static int writetcp(SVCXPRT *, char *, int);
+static SVCXPRT *makefd_xprt(int, uint_t, uint_t);
+
+struct tcp_rendezvous { /* kept in xprt->xp_p1 */
+ uint_t sendsize;
+ uint_t recvsize;
+};
+
+struct tcp_conn { /* kept in xprt->xp_p1 */
+ enum xprt_stat strm_stat;
+ uint32_t x_id;
+ XDR xdrs;
+ char verf_body[MAX_AUTH_BYTES];
+};
+
+/*
+ * Usage:
+ * xprt = svctcp_create(sock, send_buf_size, recv_buf_size);
+ *
+ * Creates, registers, and returns a (rpc) tcp based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register). This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svctcp_create
+ * binds it to an arbitrary port. The routine then starts a tcp
+ * listener on the socket's associated port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since tcp streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svctcp_create(
+ int sock,
+ uint_t sendsize,
+ uint_t recvsize)
+{
+ bool_t madesock = FALSE;
+ SVCXPRT *xprt;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+#if defined(AIX)
+ size_t len;
+#else
+ int len;
+#endif /* AIX */
+
+ len = sizeof(struct sockaddr_in);
+
+ if (sock == RPC_ANYSOCK) {
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ perror("svctcp_.c - udp socket creation problem");
+ return ((SVCXPRT *)NULL);
+ }
+ madesock = TRUE;
+ }
+ memset((char *)&addr, '\0', sizeof (addr));
+ addr.sin_family = AF_INET;
+ if (bindresvport(sock, &addr)) {
+ addr.sin_port = 0;
+ (void)bind(sock, (struct sockaddr *)&addr, len);
+ }
+ if ((getsockname(sock, (struct sockaddr *)&addr, &len) != 0) ||
+ (listen(sock, 2) != 0)) {
+ perror("svctcp_.c - cannot getsockname or listen");
+ if (madesock)
+ (void)close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ r = (struct tcp_rendezvous *)mem_alloc(sizeof(struct tcp_rendezvous));
+ if (r == NULL) {
+ (void) fprintf(stderr, "svctcp_create: out of memory\n");
+ return (NULL);
+ }
+ r->sendsize = sendsize;
+ r->recvsize = recvsize;
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == NULL) {
+ (void) fprintf(stderr, "svctcp_create: out of memory\n");
+ return (NULL);
+ }
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (void *)r;
+ xprt->xp_verf = _null_auth;
+ xprt->xp_ops = &svctcp_rendezvous_op;
+ xprt->xp_port = ntohs(addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register(xprt);
+ return (xprt);
+}
+
+/*
+ * Like svtcp_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcfd_create(
+ int fd,
+ uint_t sendsize,
+ uint_t recvsize)
+{
+
+ return (makefd_xprt(fd, sendsize, recvsize));
+}
+
+static SVCXPRT *
+makefd_xprt(
+ int fd,
+ uint_t sendsize,
+ uint_t recvsize)
+{
+ SVCXPRT *xprt;
+ struct tcp_conn *cd;
+
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == (SVCXPRT *)NULL) {
+ (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
+ goto done;
+ }
+ cd = (struct tcp_conn *)mem_alloc(sizeof(struct tcp_conn));
+ if (cd == (struct tcp_conn *)NULL) {
+ (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
+ mem_free((char *) xprt, sizeof(SVCXPRT));
+ xprt = (SVCXPRT *)NULL;
+ goto done;
+ }
+ cd->strm_stat = XPRT_IDLE;
+ xdrrec_create(&(cd->xdrs), sendsize, recvsize,
+ (void *)xprt, readtcp, writetcp);
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (void *)cd;
+ xprt->xp_verf.oa_base = cd->verf_body;
+ xprt->xp_addrlen = 0;
+ xprt->xp_ops = &svctcp_op; /* truely deals with calls */
+ xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
+ xprt->xp_sock = fd;
+ xprt_register(xprt);
+ done:
+ return (xprt);
+}
+
+/* ARGSUSED */
+static bool_t
+rendezvous_request(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ int sock;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+#if defined(AIX)
+ size_t len;
+#else
+ int len;
+#endif /* AIX */
+
+ r = (struct tcp_rendezvous *)xprt->xp_p1;
+ again:
+ len = sizeof(struct sockaddr_in);
+ if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr,
+ &len)) < 0) {
+ if (errno == EINTR)
+ goto again;
+ return (FALSE);
+ }
+ /*
+ * make a new transporter (re-uses xprt)
+ */
+ xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
+ xprt->xp_raddr = addr;
+ xprt->xp_addrlen = len;
+ return (FALSE); /* there is never an rpc msg to be processed */
+}
+
+/* ARGSUSED */
+static enum xprt_stat
+rendezvous_stat(SVCXPRT *x)
+{
+
+ return (XPRT_IDLE);
+}
+
+static void
+svctcp_destroy(
+ SVCXPRT *xprt)
+{
+ struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1;
+
+ xprt_unregister(xprt);
+ (void)close(xprt->xp_sock);
+ if (xprt->xp_port != 0) {
+ /* a rendezvouser socket */
+ xprt->xp_port = 0;
+ } else {
+ /* an actual connection socket */
+ XDR_DESTROY(&(cd->xdrs));
+ }
+ mem_free((void *)cd, sizeof(struct tcp_conn));
+ mem_free((void *)xprt, sizeof(SVCXPRT));
+}
+
+/*
+ * All read operations timeout after 35 seconds.
+ * A timeout is fatal for the connection.
+ */
+static struct timeval wait_per_try = { 35, 0 };
+
+/*
+ * reads data from the tcp conection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ */
+static int
+readtcp(
+ SVCXPRT *xprt,
+ char *buf,
+ int len)
+{
+ int sock = xprt->xp_sock;
+#ifdef FD_SETSIZE
+ fd_set mask;
+ fd_set readfds;
+
+ FD_ZERO(&mask);
+ FD_SET(sock, &mask);
+#else
+ int mask = 1 << sock;
+ int readfds;
+#endif /* def FD_SETSIZE */
+ do {
+ readfds = mask;
+ if (select(_rpc_dtablesize(), &readfds, NULL, NULL,
+ &wait_per_try) <= 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ goto fatal_err;
+ }
+#ifdef FD_SETSIZE
+ } while (!FD_ISSET(sock, &readfds));
+#else
+ } while (readfds != mask);
+#endif /* def FD_SETSIZE */
+ if ((len = read(sock, buf, len)) > 0) {
+ return (len);
+ }
+fatal_err:
+ ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
+ return (-1);
+}
+
+/*
+ * writes data to the tcp connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writetcp(
+ SVCXPRT *xprt,
+ char *buf,
+ int len)
+{
+ int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = write(xprt->xp_sock, buf, cnt)) < 0) {
+ ((struct tcp_conn *)(xprt->xp_p1))->strm_stat =
+ XPRT_DIED;
+ return (-1);
+ }
+ }
+ return (len);
+}
+
+static enum xprt_stat
+svctcp_stat(
+ SVCXPRT *xprt)
+{
+ struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+
+ if (cd->strm_stat == XPRT_DIED)
+ return (XPRT_DIED);
+ if (! xdrrec_eof(&(cd->xdrs)))
+ return (XPRT_MOREREQS);
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svctcp_recv(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+
+ xdrs->x_op = XDR_DECODE;
+ (void)xdrrec_skiprecord(xdrs);
+ if (xdr_callmsg(xdrs, msg)) {
+ cd->x_id = msg->rm_xid;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static bool_t
+svctcp_getargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void * args_ptr)
+{
+
+ return ((*xdr_args)(&(((struct tcp_conn *)(xprt->xp_p1))->xdrs), args_ptr));
+}
+
+static bool_t
+svctcp_freeargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void * args_ptr)
+{
+ XDR *xdrs =
+ &(((struct tcp_conn *)(xprt->xp_p1))->xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static bool_t
+svctcp_reply(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+ bool_t stat;
+
+ xdrs->x_op = XDR_ENCODE;
+ msg->rm_xid = cd->x_id;
+ stat = xdr_replymsg(xdrs, msg);
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (stat);
+}
+
+/* ARGSUSED */
+static bool_t
+svctcp_abortrop(
+ SVCXPRT *x,
+ struct rpc_msg *msg)
+{
+ abort();
+ /* NOTREACHED */
+ return (FALSE);
+}
+
+/* ARGSUSED */
+static bool_t
+svctcp_abortgop(
+ SVCXPRT *x,
+ xdrproc_t argp,
+ void *arg)
+{
+ abort();
+ /* NOTREACHED */
+ return (FALSE);
+}
--- /dev/null
+#ifndef lint
+static char sfs_svc_udp_id[] = "@(#)svc_udp.c 2.1 97/10/23";
+#endif
+/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_udp.c,
+ * Server side for UDP/IP based RPC. (Does some caching in the hopes of
+ * achieving execute-at-most-once semantics.)
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/rpc.h"
+#include "rpc/osdep.h"
+#include <errno.h>
+
+
+#define rpc_buffer(xprt) ((xprt)->xp_p1)
+
+static bool_t svcudp_recv(SVCXPRT *, struct rpc_msg *);
+static bool_t svcudp_reply(SVCXPRT *, struct rpc_msg *) ;
+static enum xprt_stat svcudp_stat(SVCXPRT *);
+static bool_t svcudp_getargs(SVCXPRT *, xdrproc_t, void *);
+static bool_t svcudp_freeargs(SVCXPRT *, xdrproc_t, void *);
+static void svcudp_destroy(SVCXPRT *);
+
+static struct xp_ops svcudp_op = {
+ svcudp_recv,
+ svcudp_stat,
+ svcudp_getargs,
+ svcudp_reply,
+ svcudp_freeargs,
+ svcudp_destroy
+};
+
+/*
+ * kept in xprt->xp_p2
+ */
+struct svcudp_data {
+ uint_t su_iosz; /* byte size of send.recv buffer */
+ uint32_t su_xid; /* transaction id */
+ XDR su_xdrs; /* XDR handle */
+ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
+ char * su_cache; /* cached data, NULL if no cache */
+};
+#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
+
+static void cache_set(SVCXPRT *, uint32_t);
+static int cache_get(SVCXPRT *, struct rpc_msg *, char **, uint32_t *);
+
+/*
+ * Usage:
+ * xprt = svcudp_create(sock);
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svcudp_create
+ * binds it to an arbitrary port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ * Once *xprt is initialized, it is registered as a transporter;
+ * see (svc.h, xprt_register).
+ * The routines returns NULL if a problem occurred.
+ */
+SVCXPRT *
+svcudp_bufcreate(
+ int sock,
+ uint_t sendsz,
+ uint_t recvsz)
+{
+ bool_t madesock = FALSE;
+ SVCXPRT *xprt;
+ struct svcudp_data *su;
+ struct sockaddr_in addr;
+#if defined(AIX)
+ size_t len;
+#else
+ int len;
+#endif /* AIX */
+
+ len = sizeof(struct sockaddr_in);
+
+ if (sock == RPC_ANYSOCK) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("svcudp_create: socket creation problem");
+ return ((SVCXPRT *)NULL);
+ }
+ madesock = TRUE;
+ }
+ memset((char *)&addr, '\0', sizeof (addr));
+ addr.sin_family = AF_INET;
+ if (bindresvport(sock, &addr)) {
+ addr.sin_port = 0;
+ (void)bind(sock, (struct sockaddr *)&addr, len);
+ }
+ if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) {
+ perror("svcudp_create - cannot getsockname");
+ if (madesock)
+ (void)close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ su = (struct svcudp_data *)mem_alloc(sizeof(struct svcudp_data));
+ if (su == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ su->su_iosz = ((((sendsz > recvsz) ? sendsz : recvsz) + 3) / 4) * 4;
+ if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ xdrmem_create(
+ &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
+ su->su_cache = NULL;
+ xprt->xp_p2 = (caddr_t)su;
+ xprt->xp_verf.oa_base = su->su_verfbody;
+ xprt->xp_ops = &svcudp_op;
+ xprt->xp_port = ntohs(addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register(xprt);
+ return (xprt);
+}
+
+SVCXPRT *
+svcudp_create(
+ int sock)
+{
+
+ return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
+}
+
+/*ARGSUSED*/
+static enum xprt_stat
+svcudp_stat(
+ SVCXPRT *xprt)
+{
+
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svcudp_recv(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct svcudp_data *su = su_data(xprt);
+ XDR *xdrs = &(su->su_xdrs);
+ int rlen;
+ char *reply;
+ uint32_t replylen;
+
+ again:
+ xprt->xp_addrlen = sizeof(struct sockaddr_in);
+ rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
+ 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen));
+ if (rlen == -1 && errno == EINTR)
+ goto again;
+ if (rlen < 4*sizeof(uint32_t))
+ return (FALSE);
+ xdrs->x_op = XDR_DECODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_callmsg(xdrs, msg))
+ return (FALSE);
+ su->su_xid = msg->rm_xid;
+ if (su->su_cache != NULL) {
+ if (cache_get(xprt, msg, &reply, &replylen)) {
+ (void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
+ (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+}
+
+static bool_t
+svcudp_reply(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg)
+{
+ struct svcudp_data *su = su_data(xprt);
+ XDR *xdrs = &(su->su_xdrs);
+ int slen;
+ bool_t stat = FALSE;
+
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+ msg->rm_xid = su->su_xid;
+ if (xdr_replymsg(xdrs, msg)) {
+ slen = (int)XDR_GETPOS(xdrs);
+ if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
+ (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
+ == slen) {
+ stat = TRUE;
+ if (su->su_cache && slen >= 0) {
+ cache_set(xprt, (uint32_t) slen);
+ }
+ }
+ }
+ return (stat);
+}
+
+static bool_t
+svcudp_getargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void *args_ptr)
+{
+
+ return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr));
+}
+
+static bool_t
+svcudp_freeargs(
+ SVCXPRT *xprt,
+ xdrproc_t xdr_args,
+ void *args_ptr)
+{
+ XDR *xdrs = &(su_data(xprt)->su_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static void
+svcudp_destroy(
+ SVCXPRT *xprt)
+{
+ struct svcudp_data *su = su_data(xprt);
+
+ xprt_unregister(xprt);
+ (void)close(xprt->xp_sock);
+ XDR_DESTROY(&(su->su_xdrs));
+ mem_free(rpc_buffer(xprt), su->su_iosz);
+ mem_free((caddr_t)su, sizeof(struct svcudp_data));
+ mem_free((caddr_t)xprt, sizeof(SVCXPRT));
+}
+
+
+/***********this could be a separate file*********************/
+
+/*
+ * Fifo cache for udp server
+ * Copies pointers to reply buffers into fifo cache
+ * Buffers are sent again if retransmissions are detected.
+ */
+
+#define SPARSENESS 4 /* 75% sparse */
+
+#define CACHE_PERROR(msg) \
+ (void) fprintf(stderr,"%s\n", msg)
+
+#define ALLOC(type, size) \
+ (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
+
+#define BZERO(addr, type, size) \
+ memset((void *) addr, '\0', sizeof(type) * (int) (size))
+
+/*
+ * An entry in the cache
+ */
+typedef struct cache_node *cache_ptr;
+struct cache_node {
+ /*
+ * Index into cache is xid, proc, vers, prog and address
+ */
+ uint32_t cache_xid;
+ uint32_t cache_proc;
+ uint32_t cache_vers;
+ uint32_t cache_prog;
+ struct sockaddr_in cache_addr;
+ /*
+ * The cached reply and length
+ */
+ char * cache_reply;
+ uint32_t cache_replylen;
+ /*
+ * Next node on the list, if there is a collision
+ */
+ cache_ptr cache_next;
+};
+
+
+
+/*
+ * The entire cache
+ */
+struct udp_cache {
+ uint32_t uc_size; /* size of cache */
+ cache_ptr *uc_entries; /* hash table of entries in cache */
+ cache_ptr *uc_fifo; /* fifo list of entries in cache */
+ uint32_t uc_nextvictim; /* points to next victim in fifo list */
+ uint32_t uc_prog; /* saved program number */
+ uint32_t uc_vers; /* saved version number */
+ uint32_t uc_proc; /* saved procedure number */
+ struct sockaddr_in uc_addr; /* saved caller's address */
+};
+
+
+/*
+ * the hashing function
+ */
+#define CACHE_LOC(transp, xid) \
+ (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
+
+
+/*
+ * Enable use of the cache.
+ * Note: there is no disable.
+ */
+svcudp_enablecache(
+ SVCXPRT *transp,
+ uint32_t size)
+{
+ struct svcudp_data *su = su_data(transp);
+ struct udp_cache *uc;
+
+ if (su->su_cache != NULL) {
+ CACHE_PERROR("enablecache: cache already enabled");
+ return(0);
+ }
+ uc = ALLOC(struct udp_cache, 1);
+ if (uc == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache");
+ return(0);
+ }
+ uc->uc_size = size;
+ uc->uc_nextvictim = 0;
+ uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
+ if (uc->uc_entries == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache data");
+ return(0);
+ }
+ BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
+ uc->uc_fifo = ALLOC(cache_ptr, size);
+ if (uc->uc_fifo == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache fifo");
+ return(0);
+ }
+ BZERO(uc->uc_fifo, cache_ptr, size);
+ su->su_cache = (char *) uc;
+ return(1);
+}
+
+
+/*
+ * Set an entry in the cache
+ */
+static void
+cache_set(
+ SVCXPRT *xprt,
+ uint32_t replylen)
+{
+ cache_ptr victim;
+ cache_ptr *vicp;
+ struct svcudp_data *su = su_data(xprt);
+ struct udp_cache *uc = (struct udp_cache *) su->su_cache;
+ uint_t loc;
+ char *newbuf;
+
+ /*
+ * Find space for the new entry, either by
+ * reusing an old entry, or by mallocing a new one
+ */
+ victim = uc->uc_fifo[uc->uc_nextvictim];
+ if (victim != NULL) {
+ loc = CACHE_LOC(xprt, victim->cache_xid);
+ for (vicp = &uc->uc_entries[loc];
+ *vicp != NULL && *vicp != victim;
+ vicp = &(*vicp)->cache_next)
+ ;
+ if (*vicp == NULL) {
+ CACHE_PERROR("cache_set: victim not found");
+ return;
+ }
+ *vicp = victim->cache_next; /* remote from cache */
+ newbuf = victim->cache_reply;
+ } else {
+ victim = ALLOC(struct cache_node, 1);
+ if (victim == NULL) {
+ CACHE_PERROR("cache_set: victim alloc failed");
+ return;
+ }
+ newbuf = mem_alloc(su->su_iosz);
+ if (newbuf == NULL) {
+ CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
+ return;
+ }
+ }
+
+ /*
+ * Store it away
+ */
+ victim->cache_replylen = replylen;
+ victim->cache_reply = rpc_buffer(xprt);
+ rpc_buffer(xprt) = newbuf;
+ xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
+ victim->cache_xid = su->su_xid;
+ victim->cache_proc = uc->uc_proc;
+ victim->cache_vers = uc->uc_vers;
+ victim->cache_prog = uc->uc_prog;
+ victim->cache_addr = uc->uc_addr;
+ loc = CACHE_LOC(xprt, victim->cache_xid);
+ victim->cache_next = uc->uc_entries[loc];
+ uc->uc_entries[loc] = victim;
+ uc->uc_fifo[uc->uc_nextvictim++] = victim;
+ uc->uc_nextvictim %= uc->uc_size;
+}
+
+/*
+ * Try to get an entry from the cache
+ * return 1 if found, 0 if not found
+ */
+static int
+cache_get(
+ SVCXPRT *xprt,
+ struct rpc_msg *msg,
+ char **replyp,
+ uint32_t *replylenp)
+{
+ uint_t loc;
+ cache_ptr ent;
+ struct svcudp_data *su = su_data(xprt);
+ struct udp_cache *uc = (struct udp_cache *) su->su_cache;
+
+# define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
+
+ loc = CACHE_LOC(xprt, su->su_xid);
+ for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
+ if (ent->cache_xid == su->su_xid &&
+ ent->cache_proc == uc->uc_proc &&
+ ent->cache_vers == uc->uc_vers &&
+ ent->cache_prog == uc->uc_prog &&
+ EQADDR(ent->cache_addr, uc->uc_addr)) {
+ *replyp = ent->cache_reply;
+ *replylenp = ent->cache_replylen;
+ return(1);
+ }
+ }
+ /*
+ * Failed to find entry
+ * Remember a few things so we can do a set later
+ */
+ uc->uc_proc = msg->rm_call.cb_proc;
+ uc->uc_vers = msg->rm_call.cb_vers;
+ uc->uc_prog = msg->rm_call.cb_prog;
+ uc->uc_addr = xprt->xp_raddr;
+ return(0);
+}
+
--- /dev/null
+/*
+ * @(#)types.h 2.1 97/10/23
+ */
+/* @(#)types.h 2.3 88/08/15 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)types.h 1.18 87/07/24 SMI */
+
+/*
+ * Rpc additions to <sys/types.h>
+ */
+#ifndef __TYPES_RPC_HEADER__
+#define __TYPES_RPC_HEADER__
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#if defined(__INTTYPES_INCLUDED) || defined(_SYS_INT_TYPES_H)
+#define HAVE_INTTYPES
+#endif
+
+#if defined(USE_INTTYPES)
+#include <inttypes.h>
+#else /* USE_INTTYPES */
+#if !defined(HAVE_INTTYPES)
+#define HAVE_INTTYPES
+
+typedef signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+#endif /* !HAVE_INTTYPES */
+
+#endif /* USE_INTTYPES */
+
+#define bool_t int32_t
+#define enum_t int32_t
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#define __dontcare__ -1
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define mem_alloc(bsize) malloc(bsize)
+#define mem_free(ptr, bsize) free(ptr)
+
+#if defined(NO_T_TYPES)
+typedef unsigned int uint_t;
+typedef unsigned char uchar_t;
+#endif
+
+#ifdef notdef
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (uint32_t)0x7F000001
+#endif
+#endif
+
+#endif /* ndef __TYPES_RPC_HEADER__ */
--- /dev/null
+#ifndef lint
+static char sfs_xdr_c_id[] = "@(#)xdr.c 2.1 97/10/23";
+#endif
+/* @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr.c 1.35 87/08/12";
+#endif
+
+/*
+ * xdr.c, Generic XDR routines implementation.
+ *
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ *
+ * These are the "generic" xdr routines used to serialize and de-serialize
+ * most common data items. See xdr.h for more info on the interface to
+ * xdr.
+ */
+#ifndef lint
+static char sfs_clnt_id[] = "@(#)xdr.c 2.1 97/10/23";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+
+/*
+ * constants specific to the xdr "protocol"
+ */
+#define XDR_FALSE ((int32_t) 0)
+#define XDR_TRUE ((int32_t) 1)
+#define LASTUNSIGNED ((uint_t) 0-1)
+
+/*
+ * for unit alignment
+ */
+static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 };
+
+/*
+ * Free a data structure using XDR
+ * Not a filter, but a convenient utility nonetheless
+ */
+void
+xdr_free(
+ xdrproc_t proc,
+ char *objp)
+{
+ XDR x;
+
+ x.x_op = XDR_FREE;
+ (*proc)(&x, objp);
+}
+
+/*
+ * XDR nothing
+ */
+bool_t
+xdr_void(void)
+{
+
+ return (TRUE);
+}
+
+/*
+ * XDR integers
+ */
+bool_t
+xdr_int(
+ XDR *xdrs,
+ int *ip)
+{
+
+#ifdef lint
+ (void) (xdr_int16_t(xdrs, (int16_t *)ip));
+ return (xdr_int32_t(xdrs, (int32_t *)ip));
+#else
+ if (sizeof (int) == sizeof (int32_t)) {
+ return (xdr_int32_t(xdrs, (int32_t *)ip));
+ } else {
+ return (xdr_int16_t(xdrs, (int16_t *)ip));
+ }
+#endif
+}
+
+/*
+ * XDR unsigned integers
+ */
+bool_t
+xdr_u_int(
+ XDR *xdrs,
+ uint_t *up)
+{
+
+#ifdef lint
+ (void) (xdr_int16_t(xdrs, (int16_t *)up));
+ return (xdr_uint32_t(xdrs, (uint32_t *)up));
+#else
+ if (sizeof (uint_t) == sizeof (uint32_t)) {
+ return (xdr_uint32_t(xdrs, (uint32_t *)up));
+ } else {
+ return (xdr_int16_t(xdrs, (int16_t *)up));
+ }
+#endif
+}
+
+/*
+ * XDR long integers
+ * same as xdr_u_long - open coded to save a proc call!
+ */
+bool_t
+xdr_long(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ if (xdrs->x_op == XDR_ENCODE)
+ return (XDR_PUTLONG(xdrs, lp));
+
+ if (xdrs->x_op == XDR_DECODE)
+ return (XDR_GETLONG(xdrs, lp));
+
+ if (xdrs->x_op == XDR_FREE)
+ return (TRUE);
+
+ return (FALSE);
+}
+
+bool_t
+xdr_int32_t(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ if (xdrs->x_op == XDR_ENCODE)
+ return (XDR_PUTLONG(xdrs, lp));
+
+ if (xdrs->x_op == XDR_DECODE)
+ return (XDR_GETLONG(xdrs, lp));
+
+ if (xdrs->x_op == XDR_FREE)
+ return (TRUE);
+
+ return (FALSE);
+}
+
+/*
+ * XDR unsigned long integers
+ * same as xdr_long - open coded to save a proc call!
+ */
+bool_t
+xdr_u_long(
+ XDR *xdrs,
+ uint32_t *ulp)
+{
+
+ if (xdrs->x_op == XDR_DECODE)
+ return (XDR_GETLONG(xdrs, (int32_t *)ulp));
+ if (xdrs->x_op == XDR_ENCODE)
+ return (XDR_PUTLONG(xdrs, (int32_t *)ulp));
+ if (xdrs->x_op == XDR_FREE)
+ return (TRUE);
+ return (FALSE);
+}
+
+bool_t
+xdr_uint32_t(
+ XDR *xdrs,
+ uint32_t *ulp)
+{
+
+ if (xdrs->x_op == XDR_DECODE)
+ return (XDR_GETLONG(xdrs, (int32_t *)ulp));
+ if (xdrs->x_op == XDR_ENCODE)
+ return (XDR_PUTLONG(xdrs, (int32_t *)ulp));
+ if (xdrs->x_op == XDR_FREE)
+ return (TRUE);
+ return (FALSE);
+}
+
+/*
+ * XDR short integers
+ */
+bool_t
+xdr_short(
+ XDR *xdrs,
+ int16_t *sp)
+{
+ int32_t l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (int32_t) *sp;
+ return (XDR_PUTLONG(xdrs, &l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, &l)) {
+ return (FALSE);
+ }
+ *sp = (int16_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool_t
+xdr_int16_t(
+ XDR *xdrs,
+ int16_t *sp)
+{
+ int32_t l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (int32_t) *sp;
+ return (XDR_PUTLONG(xdrs, &l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, &l)) {
+ return (FALSE);
+ }
+ *sp = (int16_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR unsigned short integers
+ */
+bool_t
+xdr_u_short(
+ XDR *xdrs,
+ uint16_t *usp)
+{
+ uint32_t l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (uint32_t) *usp;
+ return (XDR_PUTLONG(xdrs, (int32_t *)&l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, (int32_t *)&l)) {
+ return (FALSE);
+ }
+ *usp = (uint16_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool_t
+xdr_uint16_t(
+ XDR *xdrs,
+ uint16_t *usp)
+{
+ uint32_t l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (uint32_t) *usp;
+ return (XDR_PUTLONG(xdrs, (int32_t *)&l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, (int32_t *)&l)) {
+ return (FALSE);
+ }
+ *usp = (uint16_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+/*
+ * XDR a char
+ */
+bool_t
+xdr_char(
+ XDR *xdrs,
+ char *cp)
+{
+ int i;
+
+ i = (*cp);
+ if (!xdr_int(xdrs, &i)) {
+ return (FALSE);
+ }
+ *cp = i;
+ return (TRUE);
+}
+
+/*
+ * XDR an unsigned char
+ */
+bool_t
+xdr_u_char(
+ XDR *xdrs,
+ uchar_t *cp)
+{
+ uint_t u;
+
+ u = (*cp);
+ if (!xdr_u_int(xdrs, &u)) {
+ return (FALSE);
+ }
+ *cp = u;
+ return (TRUE);
+}
+
+/*
+ * XDR booleans
+ */
+bool_t
+xdr_bool(
+ XDR *xdrs,
+ bool_t *bp)
+{
+ int32_t lb;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ lb = *bp ? XDR_TRUE : XDR_FALSE;
+ return (XDR_PUTLONG(xdrs, &lb));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, &lb)) {
+ return (FALSE);
+ }
+ *bp = (lb == XDR_FALSE) ? FALSE : TRUE;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR enumerations
+ */
+bool_t
+xdr_enum(
+ XDR *xdrs,
+ enum_t *ep)
+{
+#ifndef lint
+ enum sizecheck { SIZEVAL }; /* used to find the size of an enum */
+
+ /*
+ * enums are treated as ints
+ */
+ if (sizeof (enum sizecheck) == sizeof (int32_t)) {
+ return (xdr_int32_t(xdrs, (int32_t *)ep));
+ } else if (sizeof (enum sizecheck) == sizeof (int16_t)) {
+ return (xdr_int16_t(xdrs, (int16_t *)ep));
+ } else {
+ return (FALSE);
+ }
+#else
+ (void) (xdr_int16_t(xdrs, (int16_t *)ep));
+ return (xdr_int32_t(xdrs, (int32_t *)ep));
+#endif
+}
+
+/*
+ * XDR opaque data
+ * Allows the specification of a fixed size sequence of opaque bytes.
+ * cp points to the opaque object and cnt gives the byte length.
+ */
+bool_t
+xdr_opaque(
+ XDR *xdrs,
+ void *cp,
+ uint_t cnt)
+{
+ uint_t rndup;
+ static crud[BYTES_PER_XDR_UNIT];
+
+ /*
+ * if no data we are done
+ */
+ if (cnt == 0)
+ return (TRUE);
+
+ /*
+ * round byte count to full xdr units
+ */
+ rndup = cnt % BYTES_PER_XDR_UNIT;
+ if (rndup > 0)
+ rndup = BYTES_PER_XDR_UNIT - rndup;
+
+ if (xdrs->x_op == XDR_DECODE) {
+ if (!XDR_GETBYTES(xdrs, cp, cnt)) {
+ return (FALSE);
+ }
+ if (rndup == 0)
+ return (TRUE);
+ return (XDR_GETBYTES(xdrs, crud, rndup));
+ }
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ if (!XDR_PUTBYTES(xdrs, cp, cnt)) {
+ return (FALSE);
+ }
+ if (rndup == 0)
+ return (TRUE);
+ return (XDR_PUTBYTES(xdrs, xdr_zero, rndup));
+ }
+
+ if (xdrs->x_op == XDR_FREE) {
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*
+ * XDR counted bytes
+ * *cpp is a pointer to the bytes, *sizep is the count.
+ * If *cpp is NULL maxsize bytes are allocated
+ */
+bool_t
+xdr_bytes(
+ XDR *xdrs,
+ char **cpp,
+ uint_t *sizep,
+ uint_t maxsize)
+{
+ char *sp = *cpp; /* sp is the actual string pointer */
+ uint_t nodesize;
+
+ /*
+ * first deal with the length since xdr bytes are counted
+ */
+ if (! xdr_u_int(xdrs, sizep)) {
+ return (FALSE);
+ }
+ nodesize = *sizep;
+ if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) {
+ return (FALSE);
+ }
+
+ /*
+ * now deal with the actual bytes
+ */
+ switch (xdrs->x_op) {
+
+ case XDR_DECODE:
+ if (nodesize == 0) {
+ return (TRUE);
+ }
+ if (sp == NULL) {
+ *cpp = sp = (char *)mem_alloc(nodesize);
+ }
+ if (sp == NULL) {
+ (void) fprintf(stderr, "xdr_bytes: out of memory\n");
+ return (FALSE);
+ }
+ /* fall into ... */
+
+ case XDR_ENCODE:
+ return (xdr_opaque(xdrs, sp, nodesize));
+
+ case XDR_FREE:
+ if (sp != NULL) {
+ mem_free(sp, nodesize);
+ *cpp = NULL;
+ }
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * Implemented here due to commonality of the object.
+ */
+bool_t
+xdr_netobj(
+ XDR *xdrs,
+ struct netobj *np)
+{
+
+ return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ));
+}
+
+/*
+ * XDR a descriminated union
+ * Support routine for discriminated unions.
+ * You create an array of xdrdiscrim structures, terminated with
+ * an entry with a null procedure pointer. The routine gets
+ * the discriminant value and then searches the array of xdrdiscrims
+ * looking for that value. It calls the procedure given in the xdrdiscrim
+ * to handle the discriminant. If there is no specific routine a default
+ * routine may be called.
+ * If there is no specific or default routine an error is returned.
+ */
+bool_t
+xdr_union(
+ XDR *xdrs,
+ enum_t *dscmp, /* enum to decide which arm to work on */
+ char *unp, /* the union itself */
+ struct xdr_discrim *choices, /* [value, xdr proc] for each arm */
+ xdrproc_t dfault) /* default xdr routine */
+{
+ enum_t dscm;
+
+ /*
+ * we deal with the discriminator; it's an enum
+ */
+ if (! xdr_enum(xdrs, dscmp)) {
+ return (FALSE);
+ }
+ dscm = *dscmp;
+
+ /*
+ * search choices for a value that matches the discriminator.
+ * if we find one, execute the xdr routine for that value.
+ */
+ for (; choices->proc != NULL_xdrproc_t; choices++) {
+ if (choices->value == dscm)
+ return ((*(choices->proc))(xdrs, unp, LASTUNSIGNED));
+ }
+
+ /*
+ * no match - execute the default xdr routine if there is one
+ */
+ return ((dfault == NULL_xdrproc_t) ? FALSE :
+ (*dfault)(xdrs, unp, LASTUNSIGNED));
+}
+
+
+/*
+ * Non-portable xdr primitives.
+ * Care should be taken when moving these routines to new architectures.
+ */
+
+
+/*
+ * XDR null terminated ASCII strings
+ * xdr_string deals with "C strings" - arrays of bytes that are
+ * terminated by a NULL character. The parameter cpp references a
+ * pointer to storage; If the pointer is null, then the necessary
+ * storage is allocated. The last parameter is the max allowed length
+ * of the string as specified by a protocol.
+ */
+bool_t
+xdr_string(
+ XDR *xdrs,
+ char **cpp,
+ uint_t maxsize)
+{
+ char *sp = *cpp; /* sp is the actual string pointer */
+ uint_t size;
+ uint_t nodesize;
+
+ /*
+ * first deal with the length since xdr strings are counted-strings
+ */
+ switch (xdrs->x_op) {
+ case XDR_FREE:
+ if (sp == NULL) {
+ return(TRUE); /* already free */
+ }
+ /* fall through... */
+ case XDR_ENCODE:
+ size = strlen(sp);
+ break;
+ }
+ if (! xdr_u_int(xdrs, &size)) {
+ return (FALSE);
+ }
+ if (size > maxsize) {
+ return (FALSE);
+ }
+ nodesize = size + 1;
+
+ /*
+ * now deal with the actual bytes
+ */
+ switch (xdrs->x_op) {
+
+ case XDR_DECODE:
+ if (nodesize == 0) {
+ return (TRUE);
+ }
+ if (sp == NULL)
+ *cpp = sp = (char *)mem_alloc(nodesize);
+ if (sp == NULL) {
+ (void) fprintf(stderr, "xdr_string: out of memory\n");
+ return (FALSE);
+ }
+ sp[size] = 0;
+ /* fall into ... */
+
+ case XDR_ENCODE:
+ return (xdr_opaque(xdrs, sp, size));
+
+ case XDR_FREE:
+ mem_free(sp, nodesize);
+ *cpp = NULL;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * Wrapper for xdr_string that can be called directly from
+ * routines like clnt_call
+ */
+bool_t
+xdr_wrapstring(
+ XDR *xdrs,
+ char **cpp)
+{
+ if (xdr_string(xdrs, cpp, LASTUNSIGNED)) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
--- /dev/null
+/*
+ * @(#)xdr.h 2.1 97/10/23
+ */
+
+/* @(#)xdr.h 2.2 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* @(#)xdr.h 1.19 87/04/22 SMI */
+
+/*
+ * xdr.h, External Data Representation Serialization Routines.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#ifndef __XDR_HEADER__
+#define __XDR_HEADER__
+
+#include <stdio.h>
+
+/*
+ * XDR provides a conventional way for converting between C data
+ * types and an external bit-string representation. Library supplied
+ * routines provide for the conversion on built-in C data types. These
+ * routines and utility routines defined here are used to help implement
+ * a type encode/decode routine for each user-defined type.
+ *
+ * Each data type provides a single procedure which takes two arguments:
+ *
+ * bool_t
+ * xdrproc(xdrs, argresp)
+ * XDR *xdrs;
+ * <type> *argresp;
+ *
+ * xdrs is an instance of a XDR handle, to which or from which the data
+ * type is to be converted. argresp is a pointer to the structure to be
+ * converted. The XDR handle contains an operation field which indicates
+ * which of the operations (ENCODE, DECODE * or FREE) is to be performed.
+ *
+ * XDR_DECODE may allocate space if the pointer argresp is null. This
+ * data can be freed with the XDR_FREE operation.
+ *
+ * We write only one procedure per data type to make it easy
+ * to keep the encode and decode procedures for a data type consistent.
+ * In many cases the same code performs all operations on a user defined type,
+ * because all the hard work is done in the component type routines.
+ * decode as a series of calls on the nested data types.
+ */
+
+/*
+ * Xdr operations. XDR_ENCODE causes the type to be encoded into the
+ * stream. XDR_DECODE causes the type to be extracted from the stream.
+ * XDR_FREE can be used to release the space allocated by an XDR_DECODE
+ * request.
+ */
+enum xdr_op {
+ XDR_ENCODE=0,
+ XDR_DECODE=1,
+ XDR_FREE=2
+};
+
+/*
+ * This is the number of bytes per unit of external data.
+ */
+#define BYTES_PER_XDR_UNIT (4)
+#define RNDUP(x) ((((x) + BYTES_PER_XDR_UNIT - 1) / BYTES_PER_XDR_UNIT) \
+ * BYTES_PER_XDR_UNIT)
+
+/*
+ * A xdrproc_t exists for each data type which is to be encoded or decoded.
+ *
+ * The second argument to the xdrproc_t is a pointer to an opaque pointer.
+ * The opaque pointer generally points to a structure of the data type
+ * to be decoded. If this pointer is 0, then the type routines should
+ * allocate dynamic storage of the appropriate size and return it.
+ * bool_t (*xdrproc_t)(XDR *, void **);
+ */
+typedef bool_t (*xdrproc_t)();
+
+/*
+ * The XDR handle.
+ * Contains operation which is being applied to the stream,
+ * an operations vector for the paticular implementation (e.g. see xdr_mem.c),
+ * and two private fields for the use of the particular impelementation.
+ */
+typedef struct {
+ enum xdr_op x_op; /* operation; fast additional param */
+ struct xdr_ops *x_ops;
+ char * x_public; /* users' data */
+ char * x_private; /* pointer to private data */
+ char * x_base; /* private used for position info */
+ int x_handy; /* extra private word */
+} XDR;
+
+struct xdr_ops {
+ /* get a long from underlying stream */
+ bool_t (*x_getlong)(XDR *, int32_t *);
+ /* put a long to " */
+ bool_t (*x_putlong)(XDR *, int32_t *);
+ /* get some bytes from " */
+ bool_t (*x_getbytes)(XDR *, void *, uint_t);
+ /* put some bytes to " */
+ bool_t (*x_putbytes)(XDR *, void *, uint_t);
+ /* returns bytes off from beginning */
+ uint_t (*x_getpostn)(XDR *);
+ /* lets you reposition the stream */
+ bool_t (*x_setpostn)(XDR *, uint_t);
+ /* buf quick ptr to buffered data */
+ int32_t *(*x_inline)(XDR *, uint_t);
+ /* free privates of this xdr_stream */
+ void (*x_destroy)(XDR *);
+};
+
+/*
+ * Operations defined on a XDR handle
+ *
+ * XDR *xdrs;
+ * int32_t *longp;
+ * void * addr;
+ * uint_t len;
+ * uint_t pos;
+ */
+#define XDR_GETLONG(xdrs, longp) \
+ (*(xdrs)->x_ops->x_getlong)(xdrs, longp)
+#define xdr_getlong(xdrs, longp) \
+ (*(xdrs)->x_ops->x_getlong)(xdrs, longp)
+
+#define XDR_PUTLONG(xdrs, longp) \
+ (*(xdrs)->x_ops->x_putlong)(xdrs, longp)
+#define xdr_putlong(xdrs, longp) \
+ (*(xdrs)->x_ops->x_putlong)(xdrs, longp)
+
+#define XDR_GETBYTES(xdrs, addr, len) \
+ (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len)
+#define xdr_getbytes(xdrs, addr, len) \
+ (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len)
+
+#define XDR_PUTBYTES(xdrs, addr, len) \
+ (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len)
+#define xdr_putbytes(xdrs, addr, len) \
+ (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len)
+
+#define XDR_GETPOS(xdrs) \
+ (*(xdrs)->x_ops->x_getpostn)(xdrs)
+#define xdr_getpos(xdrs) \
+ (*(xdrs)->x_ops->x_getpostn)(xdrs)
+
+#define XDR_SETPOS(xdrs, pos) \
+ (*(xdrs)->x_ops->x_setpostn)(xdrs, pos)
+#define xdr_setpos(xdrs, pos) \
+ (*(xdrs)->x_ops->x_setpostn)(xdrs, pos)
+
+#define XDR_INLINE(xdrs, len) \
+ (*(xdrs)->x_ops->x_inline)(xdrs, len)
+#define xdr_inline(xdrs, len) \
+ (*(xdrs)->x_ops->x_inline)(xdrs, len)
+
+#define XDR_DESTROY(xdrs) \
+ if ((xdrs)->x_ops->x_destroy) \
+ (*(xdrs)->x_ops->x_destroy)(xdrs)
+#define xdr_destroy(xdrs) \
+ if ((xdrs)->x_ops->x_destroy) \
+ (*(xdrs)->x_ops->x_destroy)(xdrs)
+
+/*
+ * Support struct for discriminated unions.
+ * You create an array of xdrdiscrim structures, terminated with
+ * a entry with a null procedure pointer. The xdr_union routine gets
+ * the discriminant value and then searches the array of structures
+ * for a matching value. If a match is found the associated xdr routine
+ * is called to handle that part of the union. If there is
+ * no match, then a default routine may be called.
+ * If there is no match and no default routine it is an error.
+ */
+#define NULL_xdrproc_t ((xdrproc_t)0)
+struct xdr_discrim {
+ int value;
+ xdrproc_t proc;
+};
+
+/*
+ * In-line routines for fast encode/decode of primitve data types.
+ * Caveat emptor: these use single memory cycles to get the
+ * data from the underlying buffer, and will fail to operate
+ * properly if the data is not aligned. The standard way to use these
+ * is to say:
+ * if ((buf = XDR_INLINE(xdrs, count)) == NULL)
+ * return (FALSE);
+ * <<< macro calls >>>
+ * where ``count'' is the number of bytes of data occupied
+ * by the primitive data types.
+ *
+ * N.B. and frozen for all time: each data type here uses 4 bytes
+ * of external representation.
+ */
+#define IXDR_GET_LONG(buf) ((int32_t)ntohl((uint32_t)*(buf)++))
+#define IXDR_PUT_LONG(buf, v) (*(buf)++ = (int32_t)htonl((uint32_t)v))
+
+#define IXDR_GET_BOOL(buf) ((bool_t)IXDR_GET_LONG(buf))
+#define IXDR_GET_ENUM(buf, t) ((t)IXDR_GET_LONG(buf))
+#define IXDR_GET_U_LONG(buf) ((uint32_t)IXDR_GET_LONG(buf))
+#define IXDR_GET_SHORT(buf) ((int16_t)IXDR_GET_LONG(buf))
+#define IXDR_GET_U_SHORT(buf) ((uint16_t)IXDR_GET_LONG(buf))
+
+#define IXDR_PUT_BOOL(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v)))
+#define IXDR_PUT_ENUM(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v)))
+#define IXDR_PUT_U_LONG(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v)))
+#define IXDR_PUT_SHORT(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v)))
+#define IXDR_PUT_U_SHORT(buf, v) IXDR_PUT_LONG((buf), ((int32_t)(v)))
+
+/*
+ * These are the "generic" xdr routines.
+ */
+extern bool_t xdr_void(void);
+extern bool_t xdr_int(XDR *, int *);
+extern bool_t xdr_u_int(XDR *, uint_t *);
+extern bool_t xdr_long(XDR *, int32_t *);
+extern bool_t xdr_u_long(XDR *, uint32_t *);
+extern bool_t xdr_int32_t(XDR *, int32_t *);
+extern bool_t xdr_uint32_t(XDR *, uint32_t *);
+extern bool_t xdr_short(XDR *, int16_t *);
+extern bool_t xdr_u_short(XDR *, uint16_t *);
+extern bool_t xdr_int16_t(XDR *, int16_t *);
+extern bool_t xdr_uint16_t(XDR *, uint16_t *);
+extern bool_t xdr_bool(XDR *, bool_t *);
+extern bool_t xdr_enum(XDR *, enum_t *);
+extern bool_t xdr_array(XDR *, void **, uint_t *, uint_t, uint_t, xdrproc_t);
+extern bool_t xdr_bytes(XDR *, char **cp, uint_t *, uint_t);
+extern bool_t xdr_opaque(XDR *, void *, uint_t);
+extern bool_t xdr_string(XDR *, char **cp, uint_t);
+extern bool_t xdr_union(XDR *, enum_t *, char *, struct xdr_discrim *, xdrproc_t);
+extern bool_t xdr_char(XDR *, char *);
+extern bool_t xdr_u_char(XDR *, uchar_t *);
+extern bool_t xdr_vector(XDR *, char *, uint_t, uint_t, xdrproc_t);
+extern bool_t xdr_float(XDR *, float *);
+extern bool_t xdr_double(XDR *, double *);
+extern bool_t xdr_reference(XDR *, void **, uint_t, xdrproc_t);
+extern bool_t xdr_pointer(XDR *, void **, uint_t, xdrproc_t);
+extern bool_t xdr_wrapstring(XDR *, char **);
+
+/*
+ * Common opaque bytes objects used by many rpc protocols;
+ * declared here due to commonality.
+ */
+#define MAX_NETOBJ_SZ 1024
+struct netobj {
+ uint_t n_len;
+ char *n_bytes;
+};
+typedef struct netobj netobj;
+extern bool_t xdr_netobj(XDR *, netobj *);
+
+/*
+ * These are the public routines for the various implementations of
+ * xdr streams.
+ */
+extern void xdrmem_create(XDR *, void *, uint_t, enum xdr_op);
+extern void xdrstdio_create(XDR *, FILE *, enum xdr_op);
+extern void xdrrec_create(XDR *, uint_t, uint_t, void *, int (*)(), int (*)());
+extern bool_t xdrrec_endofrecord(XDR *, bool_t);
+extern bool_t xdrrec_skiprecord(XDR *);
+extern bool_t xdrrec_eof(XDR *);
+
+#endif /* !__XDR_HEADER__ */
--- /dev/null
+#ifndef lint
+static char sfs_xdr_array_id[] = "@(#)xdr_array.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_array.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * xdr_array.c, Generic XDR routines impelmentation.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * These are the "non-trivial" xdr primitives used to serialize and de-serialize
+ * arrays. See xdr.h for more info on the interface to xdr.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+
+#define LASTUNSIGNED ((uint_t)0-1)
+
+
+/*
+ * XDR an array of arbitrary elements
+ * *addrp is a pointer to the array, *sizep is the number of elements.
+ * If addrp is NULL (*sizep * elsize) bytes are allocated.
+ * elsize is the size (in bytes) of each element, and elproc is the
+ * xdr procedure to call to handle each element of the array.
+ */
+bool_t
+xdr_array(
+ XDR *xdrs,
+ void **addrp, /* array pointer */
+ uint_t *sizep, /* number of elements */
+ uint_t maxsize, /* max numberof elements */
+ uint_t elsize, /* size in bytes of each element */
+ xdrproc_t elproc) /* xdr routine to handle each element */
+{
+ uint_t i;
+ char * target = *addrp;
+ uint_t c; /* the actual element count */
+ bool_t stat = TRUE;
+ uint_t nodesize;
+
+ /* like strings, arrays are really counted arrays */
+ if (! xdr_u_int(xdrs, sizep)) {
+ return (FALSE);
+ }
+ c = *sizep;
+ if ((c > maxsize) && (xdrs->x_op != XDR_FREE)) {
+ return (FALSE);
+ }
+ nodesize = c * elsize;
+
+ /*
+ * if we are deserializing, we may need to allocate an array.
+ * We also save time by checking for a null array if we are freeing.
+ */
+ if (target == NULL)
+ switch (xdrs->x_op) {
+ case XDR_DECODE:
+ if (c == 0)
+ return (TRUE);
+ *addrp = target = mem_alloc(nodesize);
+ if (target == NULL) {
+ (void) fprintf(stderr,
+ "xdr_array: out of memory\n");
+ return (FALSE);
+ }
+ memset(target, '\0', nodesize);
+ break;
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+
+ /*
+ * now we xdr each element of array
+ */
+ for (i = 0; (i < c) && stat; i++) {
+ stat = (*elproc)(xdrs, target, LASTUNSIGNED);
+ target += elsize;
+ }
+
+ /*
+ * the array may need freeing
+ */
+ if (xdrs->x_op == XDR_FREE) {
+ mem_free(*addrp, nodesize);
+ *addrp = NULL;
+ }
+ return (stat);
+}
+
+/*
+ * xdr_vector():
+ *
+ * XDR a fixed length array. Unlike variable-length arrays,
+ * the storage of fixed length arrays is static and unfreeable.
+ * > basep: base of the array
+ * > size: size of the array
+ * > elemsize: size of each element
+ * > xdr_elem: routine to XDR each element
+ */
+bool_t
+xdr_vector(
+ XDR *xdrs,
+ char *basep,
+ uint_t nelem,
+ uint_t elemsize,
+ xdrproc_t xdr_elem)
+{
+ uint_t i;
+ char *elptr;
+
+ elptr = basep;
+ for (i = 0; i < nelem; i++) {
+ if (! (*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) {
+ return(FALSE);
+ }
+ elptr += elemsize;
+ }
+ return(TRUE);
+}
+
--- /dev/null
+#ifndef lint
+static char sfs_xdr_float_id[] = "@(#)xdr_float.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_float.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * xdr_float.c, Generic XDR routines impelmentation.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * These are the "floating point" xdr routines used to (de)serialize
+ * most common data items. See xdr.h for more info on the interface to
+ * xdr.
+ */
+
+#include <stdio.h>
+
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+
+/*
+ * NB: Not portable.
+ * This routine works on Suns (Sky / 68000's) and Vaxen.
+ */
+
+#ifdef vax
+
+/* What IEEE single precision floating point looks like on a Vax */
+struct ieee_single {
+ unsigned int mantissa: 23;
+ unsigned int exp : 8;
+ unsigned int sign : 1;
+};
+
+/* Vax single precision floating point */
+struct vax_single {
+ unsigned int mantissa1 : 7;
+ unsigned int exp : 8;
+ unsigned int sign : 1;
+ unsigned int mantissa2 : 16;
+};
+
+#define VAX_SNG_BIAS 0x81
+#define IEEE_SNG_BIAS 0x7f
+
+static struct sgl_limits {
+ struct vax_single s;
+ struct ieee_single ieee;
+} sgl_limits[2] = {
+ {{ 0x7f, 0xff, 0x0, 0xffff }, /* Max Vax */
+ { 0x0, 0xff, 0x0 }}, /* Max IEEE */
+ {{ 0x0, 0x0, 0x0, 0x0 }, /* Min Vax */
+ { 0x0, 0x0, 0x0 }} /* Min IEEE */
+};
+#endif /* vax */
+
+bool_t
+xdr_float(xdrs, fp)
+ register XDR *xdrs;
+ register float *fp;
+{
+#if defined(vax)
+ struct ieee_single is;
+ struct vax_single vs, *vsp;
+ struct sgl_limits *lim;
+ int i;
+#endif
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+#if !defined(vax)
+ return (XDR_PUTLONG(xdrs, (int32_t *)fp));
+#else
+ vs = *((struct vax_single *)fp);
+ for (i = 0, lim = sgl_limits;
+ i < sizeof(sgl_limits)/sizeof(struct sgl_limits);
+ i++, lim++) {
+ if ((vs.mantissa2 == lim->s.mantissa2) &&
+ (vs.exp == lim->s.exp) &&
+ (vs.mantissa1 == lim->s.mantissa1)) {
+ is = lim->ieee;
+ goto shipit;
+ }
+ }
+ is.exp = vs.exp - VAX_SNG_BIAS + IEEE_SNG_BIAS;
+ is.mantissa = (vs.mantissa1 << 16) | vs.mantissa2;
+ shipit:
+ is.sign = vs.sign;
+ return (XDR_PUTLONG(xdrs, (int32_t *)&is));
+#endif
+
+ case XDR_DECODE:
+#if !defined(vax)
+ return (XDR_GETLONG(xdrs, (int32_t *)fp));
+#else
+ vsp = (struct vax_single *)fp;
+ if (!XDR_GETLONG(xdrs, (int32_t *)&is))
+ return (FALSE);
+ for (i = 0, lim = sgl_limits;
+ i < sizeof(sgl_limits)/sizeof(struct sgl_limits);
+ i++, lim++) {
+ if ((is.exp == lim->ieee.exp) &&
+ (is.mantissa == lim->ieee.mantissa)) {
+ *vsp = lim->s;
+ goto doneit;
+ }
+ }
+ vsp->exp = is.exp - IEEE_SNG_BIAS + VAX_SNG_BIAS;
+ vsp->mantissa2 = is.mantissa;
+ vsp->mantissa1 = (is.mantissa >> 16);
+ doneit:
+ vsp->sign = is.sign;
+ return (TRUE);
+#endif
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * This routine works on Suns (Sky / 68000's) and Vaxen.
+ */
+
+#ifdef vax
+/* What IEEE double precision floating point looks like on a Vax */
+struct ieee_double {
+ unsigned int mantissa1 : 20;
+ unsigned int exp : 11;
+ unsigned int sign : 1;
+ unsigned int mantissa2 : 32;
+};
+
+/* Vax double precision floating point */
+struct vax_double {
+ unsigned int mantissa1 : 7;
+ unsigned int exp : 8;
+ unsigned int sign : 1;
+ unsigned int mantissa2 : 16;
+ unsigned int mantissa3 : 16;
+ unsigned int mantissa4 : 16;
+};
+
+#define VAX_DBL_BIAS 0x81
+#define IEEE_DBL_BIAS 0x3ff
+#define MASK(nbits) ((1 << nbits) - 1)
+
+static struct dbl_limits {
+ struct vax_double d;
+ struct ieee_double ieee;
+} dbl_limits[2] = {
+ {{ 0x7f, 0xff, 0x0, 0xffff, 0xffff, 0xffff }, /* Max Vax */
+ { 0x0, 0x7ff, 0x0, 0x0 }}, /* Max IEEE */
+ {{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* Min Vax */
+ { 0x0, 0x0, 0x0, 0x0 }} /* Min IEEE */
+};
+
+#endif /* vax */
+
+
+bool_t
+xdr_double(xdrs, dp)
+ register XDR *xdrs;
+ double *dp;
+{
+ register int32_t *lp;
+#if defined(vax)
+ struct ieee_double id;
+ struct vax_double vd;
+ register struct dbl_limits *lim;
+ int i;
+#endif
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+#if !defined(vax)
+ lp = (int32_t *)dp;
+#else
+ vd = *((struct vax_double *)dp);
+ for (i = 0, lim = dbl_limits;
+ i < sizeof(dbl_limits)/sizeof(struct dbl_limits);
+ i++, lim++) {
+ if ((vd.mantissa4 == lim->d.mantissa4) &&
+ (vd.mantissa3 == lim->d.mantissa3) &&
+ (vd.mantissa2 == lim->d.mantissa2) &&
+ (vd.mantissa1 == lim->d.mantissa1) &&
+ (vd.exp == lim->d.exp)) {
+ id = lim->ieee;
+ goto shipit;
+ }
+ }
+ id.exp = vd.exp - VAX_DBL_BIAS + IEEE_DBL_BIAS;
+ id.mantissa1 = (vd.mantissa1 << 13) | (vd.mantissa2 >> 3);
+ id.mantissa2 = ((vd.mantissa2 & MASK(3)) << 29) |
+ (vd.mantissa3 << 13) |
+ ((vd.mantissa4 >> 3) & MASK(13));
+ shipit:
+ id.sign = vd.sign;
+ lp = (int32_t *)&id;
+#endif
+ return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp));
+
+ case XDR_DECODE:
+#if !defined(vax)
+ lp = (int32_t *)dp;
+ return (XDR_GETLONG(xdrs, lp++) && XDR_GETLONG(xdrs, lp));
+#else
+ lp = (int32_t *)&id;
+ if (!XDR_GETLONG(xdrs, lp++) || !XDR_GETLONG(xdrs, lp))
+ return (FALSE);
+ for (i = 0, lim = dbl_limits;
+ i < sizeof(dbl_limits)/sizeof(struct dbl_limits);
+ i++, lim++) {
+ if ((id.mantissa2 == lim->ieee.mantissa2) &&
+ (id.mantissa1 == lim->ieee.mantissa1) &&
+ (id.exp == lim->ieee.exp)) {
+ vd = lim->d;
+ goto doneit;
+ }
+ }
+ vd.exp = id.exp - IEEE_DBL_BIAS + VAX_DBL_BIAS;
+ vd.mantissa1 = (id.mantissa1 >> 13);
+ vd.mantissa2 = ((id.mantissa1 & MASK(13)) << 3) |
+ (id.mantissa2 >> 29);
+ vd.mantissa3 = (id.mantissa2 >> 13);
+ vd.mantissa4 = (id.mantissa2 << 3);
+ doneit:
+ vd.sign = id.sign;
+ *dp = *((double *)&vd);
+ return (TRUE);
+#endif
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ return (FALSE);
+}
--- /dev/null
+#ifndef lint
+static char sfs_xdr_mem_id[] = "@(#)xdr_mem.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * xdr_mem.h, XDR implementation using memory buffers.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * If you have some data to be interpreted as external data representation
+ * or to be converted to external data representation in a memory buffer,
+ * then this is the package for you.
+ *
+ */
+
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/osdep.h"
+
+static bool_t xdrmem_getlong(XDR *, int32_t *);
+static bool_t xdrmem_putlong(XDR *, int32_t *);
+static bool_t xdrmem_getbytes(XDR *, void *, uint_t);
+static bool_t xdrmem_putbytes(XDR *, void *, uint_t);
+static uint_t xdrmem_getpos(XDR *);
+static bool_t xdrmem_setpos(XDR *, uint_t);
+static int32_t *xdrmem_inline(XDR *, uint_t);
+static void xdrmem_destroy(XDR *);
+
+static struct xdr_ops xdrmem_ops = {
+ xdrmem_getlong,
+ xdrmem_putlong,
+ xdrmem_getbytes,
+ xdrmem_putbytes,
+ xdrmem_getpos,
+ xdrmem_setpos,
+ xdrmem_inline,
+ xdrmem_destroy
+};
+
+/*
+ * The procedure xdrmem_create initializes a stream descriptor for a
+ * memory buffer.
+ */
+void
+xdrmem_create(
+ XDR *xdrs,
+ void *addr,
+ uint_t size,
+ enum xdr_op op)
+{
+
+ xdrs->x_op = op;
+ xdrs->x_ops = &xdrmem_ops;
+ xdrs->x_private = xdrs->x_base = addr;
+ xdrs->x_handy = size;
+}
+
+/* ARGSUSED */
+static void
+xdrmem_destroy(XDR *xdrs)
+{
+}
+
+static bool_t
+xdrmem_getlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+ return (FALSE);
+ *lp = (int32_t)ntohl((uint32_t)(*((int32_t *)(xdrs->x_private))));
+ xdrs->x_private += sizeof(int32_t);
+ return (TRUE);
+}
+
+static bool_t
+xdrmem_putlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+ return (FALSE);
+ *(int32_t *)xdrs->x_private = (int32_t)htonl((uint32_t)(*lp));
+ xdrs->x_private += sizeof(int32_t);
+ return (TRUE);
+}
+
+static bool_t
+xdrmem_getbytes(
+ XDR *xdrs,
+ void *addr,
+ uint_t len)
+{
+
+ if ((xdrs->x_handy -= len) < 0)
+ return (FALSE);
+ memmove(addr, xdrs->x_private, len);
+ xdrs->x_private += len;
+ return (TRUE);
+}
+
+static bool_t
+xdrmem_putbytes(
+ XDR *xdrs,
+ void *addr,
+ uint_t len)
+{
+
+ if ((xdrs->x_handy -= len) < 0)
+ return (FALSE);
+ memmove(xdrs->x_private, addr, len);
+ xdrs->x_private += len;
+ return (TRUE);
+}
+
+static uint_t
+xdrmem_getpos(
+ XDR *xdrs)
+{
+
+ return ((uint_t)xdrs->x_private - (uint_t)xdrs->x_base);
+}
+
+static bool_t
+xdrmem_setpos(
+ XDR *xdrs,
+ uint_t pos)
+{
+ char * newaddr = xdrs->x_base + pos;
+ char * lastaddr = xdrs->x_private + xdrs->x_handy;
+
+ if ((int32_t)newaddr > (int32_t)lastaddr)
+ return (FALSE);
+ xdrs->x_private = newaddr;
+ xdrs->x_handy = (int)lastaddr - (int)newaddr;
+ return (TRUE);
+}
+
+static int32_t *
+xdrmem_inline(
+ XDR *xdrs,
+ uint_t len)
+{
+ int32_t *buf = 0;
+
+ if (xdrs->x_handy >= len) {
+ xdrs->x_handy -= len;
+ buf = (int32_t *) xdrs->x_private;
+ xdrs->x_private += len;
+ }
+ return (buf);
+}
--- /dev/null
+#ifndef lint
+static char sfs_xdr_rec_id[] = "@(#)xdr_rec.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking"
+ * layer above tcp (for rpc's use).
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * These routines interface XDRSTREAMS to a tcp/ip connection.
+ * There is a record marking layer between the xdr stream
+ * and the tcp transport level. A record is composed on one or more
+ * record fragments. A record fragment is a thirty-two bit header followed
+ * by n bytes of data, where n is contained in the header. The header
+ * is represented as a htonl(uint32_t). Thegh order bit encodes
+ * whether or not the fragment is the last fragment of the record
+ * (1 => fragment is last, 0 => more fragments to follow.
+ * The other 31 bits encode the byte length of the fragment.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/osdep.h"
+
+static bool_t xdrrec_getlong(XDR *, int32_t *);
+static bool_t xdrrec_putlong(XDR *, int32_t *);
+static bool_t xdrrec_getbytes(XDR *, void *, uint_t);
+static bool_t xdrrec_putbytes(XDR *, void *, uint_t);
+static uint_t xdrrec_getpos(XDR *);
+static bool_t xdrrec_setpos(XDR *, uint_t);
+static int32_t *xdrrec_inline(XDR *, uint_t);
+static void xdrrec_destroy(XDR *);
+
+static struct xdr_ops xdrrec_ops = {
+ xdrrec_getlong,
+ xdrrec_putlong,
+ xdrrec_getbytes,
+ xdrrec_putbytes,
+ xdrrec_getpos,
+ xdrrec_setpos,
+ xdrrec_inline,
+ xdrrec_destroy
+};
+
+/*
+ * A record is composed of one or more record fragments.
+ * A record fragment is a two-byte header followed by zero to
+ * 2**32-1 bytes. The header is treated as a long unsigned and is
+ * encode/decoded to the network via htonl/ntohl. The low order 31 bits
+ * are a byte count of the fragment. The highest order bit is a boolean:
+ * 1 => this fragment is the last fragment of the record,
+ * 0 => this fragment is followed by more fragment(s).
+ *
+ * The fragment/record machinery is not general; it is constructed to
+ * meet the needs of xdr and rpc based on tcp.
+ */
+
+#define LAST_FRAG (0x80000000)
+
+typedef struct rec_strm {
+ void * tcp_handle;
+ void * the_buffer;
+ /*
+ * out-goung bits
+ */
+ int (*writeit)();
+ char *out_base; /* output buffer (points to frag header) */
+ char *out_finger; /* next output position */
+ char *out_boundry; /* data cannot up to this address */
+ uint32_t *frag_header; /* beginning of curren fragment */
+ bool_t frag_sent; /* true if buffer sent in middle of record */
+ /*
+ * in-coming bits
+ */
+ int (*readit)();
+ uint32_t in_size; /* fixed size of the input buffer */
+ char *in_base;
+ char *in_finger; /* location of next byte to be had */
+ char *in_boundry; /* can read up to this location */
+ int32_t fbtbc; /* fragment bytes to be consumed */
+ bool_t last_frag;
+ uint_t sendsize;
+ uint_t recvsize;
+} RECSTREAM;
+
+
+static uint_t fix_buf_size(uint_t);
+static bool_t flush_out(RECSTREAM *, bool_t);
+static bool_t set_input_fragment(RECSTREAM *);
+static bool_t skip_input_bytes(RECSTREAM *, int32_t);
+static bool_t get_input_bytes(RECSTREAM *, char *, int);
+
+/*
+ * Create an xdr handle for xdrrec
+ * xdrrec_create fills in xdrs. Sendsize and recvsize are
+ * send and recv buffer sizes (0 => use default).
+ * tcp_handle is an opaque handle that is passed as the first parameter to
+ * the procedures readit and writeit. Readit and writeit are read and
+ * write respectively. They are like the system
+ * calls expect that they take an opaque handle rather than an fd.
+ */
+void
+xdrrec_create(
+ XDR *xdrs,
+ uint_t sendsize,
+ uint_t recvsize,
+ void * tcp_handle,
+ int (*readit)(), /* like read, but pass it a tcp_handle, not sock */
+ int (*writeit)()) /* like write, but pass it a tcp_handle, not sock */
+{
+ RECSTREAM *rstrm =
+ (RECSTREAM *)mem_alloc(sizeof(RECSTREAM));
+
+ if (rstrm == NULL) {
+ (void)fprintf(stderr, "xdrrec_create: out of memory\n");
+ /*
+ * This is bad. Should rework xdrrec_create to
+ * return a handle, and in this case return NULL
+ */
+ return;
+ }
+ /*
+ * adjust sizes and allocate buffer quad byte aligned
+ */
+ rstrm->sendsize = sendsize = fix_buf_size(sendsize);
+ rstrm->recvsize = recvsize = fix_buf_size(recvsize);
+ rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT);
+ if (rstrm->the_buffer == NULL) {
+ (void)fprintf(stderr, "xdrrec_create: out of memory\n");
+ return;
+ }
+ for (rstrm->out_base = rstrm->the_buffer;
+ (uint_t)rstrm->out_base % BYTES_PER_XDR_UNIT != 0;
+ rstrm->out_base++);
+ rstrm->in_base = rstrm->out_base + sendsize;
+ /*
+ * now the rest ...
+ */
+ xdrs->x_ops = &xdrrec_ops;
+ xdrs->x_private = (void *)rstrm;
+ rstrm->tcp_handle = tcp_handle;
+ rstrm->readit = readit;
+ rstrm->writeit = writeit;
+ rstrm->out_finger = rstrm->out_boundry = rstrm->out_base;
+ rstrm->frag_header = (uint32_t *)rstrm->out_base;
+ rstrm->out_finger += sizeof(uint32_t);
+ rstrm->out_boundry += sendsize;
+ rstrm->frag_sent = FALSE;
+ rstrm->in_size = recvsize;
+ rstrm->in_boundry = rstrm->in_base;
+ rstrm->in_finger = (rstrm->in_boundry += recvsize);
+ rstrm->fbtbc = 0;
+ rstrm->last_frag = TRUE;
+}
+
+
+/*
+ * The reoutines defined below are the xdr ops which will go into the
+ * xdr handle filled in by xdrrec_create.
+ */
+
+static bool_t
+xdrrec_getlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+ int32_t *buflp = (int32_t *)(rstrm->in_finger);
+ int32_t mylong;
+
+ /* first try the inline, fast case */
+ if ((rstrm->fbtbc >= sizeof(int32_t)) &&
+ (((int)rstrm->in_boundry - (int)buflp) >= sizeof(int32_t))) {
+ *lp = (int32_t)ntohl((uint32_t)(*buflp));
+ rstrm->fbtbc -= sizeof(int32_t);
+ rstrm->in_finger += sizeof(int32_t);
+ } else {
+ if (! xdrrec_getbytes(xdrs, (void *)&mylong, sizeof(int32_t)))
+ return (FALSE);
+ *lp = (int32_t)ntohl((uint32_t)mylong);
+ }
+ return (TRUE);
+}
+
+static bool_t
+xdrrec_putlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+ int32_t *dest_lp = ((int32_t *)(rstrm->out_finger));
+
+ if ((rstrm->out_finger += sizeof(int32_t)) > rstrm->out_boundry) {
+ /*
+ * this case should almost never happen so the code is
+ * inefficient
+ */
+ rstrm->out_finger -= sizeof(int32_t);
+ rstrm->frag_sent = TRUE;
+ if (! flush_out(rstrm, FALSE))
+ return (FALSE);
+ dest_lp = ((int32_t *)(rstrm->out_finger));
+ rstrm->out_finger += sizeof(int32_t);
+ }
+ *dest_lp = (int32_t)htonl((uint32_t)(*lp));
+ return (TRUE);
+}
+
+static bool_t /* must manage buffers, fragments, and records */
+xdrrec_getbytes(
+ XDR *xdrs,
+ void *baddr,
+ uint_t len)
+{
+ char *addr = baddr;
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+ int current;
+
+ while (len > 0) {
+ current = rstrm->fbtbc;
+ if (current == 0) {
+ if (rstrm->last_frag)
+ return (FALSE);
+ if (! set_input_fragment(rstrm))
+ return (FALSE);
+ continue;
+ }
+ current = (len < current) ? len : current;
+ if (! get_input_bytes(rstrm, addr, current))
+ return (FALSE);
+ addr += current;
+ rstrm->fbtbc -= current;
+ len -= current;
+ }
+ return (TRUE);
+}
+
+static bool_t
+xdrrec_putbytes(
+ XDR *xdrs,
+ void *baddr,
+ uint_t len)
+{
+ char *addr = baddr;
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+ int current;
+
+ while (len > 0) {
+ current = (uint_t)rstrm->out_boundry - (uint_t)rstrm->out_finger;
+ current = (len < current) ? len : current;
+ memmove(rstrm->out_finger, addr, current);
+ rstrm->out_finger += current;
+ addr += current;
+ len -= current;
+ if (rstrm->out_finger == rstrm->out_boundry) {
+ rstrm->frag_sent = TRUE;
+ if (! flush_out(rstrm, FALSE))
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+static uint_t
+xdrrec_getpos(
+ XDR *xdrs)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
+ int32_t pos;
+
+ pos = lseek((int)rstrm->tcp_handle, (int32_t) 0, 1);
+ if (pos != -1)
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ pos += rstrm->out_finger - rstrm->out_base;
+ break;
+
+ case XDR_DECODE:
+ pos -= rstrm->in_boundry - rstrm->in_finger;
+ break;
+
+ default:
+ pos = (uint_t) -1;
+ break;
+ }
+ return ((uint_t) pos);
+}
+
+static bool_t
+xdrrec_setpos(
+ XDR *xdrs,
+ uint_t pos)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
+ uint_t currpos = xdrrec_getpos(xdrs);
+ int delta = currpos - pos;
+ char *newpos;
+
+ if ((int)currpos != -1)
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ newpos = rstrm->out_finger - delta;
+ if ((newpos > (char *)(rstrm->frag_header)) &&
+ (newpos < rstrm->out_boundry)) {
+ rstrm->out_finger = newpos;
+ return (TRUE);
+ }
+ break;
+
+ case XDR_DECODE:
+ newpos = rstrm->in_finger - delta;
+ if ((delta < (int)(rstrm->fbtbc)) &&
+ (newpos <= rstrm->in_boundry) &&
+ (newpos >= rstrm->in_base)) {
+ rstrm->in_finger = newpos;
+ rstrm->fbtbc -= delta;
+ return (TRUE);
+ }
+ break;
+ }
+ return (FALSE);
+}
+
+static int32_t *
+xdrrec_inline(
+ XDR *xdrs,
+ uint_t len)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
+ int32_t * buf = NULL;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ if ((rstrm->out_finger + len) <= rstrm->out_boundry) {
+ buf = (int32_t *) rstrm->out_finger;
+ rstrm->out_finger += len;
+ }
+ break;
+
+ case XDR_DECODE:
+ if ((len <= rstrm->fbtbc) &&
+ ((rstrm->in_finger + len) <= rstrm->in_boundry)) {
+ buf = (int32_t *) rstrm->in_finger;
+ rstrm->fbtbc -= len;
+ rstrm->in_finger += len;
+ }
+ break;
+ }
+ return (buf);
+}
+
+static void
+xdrrec_destroy(
+ XDR *xdrs)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private;
+
+ mem_free(rstrm->the_buffer,
+ rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT);
+ mem_free((void *)rstrm, sizeof(RECSTREAM));
+}
+
+
+/*
+ * Exported routines to manage xdr records
+ */
+
+/*
+ * Before reading (deserializing from the stream, one should always call
+ * this procedure to guarantee proper record alignment.
+ */
+bool_t
+xdrrec_skiprecord(
+ XDR *xdrs)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+
+ while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
+ if (! skip_input_bytes(rstrm, rstrm->fbtbc))
+ return (FALSE);
+ rstrm->fbtbc = 0;
+ if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
+ return (FALSE);
+ }
+ rstrm->last_frag = FALSE;
+ return (TRUE);
+}
+
+/*
+ * Look ahead fuction.
+ * Returns TRUE iff there is no more input in the buffer
+ * after consuming the rest of the current record.
+ */
+bool_t
+xdrrec_eof(
+ XDR *xdrs)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+
+ while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) {
+ if (! skip_input_bytes(rstrm, rstrm->fbtbc))
+ return (TRUE);
+ rstrm->fbtbc = 0;
+ if ((! rstrm->last_frag) && (! set_input_fragment(rstrm)))
+ return (TRUE);
+ }
+ if (rstrm->in_finger == rstrm->in_boundry)
+ return (TRUE);
+ return (FALSE);
+}
+
+/*
+ * The client must tell the package when an end-of-record has occurred.
+ * The second paraemters tells whether the record should be flushed to the
+ * (output) tcp stream. (This let's the package support batched or
+ * pipelined procedure calls.) TRUE => immmediate flush to tcp connection.
+ */
+bool_t
+xdrrec_endofrecord(
+ XDR *xdrs,
+ bool_t sendnow)
+{
+ RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private);
+ uint32_t len; /* fragment length */
+
+ if (sendnow || rstrm->frag_sent ||
+ ((uint32_t)rstrm->out_finger + sizeof(uint32_t) >=
+ (uint32_t)rstrm->out_boundry)) {
+ rstrm->frag_sent = FALSE;
+ return (flush_out(rstrm, TRUE));
+ }
+ len = (uint32_t)(rstrm->out_finger) - (uint32_t)(rstrm->frag_header) -
+ sizeof(uint32_t);
+ *(rstrm->frag_header) = htonl((uint32_t)len | (uint32_t)LAST_FRAG);
+ rstrm->frag_header = (uint32_t *)rstrm->out_finger;
+ rstrm->out_finger += sizeof(uint32_t);
+ return (TRUE);
+}
+
+
+/*
+ * Internal useful routines
+ */
+static bool_t
+flush_out(
+ RECSTREAM *rstrm,
+ bool_t eor)
+{
+ uint32_t eormask = (eor == TRUE) ? LAST_FRAG : 0;
+ uint32_t len = (uint32_t)(rstrm->out_finger) -
+ (uint32_t)(rstrm->frag_header) - sizeof(uint32_t);
+
+ *(rstrm->frag_header) = htonl(len | eormask);
+ len = (uint32_t)(rstrm->out_finger) - (uint32_t)(rstrm->out_base);
+ if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len)
+ != (int)len)
+ return (FALSE);
+ rstrm->frag_header = (uint32_t *)rstrm->out_base;
+ rstrm->out_finger = (char *)rstrm->out_base + sizeof(uint32_t);
+ return (TRUE);
+}
+
+static bool_t /* knows nothing about records! Only about input buffers */
+fill_input_buf(
+ RECSTREAM *rstrm)
+{
+ char *where;
+ uint_t i;
+ int len;
+
+ where = rstrm->in_base;
+ i = (uint_t)rstrm->in_boundry % BYTES_PER_XDR_UNIT;
+ where += i;
+ len = rstrm->in_size - i;
+ if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1)
+ return (FALSE);
+ rstrm->in_finger = where;
+ where += len;
+ rstrm->in_boundry = where;
+ return (TRUE);
+}
+
+static bool_t /* knows nothing about records! Only about input buffers */
+get_input_bytes(
+ RECSTREAM *rstrm,
+ char *addr,
+ int len)
+{
+ int current;
+
+ while (len > 0) {
+ current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
+ if (current == 0) {
+ if (! fill_input_buf(rstrm))
+ return (FALSE);
+ continue;
+ }
+ current = (len < current) ? len : current;
+ memmove(addr, rstrm->in_finger, current);
+ rstrm->in_finger += current;
+ addr += current;
+ len -= current;
+ }
+ return (TRUE);
+}
+
+static bool_t /* next two bytes of the input stream are treated as a header */
+set_input_fragment(
+ RECSTREAM *rstrm)
+{
+ uint32_t header;
+
+ if (! get_input_bytes(rstrm, (void *)&header, sizeof(header)))
+ return (FALSE);
+ header = (int32_t)ntohl(header);
+ rstrm->last_frag = ((header & (uint32_t)LAST_FRAG) == 0) ? FALSE : TRUE;
+ rstrm->fbtbc = header & (~LAST_FRAG);
+ return (TRUE);
+}
+
+static bool_t /* consumes input bytes; knows nothing about records! */
+skip_input_bytes(
+ RECSTREAM *rstrm,
+ int32_t cnt)
+{
+ int current;
+
+ while (cnt > 0) {
+ current = (int)rstrm->in_boundry - (int)rstrm->in_finger;
+ if (current == 0) {
+ if (! fill_input_buf(rstrm))
+ return (FALSE);
+ continue;
+ }
+ current = (cnt < current) ? cnt : current;
+ rstrm->in_finger += current;
+ cnt -= current;
+ }
+ return (TRUE);
+}
+
+static uint_t
+fix_buf_size(
+ uint_t s)
+{
+
+ if (s < 100)
+ s = 4000;
+ return (RNDUP(s));
+}
--- /dev/null
+#ifndef lint
+static char sfs_xdr_reference_id[] = "@(#)xdr_reference.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_reference.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_reference.c 1.11 87/08/11 SMI";
+#endif
+
+/*
+ * xdr_reference.c, Generic XDR routines impelmentation.
+ *
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ *
+ * These are the "non-trivial" xdr primitives used to serialize and de-serialize
+ * "pointers". See xdr.h for more info on the interface to xdr.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+
+#define LASTUNSIGNED ((uint_t)0-1)
+
+/*
+ * XDR an indirect pointer
+ * xdr_reference is for recursively translating a structure that is
+ * referenced by a pointer inside the structure that is currently being
+ * translated. pp references a pointer to storage. If *pp is null
+ * the necessary storage is allocated.
+ * size is the sizeof the referneced structure.
+ * proc is the routine to handle the referenced structure.
+ */
+bool_t
+xdr_reference(
+ XDR *xdrs,
+ void **pp, /* the pointer to work on */
+ uint_t size, /* size of the object pointed to */
+ xdrproc_t proc) /* xdr routine to handle the object */
+{
+ void *loc = *pp;
+ bool_t stat;
+
+ if (loc == NULL)
+ switch (xdrs->x_op) {
+ case XDR_FREE:
+ return (TRUE);
+
+ case XDR_DECODE:
+ *pp = loc = (void *) mem_alloc(size);
+ if (loc == NULL) {
+ (void) fprintf(stderr,
+ "xdr_reference: out of memory\n");
+ return (FALSE);
+ }
+ memset(loc, '\0', (int)size);
+ break;
+ }
+
+ stat = (*proc)(xdrs, loc, LASTUNSIGNED);
+
+ if (xdrs->x_op == XDR_FREE) {
+ mem_free(loc, size);
+ *pp = NULL;
+ }
+ return (stat);
+}
+
+
+/*
+ * xdr_pointer():
+ *
+ * XDR a pointer to a possibly recursive data structure. This
+ * differs with xdr_reference in that it can serialize/deserialiaze
+ * trees correctly.
+ *
+ * What's sent is actually a union:
+ *
+ * union object_pointer switch (boolean b) {
+ * case TRUE: object_data data;
+ * case FALSE: void nothing;
+ * }
+ *
+ * > objpp: Pointer to the pointer to the object.
+ * > obj_size: size of the object.
+ * > xdr_obj: routine to XDR an object.
+ *
+ */
+bool_t
+xdr_pointer(
+ XDR *xdrs,
+ void **objpp,
+ uint_t obj_size,
+ xdrproc_t xdr_obj)
+{
+
+ bool_t more_data;
+
+ more_data = (*objpp != NULL);
+ if (! xdr_bool(xdrs,&more_data)) {
+ return (FALSE);
+ }
+ if (! more_data) {
+ *objpp = NULL;
+ return (TRUE);
+ }
+ return (xdr_reference(xdrs,objpp,obj_size,xdr_obj));
+}
--- /dev/null
+#ifndef lint
+static char sfs_xdr_stdio_id[] = "@(#)xdr_stdio.c 2.1 97/10/23";
+#endif
+/* @(#)xdr_stdio.c 2.1 88/07/29 4.0 RPCSRC */
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)xdr_stdio.c 1.16 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * xdr_stdio.c, XDR implementation on standard i/o file.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * This set of routines implements a XDR on a stdio stream.
+ * XDR_ENCODE serializes onto the stream, XDR_DECODE de-serializes
+ * from the stream.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "rpc/types.h"
+#include "rpc/xdr.h"
+#include "rpc/osdep.h"
+
+static bool_t xdrstdio_getlong(XDR *, int32_t *);
+static bool_t xdrstdio_putlong(XDR *, int32_t *);
+static bool_t xdrstdio_getbytes(XDR *, void *, uint_t);
+static bool_t xdrstdio_putbytes(XDR *, void *, uint_t);
+static uint_t xdrstdio_getpos(XDR *);
+static bool_t xdrstdio_setpos(XDR *, uint_t);
+static int32_t *xdrstdio_inline(XDR *, uint_t);
+static void xdrstdio_destroy(XDR *);
+
+/*
+ * Ops vector for stdio type XDR
+ */
+static struct xdr_ops xdrstdio_ops = {
+ xdrstdio_getlong, /* deseraialize a long int */
+ xdrstdio_putlong, /* seraialize a long int */
+ xdrstdio_getbytes, /* deserialize counted bytes */
+ xdrstdio_putbytes, /* serialize counted bytes */
+ xdrstdio_getpos, /* get offset in the stream */
+ xdrstdio_setpos, /* set offset in the stream */
+ xdrstdio_inline, /* prime stream for inline macros */
+ xdrstdio_destroy /* destroy stream */
+};
+
+/*
+ * Initialize a stdio xdr stream.
+ * Sets the xdr stream handle xdrs for use on the stream file.
+ * Operation flag is set to op.
+ */
+void
+xdrstdio_create(
+ XDR *xdrs,
+ FILE *file,
+ enum xdr_op op)
+{
+
+ xdrs->x_op = op;
+ xdrs->x_ops = &xdrstdio_ops;
+ xdrs->x_private = (void *)file;
+ xdrs->x_handy = 0;
+ xdrs->x_base = 0;
+}
+
+/*
+ * Destroy a stdio xdr stream.
+ * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create.
+ */
+static void
+xdrstdio_destroy(
+ XDR *xdrs)
+{
+ (void)fflush((FILE *)xdrs->x_private);
+ /* xx should we close the file ?? */
+}
+
+static bool_t
+xdrstdio_getlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ if (fread((void *)lp, sizeof(int32_t), 1, (FILE *)xdrs->x_private) != 1)
+ return (FALSE);
+ *lp = ntohl(*lp);
+ return (TRUE);
+}
+
+static bool_t
+xdrstdio_putlong(
+ XDR *xdrs,
+ int32_t *lp)
+{
+
+ int32_t mycopy = htonl(*lp);
+ lp = &mycopy;
+
+ if (fwrite((void *)lp, sizeof(int32_t), 1, (FILE *)xdrs->x_private) != 1)
+ return (FALSE);
+ return (TRUE);
+}
+
+static bool_t
+xdrstdio_getbytes(
+ XDR *xdrs,
+ void * addr,
+ uint_t len)
+{
+
+ if ((len != 0) && (fread(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1))
+ return (FALSE);
+ return (TRUE);
+}
+
+static bool_t
+xdrstdio_putbytes(
+ XDR *xdrs,
+ void * addr,
+ uint_t len)
+{
+
+ if ((len != 0) && (fwrite(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1))
+ return (FALSE);
+ return (TRUE);
+}
+
+static uint_t
+xdrstdio_getpos(
+ XDR *xdrs)
+{
+
+ return ((uint_t) ftell((FILE *)xdrs->x_private));
+}
+
+static bool_t
+xdrstdio_setpos(
+ XDR *xdrs,
+ uint_t pos)
+{
+
+ return ((fseek((FILE *)xdrs->x_private, (int32_t)pos, 0) < 0) ?
+ FALSE : TRUE);
+}
+
+/* ARGSUSED */
+static int32_t *
+xdrstdio_inline(
+ XDR *xdrs,
+ uint_t len)
+{
+
+ /*
+ * Must do some work to implement this: must insure
+ * enough data in the underlying stdio buffer,
+ * that the buffer is aligned so that we can indirect through a
+ * int32_t *, and stuff this pointer in xdrs->x_buf. Doing
+ * a fread or fwrite to a scratch buffer would defeat
+ * most of the gains to be had here and require storage
+ * management on this buffer, so we don't do this.
+ */
+ return (NULL);
+}
--- /dev/null
+#include <sys/sem.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+extern int errno;
+
+#define SEMPERM 0777
+#define TRUE 1
+#define FALSE 0
+
+#define MAX_SEM 50
+struct {
+ int semid;
+ char name[256];
+} semname[MAX_SEM] =
+{
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+/*
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""},
+*/
+{0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}, {0, ""}
+} ;
+
+
+/* semaphore operations */
+
+int initsem(key_t key, int value) /* create a semaphore */
+{
+ int status = 0;
+ int semid;
+
+ if ((semid = semget(key, 1, SEMPERM|IPC_CREAT|IPC_EXCL)) == -1) {
+ if (errno == EEXIST) {
+ printf("semget key %d already exist\n", key);
+ semid = semget(key, 1, 0);
+ }
+ } else {
+ status = semctl(semid, 0, SETVAL, value);
+ }
+
+ if (semid == -1 || status == -1) {
+ printf("%d:\n", getpid());
+ perror("initsem() failed");
+ return (-1);
+ } else
+ return semid;
+}
+
+//int getsem(key_t key, int setval) /* create a semaphore */
+int getsem(key_t key) /* create a semaphore */
+{
+ int status = 0;
+ int semid;
+
+ semid = semget(key, 1, 0);
+
+ if (semid == -1 || status == -1) {
+ printf("%d:\n", getpid());
+ perror("initsem() failed");
+ return (-1);
+ } else
+ return semid;
+}
+
+int waitsem(int semid) /* wait on a semaphore */
+{
+ struct sembuf p_buf;
+#ifdef NO_SEM
+ return;
+#endif
+
+ p_buf.sem_num = 0;
+ p_buf.sem_op = -1;
+ p_buf.sem_flg = SEM_UNDO;
+
+ if (semop(semid, &p_buf, 1) == -1) {
+ printf("%d:", getpid());
+ perror("waitsem() failed");
+ exit(1);
+ } else
+ return (0);
+}
+
+int postsem(int semid) /* post to a semaphore */
+{
+ struct sembuf v_buf;
+
+#ifdef NO_SEM
+ return;
+#endif
+
+
+ v_buf.sem_num = 0;
+ v_buf.sem_op = 1;
+ v_buf.sem_flg = SEM_UNDO;
+
+ if (semop(semid, &v_buf, 1) == -1) {
+ printf("%d:", getpid());
+ perror("postsem() failed");
+ exit(1);
+ } else
+ return (0);
+}
+
+void destsem(int semid) /* destroy a semaphore */
+{
+ semctl(semid, 0, IPC_RMID, 0);
+}
+
+int dest_and_init_sem (key_t key, int value, char * name)
+{
+ int semid;
+ int i;
+ semid = getsem (key);
+ if (semid!=-1)
+ destsem (semid);
+ semid = initsem (key, value);
+ printf ("%s semid %d for key %d value %d \n", name, semid, key, value);
+
+ if (semid == 0) {
+ printf ("semid == 0\n");
+ exit(0);
+ }
+
+ for (i=0; i<MAX_SEM; i++) {
+ if (semname[i].semid == 0) {
+ semname[i].semid = semid;
+ strcpy(semname[i].name, name);
+ break;
+ }
+ }
+
+ if (i==MAX_SEM) {
+ printf ("semname full\n");
+ exit (-1);
+ }
+
+ return semid;
+}
+
+
--- /dev/null
+.\" @(#)sfs.1 2.1 97/10/23
+.\" See DESCR.SFS file for restrictions
+.\"
+.\" create man page by running 'tbl sfs.1 | nroff -man > sfs.cat'
+.\"
+.TH SFS 1 "5 October 1994"
+.SH NAME
+SFS \- Network File System server benchmark program
+.SH SYNOPSIS
+.B sfs
+[
+.B \-a access_pcnt
+] [
+.B \-A append_pcnt
+]
+.br
+[
+.B \-b blocksz_file
+] [
+.B \-B block_size
+] [
+.B \-d debug_level
+]
+.br
+[
+.B \-D dir_cnt
+] [
+.B \-f file_set_delta
+] [
+.B \-F file_cnt
+] [
+.B \-i
+] [
+.B \-l load
+]
+.br
+[
+.B \-m mix_file
+] [
+.B \-M prime_client_hostname
+]
+.br
+[
+.B \-N client_num
+] [
+.B \-p processes
+] [
+.B \-P
+] [
+.B \-Q
+]
+.br
+[
+.B \-R biod_reads
+] [
+[
+.B \-S symlink_cnt
+] [
+.B \-t time
+] [
+.B \-T
+]
+.br
+[
+.B \-V
+] [
+.B \-W biod_writes
+] [
+.B \-w warmup_time
+] [
+.B \-z
+]
+.br
+[
+.B server:/directory ...
+]
+.LP
+.B sfs3
+[
+.B \-a access_pcnt
+] [
+.B \-A append_pcnt
+]
+.br
+[
+.B \-b blocksz_file
+] [
+.B \-B block_size
+] [
+.B \-d debug_level
+]
+.br
+[
+.B \-D dir_cnt
+] [
+.B \-f file_set_delta
+] [
+.B \-F file_cnt
+] [
+.B \-i
+] [
+.B \-l load
+]
+.br
+[
+.B \-m mix_file
+] [
+.B \-M prime_client_hostname
+]
+.br
+[
+.B \-N client_num
+] [
+.B \-p processes
+] [
+.B \-P
+] [
+.B \-Q
+]
+.br
+[
+.B \-R biod_reads
+] [
+[
+.B \-S symlink_cnt
+] [
+.B \-t time
+] [
+.B \-T
+]
+.br
+[
+.B \-V
+] [
+.B \-W biod_writes
+] [
+.B \-w warmup_time
+] [
+.B \-z
+]
+.br
+[
+.B server:/directory ...
+]
+.SH DESCRIPTION
+Normally,
+.B SFS
+is executed via the
+.B sfs_mgr
+script which controls
+.B SFS
+execution on one or more
+.SM NFS
+client systems using a single user interface.
+.P
+.B SFS
+is a Network File System server benchmark.
+It runs on one or more
+.SM NFS
+client machines to generate an artificial load
+consisting of a particular mix of
+.SM NFS
+operations on a particular set of files
+residing on the server being tested.
+The benchmark reports the average response time of the
+.SM NFS
+requests in milliseconds for the requested target load.
+The response time is the dependent variable.
+Load can be generated for a specific amount of time,
+or for a specific number of
+.SM NFS
+calls.
+.P
+.B SFS
+can also be used to characterize server performance.
+Nearly all of the major factors that influence NFS performance
+can be controlled using
+.B SFS
+command line arguments. Normally however, only the
+.B \-l load
+option used.
+Other commonly used options include the
+.B \-t time
+,
+.B \-p processes
+,
+.B \-m mix_file
+options.
+The remaining options are used to adjust specific parameters that affect
+.SM NFS
+performance.
+If these options are used, the results produced will be non\-standard,
+and thus, will not be comparable to tests run with other option settings.
+.P
+Normally,
+.B SFS
+is used as a benchmark program to measure the
+.SM NFS
+performance
+of a particular server machine at different load levels.
+In this case,
+the preferred measurement is to make a series of benchmark runs,
+varying the load factor for each run
+in order to produce a performance/load curve.
+Each point in the curve
+represents the server's response time at a specific load.
+.P
+.B SFS
+generates and transmits
+.SM NFS
+packets over the network to the server directly from the benchmark program,
+without using the client's local NFS service.
+This reduces the effect of the client NFS implementation on results,
+and makes comparison of different servers more client-independent.
+However, not all client implementation effects have been eliminated.
+Since the benchmark does by-pass much of the client NFS implementation
+(including operating system level data caching and write behind),
+.B SFS
+can only be used to measure server performance.
+.P
+Although
+.B SFS
+can be run between a single client and single server pair of machines,
+a more accurate measure of server performance is obtained
+by executing the benchmark on a number of clients simultaneously.
+Not only does this present a more realistic model of
+.SM NFS
+usage, but also improves the chances that maximum performance
+is limited by a lack of resources on the server machine,
+rather than on a single client machine.
+.P
+In order to facilitate running
+.B SFS
+on a number of clients simultaneously,
+an accompanying program called
+.B sfs_mgr
+provides a mechanism to run and synchronize the execution of multiple
+instances of
+.B SFS
+spread across multiple clients and multiple networks
+all generating load to the same
+.SM NFS
+server.
+In general,
+.B sfs_mgr
+should be used to run both single- and multiple-client tests.
+.P
+.B SFS
+employs a number of sub\-processes, each with its own test directory named
+.B ./testdir<n>
+(where <n> is a number from 0 to
+.B processes
+\- 1.)
+A standard set of files is created in the test directory.
+.P
+If multiple
+.B directories
+are specified on the command line, the
+.B SFS
+processes will be evenly distributed among the directories.
+This will produce a balanced load across each of the directories.
+.P
+The mix of
+.SM NFS
+operations generated can be set from a user defined mix file.
+The format of the file consists of a simple format, the first
+line contains the string "SFS MIXFILE VERSION 2" followed by
+each line containing the operation name and the percentage (eg.
+"write 12"). The total percentages must equal 100.
+Operations with not specified will never be called by
+.B SFS.
+.SH SFS OPTIONS
+.TP
+.B \-a access_pcnt
+The percentage of I/O files to access.
+The access percent can be set from 0 to 100 percent.
+The default is 10% access.
+.TP
+.B \-A append_pcnt
+The percentage of write operations that append data to files
+rather than overwriting existing data.
+The append percent can be set from 0 to 100 percent.
+The default is 70% append.
+.TP
+.B \-b blocksz_file
+The name of a file containing a block transfer size distribution specification.
+The format of the file and the default values are discussed below
+under "Block Size Distribution".
+.TP
+.B \-B block_size
+The maximum number of Kilo-bytes (KB) contained in any one data transfer block.
+The valid range of values is from 1 to 8 KB.
+The default maximum is 8 KB per transfer.
+.TP
+.B \-d debug_level
+The debugging level, with higher values producing more debugging output.
+When the benchmark is executed with debugging enabled,
+the results are invalid.
+The debug level must be greater than zero.
+.TP
+.B \-D dir_cnt
+The number of files in each directory, the number of directories varies with
+load load.
+The default is 30 files per directory.
+.TP
+.B \-f file_set_delta
+The percentage change in file set size.
+The change percent can be set from 0 to 100 percent.
+The default is 10% append.
+.TP
+.B \-F file_cnt
+The number of files to be used for read and write operations.
+The file count must be greater than 0.
+The default is 100 files.
+.TP
+.B \-i
+Run the test in interactive mode,
+pausing for user input before starting the test.
+The default setting is to run the test automatically.
+.TP
+.B \-l load
+The number of NFS calls per second to generate from each client machine.
+The load must be greater than the number of processes
+(see the "\-p processes" option).
+The default is to generate 60 calls/sec on each/client.
+.TP
+.B \-m mix_file
+The name of a file containing the operation mix specification.
+The format of the file and the default values are discussed below
+under "Operation Mix".
+.TP
+.B \-p processes
+The number of processes used to generate load on each client machine.
+The number of processes must be greater than 0.
+The default is 4 processes per client.
+.TP
+.B \-P
+Populate the test directories and then exit; don't run the test.
+This option can be used to examine the file set that the benchmark creates.
+The default is to populate the test directories and then
+execute the test automatically.
+.TP
+.B \-Q
+Run NFS over TCP.
+The default is UDP.
+.TP
+.B \-R biod_reads
+The maximum number of asynchronus reads issued to simulate biod behavior.
+The default is 2.
+.TP
+.B \-S symlink_cnt
+The number of symbolic links to be used for symlink operations.
+The symbolic link count must be greater than 0.
+The default is 20 symlinks.
+.TP
+.B \-t time
+The number of seconds to run the benchmark.
+The number of seconds must be greater than 0.
+The default is 300 seconds.
+(Run times less than 300 seconds may produce invalid results.)
+.TP
+.B \-T op_num
+Test a particular
+.SM NFS
+operation by executing it once.
+The valid range of operations is from 1 to 23.
+These values correspond to the procedure number
+for each operation type as defined in the
+.SM NFS
+protocol specification.
+The default is to run the benchmark, with no preliminary operation testing.
+.TP
+.B \-V
+Validate the correctness of the server's
+.SM NFS
+implementation.
+The option verifies the correctness of
+.SM NFS
+operations and data copies.
+The verification takes place immediately before executing the test,
+and does not affect the results reported by the test.
+The default is not to verify server
+.SM NFS
+operation before beginning the test.
+.TP
+.B \-z
+Generate raw data dump of the individual data points
+for the test run.
+.TP
+.B \-w warmup
+The number of seconds to generate load before starting the timed test run.
+The goal is to reach a steady state and eliminate any variable startup costs,
+before beginning the test.
+The warm up time must be greater than or equal to 0 seconds.
+The default is a 60 second warmup period.
+.TP
+.B \-W biod_writes
+The maximum number of asynchronus writes issued to simulate biod behavior.
+The default is 2.
+.SH MULTI-CLIENT OPTIONS
+.B SFS
+also recognizes options that are only used when executing a multi-client test.
+These options are generated by
+.B sfs_mgr
+and should not be specified by an end-user.
+.TP
+.B \-M prime_client_hostname
+The hostname of the client machine where a multi-client test
+is being controlled from.
+This machine is designated as the "prime client".
+The prime client machine may also be executing the
+.B SFS
+load-generating code. There is no default value.
+.TP
+.B \-N client_num
+The client machine's unique identifier within a multi-client test,
+assigned by the
+.B sfs_mgr
+script.
+There is no default value.
+.\".TP
+.\".B \-R random_number_seed
+.\"The value used by the client to index into a table of random number seeds.
+.\"There is no default value.
+.SH OPERATION MIX
+The
+.B SFS
+default mix of operations for version 2 is:
+.sp
+.TS
+center;
+l l l l l l
+n n n n n n
+l l l l l l
+n n n n n n
+l l l l l l
+n n n n n n.
+null getattr setattr root lookup readlink
+0% 26% 1% 0% 36% 7%
+read wrcache write create remove rename
+14% 7% 1% 1% 0% 0%
+link symlink mkdir rmdir readdir fsstat
+0% 0% 0% 0% 6% 1%
+.TE
+.LP
+The
+.B SFS
+default mix of operations for version 3 is:
+.sp
+.TS
+center;
+l l l l l l
+n n n n n n
+l l l l l l
+n n n n n n
+l l l l l l
+n n n n n n
+l l l l l l
+n n n n n n.
+null getattr setattr lookup access readlink
+0% 11% 1% 27% 7% 7%
+read write create mkdir symlink mknod
+18% 9% 1% 0% 0% 0%
+remove rmdir rename link readdir readdirplus
+1% 0% 0% 0% 2% 9%
+fsstat fsinfo pathconf commit
+1% 0% 0% 5%
+.TE
+.P
+The format of the file consists of a simple format, the first
+line contains the string "SFS MIXFILE VERSION 2" followed by
+each line containing the operation name and the percentage (eg.
+"write 12"). The total percentages must equal 100.
+.SH FILE SET
+The default basic file set used by
+.B SFS
+consists of regular files varying in size from 1KB to 1MB used for read and
+write operations,
+and 20 symbolic links used for symbolic link operations.
+In addition to these, a small number of regular files are created
+and used for non-I/O operations (eg, getattr),
+and a small number of regular, directory, and symlink files may
+be added to this total due to creation operations (eg, mkdir).
+.P
+While these values can be controlled with command line options,
+some file set configurations may produce invalid results.
+If there are not enough files of a particular type,
+the specified mix of operations will not be achieved.
+Too many files of a particular type may produce
+thrashing effects on the server.
+.SH BLOCK SIZE DISTRIBUTION
+The block transfer size distribution is specified by a table of values.
+The first column gives the percent of operations that will be included in a
+any particular specific block transfer.
+The second column gives the number of blocks units that will be transferred.
+Normally the block unit size is 8KB.
+The third column is a boolean specifying
+whether a trailing fragment block should be transferred.
+The fragment size for each transfer is a random multiple of 1 KB,
+up to the block size - 1 KB.
+Two tables are used, one for read operation and one for write operations.
+The following tables give the default distributions
+for the read and write operations.
+.sp
+.TS
+center;
+c s s s
+c s s s
+r r r r
+r r r r
+c s s s
+n n n r.
+Read - Default Block Transfer Size Distribution Table
+
+ resulting transfer
+percent block count fragment (8KB block size)
+
+0 0 0 0% 0 - 7 KB
+85 1 0 85% 8 - 15 KB
+8 2 1 8% 16 - 23 KB
+4 4 1 4% 32 - 39 KB
+2 8 1 2% 64 - 71 KB
+1 16 1 1% 128 - 135 KB
+.TE
+.sp 2
+.TS
+center;
+c s s s
+c s s s
+r r r r
+r r r r
+c s s s
+n n n r.
+Write - Default Block Transfer Size Distribution Table
+
+ resulting transfer
+percent block count fragment (8KB block size)
+
+49 0 1 49% 0 - 7 KB
+36 1 1 36% 8 - 15 KB
+8 2 1 8% 16 - 23 KB
+4 4 1 4% 32 - 39 KB
+2 8 1 2% 64 - 71 KB
+1 16 1 1% 128 - 135 KB
+.TE
+.P
+A different distribution can be substituted by using the '-b' option.
+The format for the block size distribution file consists of the first
+three columns given above: percent, block count, and fragment. Read
+and write distribution tables are identified by the keywords "Read" and
+"Write". An example input file, using the default values, is given below:
+.sp
+.TS
+l s s
+n n n.
+Read
+0 0 0
+85 1 0
+8 2 1
+4 4 1
+2 8 1
+1 16 1
+.TE
+.TS
+l s s
+n n n.
+Write
+49 0 1
+36 1 1
+8 2 1
+4 4 1
+2 8 1
+1 16 1
+.TE
+.P
+A second aspect of the benchmark controlled
+by the block transfer size distribution table is the network data packet size.
+The distribution tables define the relative proportion
+of full block packets to fragment packets.
+For instance, the default tables have been constructed
+to produce a specific distribution of ethernet packet sizes
+for i/o operations by controlling the amount of data in each packet.
+The write packets produced consist of 50% 8-KB packets, and 50% 1-7 KB packets.
+The read packets consist of 90% 8-KB packets, and 10% 1-7 KB packets.
+These figures are determined by multiplying the percentage
+of type of transfer times the number of blocks and fragments generated,
+and adding the totals.
+These computations are performed below
+for the default block size distribution tables:
+.sp
+.TS
+c c c c c
+c c c c c
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+r r r l l
+r r r n n
+r r r l l.
+Read total total
+percent blocks fragments blocks fragments
+0 0 0 0 0
+85 1 0 85 0
+8 2 1 16 8
+4 4 1 16 4
+2 8 1 16 2
+1 16 1 16 1
+ ---- -----
+ 149 15
+ 90% 10%
+.TE
+.sp 3
+.TS
+r r r r r
+r r r r r
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+n n n n n
+r r r l l
+r r r n n
+r r r l l.
+Write total total
+percent blocks fragments blocks fragments
+49 0 1 0 49
+36 1 1 36 36
+8 2 1 16 8
+4 4 1 16 4
+2 8 1 16 2
+1 16 1 16 1
+ ---- ------
+ 100 100
+ 50% 50%
+.TE
+.SH USING SFS
+As with all benchmarks,
+.B SFS
+can only provide numbers that are useful
+if the test runs are set up carefully.
+Since it measures server performance,
+the client (or clients) should not limit throughput.
+The goal is to determine how well the server performs.
+Most tests involving a single client will be limited by the client's
+ability to generate load, not by the server's ability to handle more load.
+Whether this is the case can be determined by running the benchmark
+at successively greater load levels and finding the "knee of the curve"
+at which load level the response time begins to increase rapidly.
+Having found the knee of the curve, measurements of CPU utilization,
+disk i/o rates, and network utilization levels should be made in order
+to determine whether the performance bottleneck is due to the client
+or server.
+.P
+For the results reported by
+.B SFS
+to be meaningful, the tests should be run on an isolated network,
+and both the client and server should be as quiescent as possible during tests.
+.P
+High error rates on either the client or server
+can also cause delays due to retransmissions
+of lost or damaged packets.
+.B netstat(8)
+can be used to measure the network error
+and collision rates on the client and server.
+Also
+.B SFS
+reports the number of timed-out
+.SM RPC
+calls that occur during the test as bad calls.
+If the number of bad calls is too great,
+or the specified mix of operations is not achieved,
+.B SFS
+reports that the test run is "Invalid".
+In this case, the reported results should be examined
+to determine the cause of the errors.
+.P
+To best simulate the effects of
+.SM NFS
+clients on the server, the test
+directories should be set up so that they are on at least two
+disk partitions exported by the server.
+.SM NFS
+operations tend to randomize disk access,
+so putting all of the
+.B SFS
+test directories on a single partition will not show realistic results.
+.P
+On all tests it is a good idea to run the tests repeatedly and compare results.
+If the difference between runs is large,
+the run time of the test should be increased
+until the variance in milliseconds per call is acceptably small.
+If increasing the length of time does not help,
+there may be something wrong with the experimental setup.
+.P
+The numbers generated by
+.B SFS
+are only useful for comparison if the test setup on the client machine
+is the same across different server configurations.
+Changing the
+.B processes
+or
+.B mix
+parameters will produce numbers that can not be meaningfully compared.
+Changing the number of generator processes may affect the measured response
+time due to context switching or other delays on the client machine,
+while changing the mix of
+.SM NFS
+operations will change the whole nature of the experiment.
+Other changes to the client configuration may also effect the comparability
+of results.
+.P
+To do a comparison of different server configurations, first set up the
+client test directory and do
+.B SFS
+runs at different loads to be sure that the variability is
+reasonably low. Second, run
+.B SFS
+at different loads of interest and
+save the results. Third, change the server configuration (for example,
+add more memory, replace a disk controller, etc.). Finally, run the same
+.B SFS
+loads again and compare the results.
+.SH SEE ALSO
+.P
+The benchmark
+.B README
+file contains many pointers to other
+files which provide information concerning SFS.
+.SH ERROR MESSAGES
+.TP 10
+.B "illegal load value"
+The
+.B load
+argument following the
+.B \-l
+flag on the command line is not a positive number.
+.TP
+.B "illegal procs value"
+The
+.B processes
+argument following the
+.B \-p
+flag on the command line is not a positive number.
+.TP
+.B "illegal time value"
+The
+.B time
+argument following the
+.B \-t
+flag on the command line is not a positive number.
+.TP
+.B "bad mix file"
+The
+.B mix
+file argument following the
+.B \-m
+flag on the command line could not be accessed.
+.TP
+.B "can't fork"
+The parent couldn't fork the child processes. This usually results from
+lack of resources, such as memory or swap space.
+.TP
+.PD 0
+.B "can't open log file"
+.TP
+.B "can't stat log"
+.TP
+.B "can't truncate log"
+.TP
+.B "can't write sync file"
+.TP
+.B "can't write log"
+.TP
+.B "can't read log"
+.PD
+A problem occurred during the creation, truncation, reading or writing of the
+synchronization log file. The parent process creates the
+log file in /tmp and uses it to synchronize and communicate with its children.
+.TP
+.PD 0
+.B "can't open test directory"
+.TP
+.B "can't create test directory"
+.TP
+.B "can't cd to test directory"
+.TP
+.B "wrong permissions on test dir"
+.TP
+.B "can't stat testfile"
+.TP
+.B "wrong permissions on testfile"
+.TP
+.B "can't create rename file"
+.TP
+.B "can't create subdir"
+.PD
+A child process had problems creating or checking the contents of its
+test directory. This is usually due to a permission problem (for example
+the test directory was created by a different user) or a full file system.
+.TP
+.PD 0
+.B "op failed: "
+One of the internal pseudo\-NFS operations failed. The name of the operation,
+e.g. read, write, lookup, will be printed along with an indication of the
+nature of the failure.
+.TP
+.B "select failed"
+The select system call returned an unexpected error.
+.SH BUGS
+.P
+.B SFS
+can not be run on non\-NFS file systems.
+.P
+.P
+Shell scripts that execute
+.B SFS
+must catch and ignore SIGUSR1, SIGUSR2, and SIGALRM, (see signal(3)).
+These signals are used to synchronize the test processes.
+If one of these signals is not caught,
+the shell that is running the script will be killed.
+.SH FILES
+.PD 0
+.TP
+.B ./testdir*
+per process test directory
+.TP
+.B /tmp/sfs_log%d
+child process synchronization file
+.TP
+.B /tmp/sfs_CL%d
+client log file
+.TP
+.B /tmp/sfs_PC_sync
+prime client log file
+.TP
+.B /tmp/sfs_res
+prime results log file
+.PD
--- /dev/null
+#ifndef lint
+static char sfs_c_opsSid[] = "@(#)sfs_2_ops.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_ops.c ---------------------
+ *
+ * RPC routines to implement the NFS protocol.
+ *
+ *.Local Routines
+ * int op_null(void)
+ * int op_getattr(void)
+ * int op_setattr(int)
+ * int op_nosys(void)
+ * int op_lookup(void)
+ * int op_readlink(void)
+ * int op_read(int)
+ * int op_write(int, int, int)
+ * int op_create(void)
+ * int op_remove(void)
+ * int op_rename(void)
+ * int op_link(void)
+ * int op_symlink(void)
+ * int op_mkdir(void)
+ * int op_rmdir(void)
+ * int op_readdir(void)
+ * int op_fsstat(void)
+ *
+ *.Revision_History
+ * 20-Apr-92 Wittle Fix i/o offsets randomization.
+ * 05-Jan-92 Pawlowski Added hooks in for raw data dump.
+ * 04-Dec-91 Keith Define string.h for SYSV/SVR4.
+ * 28-Nov-91 Teelucksingh ANSI C
+ * 01-Aug-91 Santa Wiryaman fix declaration of sfs_srandom()
+ * and sfs_random()
+ * 25-Jun-91 Santa Wiryaman op_rmdir bug fix: when reply==NFS_OK
+ * Cur_file_ptr->state = Nonexistent
+ * 17-May-90 Richard Bean Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * -------------------- Local NFS ops function --------------------
+ */
+static int op_null(void);
+static int op_getattr(void);
+static int op_setattr(int);
+static int op_lookup(void);
+static int op_readlink(void);
+static int op_read(int);
+static int op_write(int, int, int);
+static int op_create(void);
+static int op_remove(void);
+static int op_rename(void);
+static int op_link(void);
+static int op_symlink(void);
+static int op_mkdir(void);
+static int op_rmdir(void);
+static int op_readdir(void);
+static int op_fsstat(void);
+static int op_nosys(void);
+static char *nfs2_strerror(int);
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+static sfs_op_type nfsv2_Ops[] = {
+
+/* name mix function op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 26, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 36, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 14, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 7, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 6, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+
+sfs_op_type *Ops;
+
+/*
+ * -------------------- RPC routines for NFS protocol --------------------
+ */
+
+void
+init_ops(void)
+{
+ Ops = nfsv2_Ops;
+ nfs_version = NFS_VERSION;
+}
+
+/*
+ * The routines below attempt to do over-the-wire operations.
+ * Each op tries to cause one or more of a particular
+ * NFS operation to go over the wire. OPs return the success
+ * of their NFS call(s). Each OP records how many calls it
+ * actually made in global data.
+ *
+ * An array of file information is kept for files existing in
+ * the test directory. File handles, attributes, names, etc
+ * are stored in this array.
+ *
+ */
+
+static int
+op_nosys(void)
+{
+ /*
+ * This is a generic catcher for operations that either don't
+ * exist or were never implemented. We will be
+ * kind and simply mark it as a bad call.
+ */
+ Ops[TOTAL].results.bad_calls++;
+ return(0);
+
+} /* op_nosys */
+
+
+static int
+op_null(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[NULLCALL];
+ ret = 0;
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_NULL,
+ xdr_void, (char *)0,
+ xdr_void, (char *)0,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: null_op call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_null */
+
+
+static int
+op_getattr(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ fhandle_t fh; /* fh to do op on */
+ attrstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[GETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_GETATTR,
+ xdr_getattr, (char *) &fh,
+ xdr_getattr, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->attributes2 = reply.attrstat_u.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_getattr */
+
+
+/*
+ * perform an RPC setattr operation. If 'truncate_size' is non-negative,
+ * truncate the file to that size.
+ */
+static int
+op_setattr(
+ int truncate_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sattrargs args;
+ attrstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ args.attributes.mode = 0666;
+ args.attributes.uid = (unsigned int) -1;
+ args.attributes.gid = (unsigned int) -1;
+ args.attributes.size = (unsigned int) -1;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+
+ /* handle file truncations */
+ if (truncate_size >= 0) {
+ if (truncate_size > Cur_file_ptr->attributes2.size)
+ args.attributes.size = (unsigned int) 0;
+ else
+ args.attributes.size = (unsigned int) Cur_file_ptr->attributes2.size
+ - truncate_size;
+ }
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR,
+ xdr_setattr, (char *) &args,
+ xdr_setattr, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->attributes2 = reply.attrstat_u.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_setattr */
+
+
+static int
+op_lookup(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ diropargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LOOKUP];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.name = Cur_filename;
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP,
+ xdr_lookup, (char *) &args,
+ xdr_lookup, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file,
+ NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ /* We do lookup Nonexistent and this is not an error */
+ if (reply.status != NFSERR_NOENT ||
+ Cur_file_ptr->state != Nonexistent) {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: lookup call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_lookup */
+
+
+static int
+op_readlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ fhandle_t fh; /* fh to do op on */
+ readlinkres reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN];
+ int len; /* length of symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ /*
+ * Note: this fh may be bogus because SYMLINK does
+ * not return a fh ... only a status. So unless we have
+ * done a LOOKUP on this guy, the fh will probably be bad.
+ * If it is bad it shows up as a symlink error in the results.
+ */
+ (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+
+ /* Have lower layers fill in the data directly. */
+ reply.readlinkres_u.data = sym_data;
+ len = 0;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READLINK,
+ xdr_readlink, (char *) &fh,
+ xdr_readlink, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ if (DEBUG_CHILD_RPC) {
+ len = reply.readlinkres_u.len;
+ sym_data[len] = '\0';
+ (void) fprintf(stderr, "%s: READLINK on %s returned %s\n",
+ sfs_Myname, Cur_filename, sym_data);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readlink */
+
+
+/*
+ * perform an RPC read operation of length 'xfer_size'
+ */
+static int
+op_read(
+ int xfer_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int cur_cnt;
+ int max_cnt; /* packet ctrs */
+ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
+ readargs args;
+ readres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int size;
+ int j;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READ];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+
+ /*
+ * Don't allow a read of less than one block size
+ */
+ if (xfer_size < Bytes_per_block)
+ xfer_size = Bytes_per_block;
+
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (Cur_file_ptr->attributes2.size > xfer_size) {
+ args.offset = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes2.size - xfer_size)
+ / Bytes_per_block) + 1));
+ } else
+ args.offset = 0;
+
+ /* first read the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block -1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ args.count = size;
+ args.totalcount = size; /* unused */
+
+ /* Have lower layers fill in the data directly. */
+ reply.readres_u.reply.data.data_val = buf;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "read: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READ,
+ xdr_read, (char *) &args,
+ xdr_read, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS_OK)
+ ? reply.readres_u.reply.data.data_len : 0;
+ Dump_offset = args.offset;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ Cur_file_ptr->attributes2 =
+ reply.readres_u.reply.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ size = reply.readres_u.reply.data.data_len;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READ %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ args.offset += size;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for reading max_cnt packets */
+ } /* for buffers and fragments */
+ return(ret);
+
+} /* op_read */
+
+
+char *
+init_write_buffer(
+ void)
+{
+ uint32_t *bp;
+ static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)];
+ uint32_t *be = write_buf + (sizeof(write_buf) /
+ sizeof(uint32_t));
+
+ if (write_buf[0] != (uint32_t)0xdeadbeef) {
+ for (bp = write_buf; bp < be; bp++)
+ *bp = (uint32_t)0xdeadbeef;
+ }
+ return (char *)write_buf;
+}
+
+/*
+ * Perform and RPC write operation of length 'xfer_size'. If 'append_flag'
+ * is true, then write the data to the end of the file.
+ */
+/* ARGSUSED2 */
+static int
+op_write(
+ int xfer_size,
+ int append_flag,
+ int stable)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ static char *buf = NULL; /* the data buffer */
+ int size; /* size of data write */
+ int cur_cnt; /* controls # NFS calls */
+ int max_cnt;
+ writeargs args;
+ attrstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int j;
+ int ret; /* ret val == call success */
+
+ /*
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+ op_ptr = &Ops[WRITE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ args.beginoffset = 0; /* unused */
+
+ if (append_flag == 1) {
+ args.offset = Cur_file_ptr->attributes2.size;
+ } else {
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (Cur_file_ptr->attributes2.size > xfer_size) {
+ args.offset = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes2.size - xfer_size)
+ / Bytes_per_block) + 1));
+ } else
+ args.offset = 0;
+ }
+
+ /* first write the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block - 1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ args.totalcount = size; /* unused */
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "write: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_WRITE,
+ xdr_write, (char *) &args,
+ xdr_write, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = args.data.data_len;
+ Dump_offset = args.offset;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ Cur_file_ptr->attributes2 = reply.attrstat_u.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ args.offset += size;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: WRITE %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for writing max_cnt packets */
+ } /* for buffers and fragments */
+ return(ret);
+
+} /* op_write */
+
+
+static int
+op_create(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ createargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[CREATE];
+ ret = 0;
+
+ /* set up the arguments */
+ args.attributes.mode = (0100000 | 0666); /* 666 NFREG file */
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.size = 0;
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.where.name = Cur_filename;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_CREATE,
+ xdr_create, (char *) &args,
+ xdr_create, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file,
+ NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: create call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_create */
+
+
+static int
+op_remove(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ diropargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[REMOVE];
+ ret = 0;
+
+ /* set up the arguments */
+ args.name = Cur_filename;
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_REMOVE,
+ xdr_remove, (char *) &args,
+ xdr_remove, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply == NFS_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: remove call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_remove */
+
+
+static int
+op_rename(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target name */
+ renameargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RENAME];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+
+ target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent,
+ Sfs_non_io_file);
+
+ args.from.name = Cur_file_ptr->file_name;
+ (void) sprintf(target_fileinfo_ptr->file_name, Filespec,
+ target_fileinfo_ptr->unique_num);
+ args.to.name = target_fileinfo_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_RENAME,
+ xdr_rename, (char *) &args,
+ xdr_rename, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply == NFS_OK) {
+ target_fileinfo_ptr->state = Exists;
+ (void) memmove((char *) &target_fileinfo_ptr->fh2,
+ (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ target_fileinfo_ptr->attributes2 = Cur_file_ptr->attributes2;
+ target_fileinfo_ptr->size = Cur_file_ptr->size;
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rename call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rename */
+
+
+static int
+op_link(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target */
+ linkargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.from, (char *) &target_fileinfo_ptr->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.to.name = Cur_filename;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_LINK,
+ xdr_link, (char *) &args,
+ xdr_link, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &target_fileinfo_ptr->fh2, NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ target_fileinfo_ptr->attributes2.nlink++;
+ Cur_file_ptr->attributes2 = target_fileinfo_ptr->attributes2;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: link call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_link */
+
+
+static int
+op_symlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target file */
+ symlinkargs args;
+ nfsstat reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SYMLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.from.name = Cur_filename;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target_fileinfo_ptr->file_name);
+ args.attributes.size = strlen(sym_data);
+ args.to = sym_data;
+
+ args.attributes.mode = (0120000 | 0777);
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK,
+ xdr_symlink, (char *) &args,
+ xdr_symlink, (char *) &reply,
+ ((int)Current_test_phase < (int)Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply == NFS_OK) {
+ /*
+ * SYMLINK doesn't return a fh. If we try to
+ * access this symlink (eg, remove(), readlink())
+ * before we do a lookup, we won't have a fh to use.
+ * So, we do a lookup call here.
+ * If it fails, we fill in what we can.
+ */
+ Cur_file_ptr->state = Exists;
+ if (op_lookup() == 0) {
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2.type = NFLNK;
+ Cur_file_ptr->attributes2.mode = (0120000|0777);
+ Cur_file_ptr->attributes2.uid = Cur_uid;
+ Cur_file_ptr->attributes2.gid = Cur_gid;
+ Cur_file_ptr->attributes2.atime.seconds =(unsigned int)Cur_time.esec;
+ Cur_file_ptr->attributes2.atime.useconds=(unsigned int)Cur_time.usec;
+ Cur_file_ptr->attributes2.mtime = Cur_file_ptr->attributes2.atime;
+ } else
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: symlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: symlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_symlink */
+
+
+static int
+op_mkdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ mkdirargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[MKDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ args.attributes.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.size = (unsigned int) 512;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.where.name = Cur_filename;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR,
+ xdr_mkdir, (char *) &args,
+ xdr_mkdir, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Empty_dir;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file,
+ NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: mkdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_mkdir */
+
+
+static int
+op_rmdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ diropargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RMDIR];
+ ret = 0;
+
+ if (Cur_file_ptr->state != Empty_dir) {
+ (void) fprintf(stderr, "%s: Attempting to remove non-Empty_dir %d\n",
+ sfs_Myname, Cur_file_ptr->unique_num);
+ }
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.name = Cur_file_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR,
+ xdr_rmdir, (char *) &args,
+ xdr_rmdir, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply == NFS_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rmdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rmdir */
+
+
+static int
+op_readdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ readdirargs args;
+ readdirres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ uint_t i;
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+ nfscookie cookie;
+ bool_t hit_eof;
+ /* arbitrary fixed ceiling */
+ int entry_cnt = SFS_MAXDIRENTS;
+ /* array of entries */
+ entry entry_stream[SFS_MAXDIRENTS];
+ entry *entry_ptr; /* ptr to the dir entry */
+
+ char name[SFS_MAXNAMLEN];
+ /* array of dir names */
+ char name_stream[SFS_MAXDIRENTS * SFS_MAXNAMLEN];
+
+
+ /*
+ * 1) need some measure of how many entries are in a directory
+ * currently, we assume SFS_MAXDIRENTS - it should be random
+ * from 0 to MAX for a large MAX we should pre-allocate a buffer for the
+ * returned directory names.
+ * 2) need some measure of how many directory entries to read
+ * during each readdir() call. Again, we assume SFS_MAXDIRENTS.
+ */
+
+ op_ptr = &Ops[READDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ (void) memset((char *) args.cookie, '\0', NFS_COOKIESIZE);
+ args.count = DEFAULT_MAX_BUFSIZE;
+
+ /* Have lower layers fill in the data directly. */
+ reply.readdirres_u.reply.max_entries = entry_cnt;
+ reply.readdirres_u.reply.entries = entry_stream;
+ for (i = 0; i < entry_cnt; i++) {
+ entry_stream[i].name = &name_stream[i * SFS_MAXNAMLEN];
+ }
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READDIR,
+ xdr_readdir, (char *) &args,
+ xdr_readdir, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS_OK) {
+
+ if (DEBUG_CHILD_RPC) {
+ hit_eof = reply.readdirres_u.reply.eof;
+ entry_cnt = reply.readdirres_u.reply.max_entries;
+ entry_ptr = reply.readdirres_u.reply.entries;
+ for (i = 0; i < entry_cnt; i++) {
+ entry_ptr->name[entry_ptr->name_len] ='\0';
+ (void) strcpy(name, entry_ptr->name);
+ (void) memmove((char *) cookie,
+ (char *) entry_ptr->cookie,
+ NFS_COOKIESIZE);
+ (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n",
+ sfs_Myname, hit_eof, name);
+ entry_ptr++;
+ }
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs2_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: readdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readdir */
+
+
+/* Beware - op_statfs() collides w/ some other name, use op_fsstat() */
+static int
+op_fsstat(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ fhandle_t fh;
+ statfsres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[FSSTAT];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &fh, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC_STATFS,
+ xdr_statfs, (char *) &fh,
+ xdr_statfs, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: fsstat call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_fsstat */
+
+
+/*
+ * These are a set of reliable functions used by the initialization code.
+ */
+
+#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES))
+
+/*
+ * Reliably lookup a file in the current directory
+ * Return:
+ * -1 RPC error
+ * 1 File doesn't exist
+ * 0 File exists
+ */
+int
+lad_lookup(sfs_fh_type *file_ptr, char *name)
+{
+ diropargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.name = name;
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP,
+ xdr_lookup, (char *) &args,
+ xdr_lookup, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status == NFSERR_NOENT) {
+ return(1);
+ }
+
+ if (reply.status != NFS_OK) {
+ (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n",
+ name, nfs2_strerror(reply.status));
+ return(-1);
+ }
+
+ file_ptr->state = Exists;
+ (void) memmove((char *) &file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+ (void) strcpy(file_ptr->file_name, name);
+ file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ file_ptr->size = fh_size(file_ptr);
+
+ return(0);
+}
+
+/*
+ * Reliably remove a file in the current directory
+ */
+int
+lad_remove(sfs_fh_type *file_ptr, char *name)
+{
+ diropargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does exist
+ */
+ if (file_ptr->attributes2.type == NFDIR)
+ return (lad_rmdir(file_ptr, name));
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.name = name;
+ (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_REMOVE,
+ xdr_remove, (char *) &args,
+ xdr_remove, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply != NFS_OK) {
+ if (reply != NFSERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n",
+ name, nfs2_strerror(reply));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably remove a directory in the current directory
+ */
+int
+lad_rmdir(sfs_fh_type *file_ptr, char *name)
+{
+ diropargs args;
+ nfsstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: lad_rmdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does exist and the directory
+ * is empty.
+ */
+ if (file_ptr->attributes2.type != NFDIR)
+ return (lad_remove(file_ptr, name));
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.name = name;
+ (void) memmove((char *) &args.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR,
+ xdr_remove, (char *) &args,
+ xdr_remove, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply != NFS_OK) {
+ if (reply != NFSERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n",
+ name, nfs2_strerror(reply));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably create a symlink in the current directory
+ */
+int
+lad_symlink(sfs_fh_type *file_ptr, char *target, char *name)
+{
+ symlinkargs args;
+ nfsstat reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_symlink: %lx %s -> %s\n", sfs_Myname,
+ (int32_t) file_ptr, name, target);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.from.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.from.name = name;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target);
+ args.attributes.size = strlen(sym_data);
+ args.to = sym_data;
+
+ args.attributes.mode = (0120000 | 0777);
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK,
+ xdr_symlink, (char *) &args,
+ xdr_symlink, (char *) &reply,
+ Nfs_timers[Init]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply != NFS_OK) {
+ if (reply != NFSERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n",
+ target, name, nfs2_strerror(reply));
+ return(-1);
+ }
+ }
+
+ /*
+ * SYMLINK doesn't return a fh. If we try to
+ * access this symlink (eg, remove(), readlink())
+ * before we do a lookup, we won't have a fh to use.
+ * So, we do a lookup call here.
+ * If it fails, we fill in what we can.
+ */
+ return (lad_lookup(file_ptr, name));
+}
+
+/*
+ * Reliably create a directory in the current directory
+ */
+int
+lad_mkdir(sfs_fh_type *file_ptr, char *name)
+{
+ mkdirargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.attributes.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.size = (unsigned int) 512;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.where.name = name;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR,
+ xdr_mkdir, (char *) &args,
+ xdr_mkdir, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFSERR_EXIST)
+ return(1);
+
+ if (reply.status != NFS_OK) {
+ if (reply.status != NFSERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n",
+ name, nfs2_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first mkdir suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file,
+ NFS_FHSIZE);
+ (void) strcpy(file_ptr->file_name, name);
+ file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ file_ptr->size = fh_size(file_ptr);
+ }
+ file_ptr->state = Empty_dir;
+
+ return(0);
+}
+
+/*
+ * Reliably write a file in the current directory
+ */
+int
+lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length)
+{
+ static char *buf = NULL; /* the data buffer */
+ int32_t size; /* size of data write */
+ int32_t cur_cnt;
+ writeargs args;
+ attrstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n",
+ sfs_Myname, (int32_t) file_ptr, (int32_t) file_ptr->dir,
+ offset, length);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does exist
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh2,
+ NFS_FHSIZE);
+ args.beginoffset = 0; /* unused */
+ args.offset = offset;
+
+ size = Bytes_per_block;
+ for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) {
+ if ((cur_cnt + size) > length)
+ size = length - cur_cnt;
+
+ if (size == 0)
+ break;
+
+ args.totalcount = size; /* unused */
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* make the call now */
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_WRITE,
+ xdr_write, (char *) &args,
+ xdr_write, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_write() RPC call failed : %s\n",
+ clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+ if (reply.status != NFS_OK) {
+ (void) fprintf(stderr, "lad_write() NFS call failed : %s\n",
+ nfs2_strerror(reply.status));
+ return(-1);
+ }
+ file_ptr->state = Exists;
+ file_ptr->attributes2 = reply.attrstat_u.attributes;
+ file_ptr->size = fh_size(file_ptr);
+ args.offset += size;
+ }
+ return(0);
+}
+
+/*
+ * Reliably create a file in the current directory
+ */
+int
+lad_create(sfs_fh_type *file_ptr, char *name)
+{
+ createargs args;
+ diropres reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.attributes.mode = (0100000 | 0666); /* 666 NFREG file */
+ args.attributes.uid = Cur_uid;
+ args.attributes.gid = Cur_gid;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.size = 0;
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh2,
+ NFS_FHSIZE);
+ args.where.name = name;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_CREATE,
+ xdr_create, (char *) &args,
+ xdr_create, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFSERR_EXIST) {
+ return(1);
+ }
+
+ if (reply.status != NFS_OK) {
+ if (reply.status != NFSERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n",
+ name, nfs2_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first create suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+ (void) strcpy(file_ptr->file_name, name);
+ file_ptr->attributes2 = reply.diropres_u.diropres.attributes;
+ file_ptr->size = fh_size(file_ptr);
+ }
+
+ file_ptr->state = Exists;
+ /*
+ * Directories are created as Empty_dir, when a file is created it
+ * becomes an Exists.
+ */
+ file_ptr->dir->state = Exists;
+
+ return(0);
+}
+
+/*
+ * Reliably set the size of a file in the current directory
+ */
+int
+lad_truncate(sfs_fh_type *file_ptr, int32_t size)
+{
+ sattrargs args;
+ attrstat reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, size);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name already exists
+ */
+ /* CONSTCOND */
+ while (1) {
+ /*
+ * set up the arguments
+ * Set the mode and times as well
+ */
+ args.attributes.mode = 0666;
+ args.attributes.uid = (unsigned int) -1;
+ args.attributes.gid = (unsigned int) -1;
+ args.attributes.size = (unsigned int) -1;
+ args.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ args.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ args.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh2,
+ NFS_FHSIZE);
+ args.attributes.size = (unsigned int) size;
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR,
+ xdr_setattr, (char *) &args,
+ xdr_setattr, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr,
+ "lad_truncate(%ld) RPC call failed : %s\n",
+ size, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status != NFS_OK) {
+ (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n",
+ size, nfs2_strerror(reply.status));
+ return(-1);
+ }
+ file_ptr->attributes2 = reply.attrstat_u.attributes;
+ file_ptr->size = fh_size(file_ptr);
+
+ return(0);
+}
+
+static char *
+nfs2_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFSERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFSERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFSERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFSERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFSERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFSERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFSERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFSERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFSERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFSERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFSERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFSERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFSERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFSERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFSERR_OPNOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFSERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFSERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFSERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFSERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFSERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFSERR_WFLUSH:
+ (void) strcpy(str, "write cache flushed");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+/* sfs_c_ops.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_vldSid[] = "@(#)sfs_2_vld.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ------------------------------ sfs_c_vld.c ---------------------------
+ *
+ * Validation suite for sfs.
+ *
+ *.Exported_routines
+ * void Validate_ops(int, char **)
+ *
+ *.Local_routines
+ * void validate_init_rpc()
+ * void validate_creation(void)
+ * void validate_attributes(void)
+ * void validate_read_write(void)
+ * void validate_rename(void)
+ * int compare_sattr(char *, char *, sattr *, fattr *)
+ * int compare_fattr(char *, char *, fattr *, fattr *)
+ * uint16_t sum(unsigned char *, uint_t)
+ * void validate_remove(void)
+ * void validate_cleanup(void)
+ * void validate_exit(void)
+ * void verror(int, ValMsgType, char *, ...)
+ *
+ *.Revision History
+ * 04-Dec-91 Keith Define string.h for SYSV/SVR4.
+ * 25-Jun-91 Wiryaman Created
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+extern struct hostent *Server_hostent;
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+
+/* forward definitions for local routines */
+/*
+ * validate options
+ * BATCH - do complete pass of validation, reporting errors if any
+ * VERBOSE - prints step-by-step validation actions being performed
+ * INTERACTIVE - VERBOSE and if any errors encountered, ask to continue
+ * validation or not.
+ */
+#define VAL_BATCH 1
+#define VAL_VERBOSE 2
+#define VAL_INTERACTIVE 3
+
+typedef enum {
+ I = 1,
+ W = 2,
+ E = 3
+} ValMsgType;
+
+#define NUMREGFILES 7
+#define NUMDIRS 5
+#define NUMLINKS 5
+#define NUMSYMLINKS 5
+#define NUMFILES NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS
+#define NUMFRAGS 8
+
+static void validate_init_rpc(void);
+static void validate_exit(void);
+static void validate_creation(void);
+static void validate_attributes(void);
+static void validate_read_write(void);
+static void validate_rename(void);
+static void validate_remove(void);
+static void validate_cleanup(void);
+static int compare_sattr(char *, char *, sattr *, fattr *);
+static int compare_fattr(char *, char *, fattr *, fattr *);
+static uint16_t sum(unsigned char *, uint_t);
+static void verror(int, ValMsgType, char *, ...);
+
+static void val_op_null(void);
+static void val_op_getattr(fhandle_t *, attrstat *);
+static void val_op_setattr(sattrargs *, attrstat *);
+static void val_op_lookup(diropargs *, diropres *);
+static void val_op_readlink(fhandle_t *, readlinkres *);
+static void val_op_read(readargs *, readres *);
+static void val_op_write(writeargs *, attrstat *);
+static void val_op_create(createargs *, diropres *);
+static void val_op_remove(diropargs *, nfsstat *);
+static void val_op_rename(renameargs *, nfsstat *);
+static void val_op_link(linkargs *, nfsstat *);
+static void val_op_symlink(symlinkargs *, nfsstat *);
+static void val_op_mkdir(mkdirargs *, diropres *);
+static void val_op_rmdir(diropargs *, nfsstat *);
+static void val_op_readdir(readdirargs *, readdirres *);
+static void val_op_statfs(fhandle_t *, statfsres *);
+static void create_tmp_handles(void);
+static void delete_tmp_handles(void);
+
+/*
+ * ---------------------- Static Declarations ----------------------
+ */
+
+int Validate;
+
+static int Validate_errors = 0;
+static char Testdirname[SFS_MAXPATHLEN]; /* test dir component name */
+
+/*
+ * ---------------------- SFS Validation Suite ----------------------
+ */
+
+void
+Validate_ops(
+ int argc,
+ char * argv[])
+{
+ char * valdir;
+ CLIENT * mount_client_ptr;
+
+ if (argc > 1) {
+ verror(VAL_BATCH, E, "Can only validate one directory at a time.\n");
+ exit(1);
+ }
+
+ Num_io_files = NUMFILES;
+ Cur_uid = Real_uid;
+ nfs_version = NFS_VERSION;
+
+ if (argc == 0)
+ valdir = ".";
+ else
+ valdir = argv++[0];
+
+ (void) sprintf(Testdirname, "%s/validatedir", valdir);
+
+ do {
+ verror(VAL_BATCH, I, "validating sfs on \"%s\" directory ...\n",
+ valdir);
+
+ init_fileinfo();
+ create_tmp_handles();
+
+ /*
+ * need priv port to do following
+ */
+ mount_client_ptr = lad_getmnt_hand(valdir);
+ if (mount_client_ptr == NULL) {
+ exit(1);
+ }
+ validate_init_rpc();
+
+ /*
+ * should be all done doing priv port stuff
+ */
+ if (setuid(Real_uid) != 0) {
+ (void) fprintf(stderr,"%s: %s%s\n",
+ sfs_Myname, "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, valdir, mount_client_ptr);
+ verror(VAL_VERBOSE, I, "validating null operation ...\n");
+ val_op_null();
+
+ validate_creation();
+ validate_attributes();
+ validate_read_write();
+ validate_rename();
+ validate_remove();
+ argc--;
+ valdir = argv++[0];
+
+ /*
+ * Cleanup mount client handle
+ */
+ clnt_destroy(mount_client_ptr);
+
+ delete_tmp_handles();
+ validate_cleanup();
+
+ } while (argc > 0);
+
+ validate_exit();
+
+} /* Validate_ops */
+
+
+/*
+ * allocate and initialize client handles
+ */
+static void
+validate_init_rpc(void)
+{
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) NFS_VERSION,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ verror(VAL_BATCH, E, "portmap/nfsd server not responding\n");
+ exit(1);
+ }
+
+ NFS_client->cl_auth = authunix_create(lad_hostname, Real_uid,
+ Cur_gid, 0, NULL);
+} /* validate_init_rpc */
+
+
+static void
+validate_creation(void)
+{
+ int filenum;
+ int target_filenum;
+ diropargs arglp;
+ createargs argcr;
+ mkdirargs argmk;
+ linkargs argln;
+ symlinkargs argsl;
+ char sl_target_path[NFS_MAXPATHLEN];
+ diropres reply;
+ readlinkres rlreply;
+ char sym_data[NFS_MAXPATHLEN];
+
+ for (filenum=0; filenum < NUMFILES ; filenum++) {
+
+ Cur_file_ptr = &Io_files[filenum];
+ sfs_gettime(&Cur_time);
+
+ if (filenum < NUMREGFILES) {
+
+ (void) sprintf(Cur_filename, Filespec, filenum);
+
+ /* regular file creation */
+ argcr.attributes.mode= (NFSMODE_REG | 0666);
+ argcr.attributes.uid = Cur_uid;
+ argcr.attributes.gid = Cur_gid;
+ argcr.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ argcr.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ argcr.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ argcr.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ argcr.attributes.size = 0;
+ (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ argcr.where.name = Cur_filename;
+
+ verror(VAL_VERBOSE, I, "validating create file %s ...\n",
+ Cur_filename);
+ val_op_create(&argcr, &reply);
+
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes2,
+ (char *) &reply.diropres_u.diropres.attributes,
+ sizeof(Cur_file_ptr->attributes2));
+ (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name,
+ &argcr.attributes, &Cur_file_ptr->attributes2);
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ errno = (int)reply.status;
+ verror(VAL_BATCH, E, "create %s failed: %m\n", Cur_filename);
+ /*
+ * An error in file creation is fatal, because we use the
+ * created files to validate the other operations.
+ */
+ validate_exit();
+ }
+
+ } else if (filenum < NUMREGFILES + NUMDIRS) {
+
+ (void) sprintf(Cur_filename, Dirspec, filenum);
+
+ /* directory creation */
+ argmk.attributes.mode= (NFSMODE_DIR | 0777);
+ argmk.attributes.uid = Cur_uid;
+ argmk.attributes.gid = Cur_gid;
+ argmk.attributes.size = 0xFFFFFFFF;
+ argmk.attributes.atime.seconds = (unsigned int) Cur_time.esec;
+ argmk.attributes.atime.useconds = (unsigned int) Cur_time.usec;
+ argmk.attributes.mtime.seconds = (unsigned int) Cur_time.esec;
+ argmk.attributes.mtime.useconds = (unsigned int) Cur_time.usec;
+ (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ argmk.where.name = Cur_filename;
+
+ verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename);
+ val_op_mkdir(&argmk, &reply);
+
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file,
+ NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes2,
+ (char *) &reply.diropres_u.diropres.attributes,
+ sizeof(Cur_file_ptr->attributes2));
+ (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name,
+ &argmk.attributes, &Cur_file_ptr->attributes2);
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "mkdir %s failed:%m\n", Cur_filename);
+ }
+
+ } else if(filenum < NUMREGFILES + NUMDIRS + NUMLINKS ) {
+
+ (void) sprintf(Cur_filename, Filespec, filenum);
+
+ /* hard link creation */
+ target_filenum = NUMFILES-NUMSYMLINKS-1-filenum;
+ (void) memmove((char *) &argln.from,
+ (char *) &Io_files[target_filenum].fh2, NFS_FHSIZE);
+ (void) memmove((char *) &argln.to.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ argln.to.name = Cur_filename;
+
+ verror(VAL_VERBOSE, I, "validating link %s %s ...\n",
+ Io_files[target_filenum].file_name, Cur_filename);
+ val_op_link(&argln, &reply.status);
+
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &Io_files[target_filenum].fh2,
+ NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2 = Io_files[target_filenum].attributes2;
+ Io_files[target_filenum].attributes2.nlink++;
+ Cur_file_ptr->attributes2.nlink++;
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename);
+ }
+
+ } else {
+
+ (void) sprintf(Cur_filename, Symspec, filenum);
+
+ /* symbolic link creation */
+ target_filenum = NUMFILES-1-filenum;
+ (void) memmove((char *) &argsl.from.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ argsl.from.name = Cur_filename;
+ (void) sprintf(sl_target_path,
+ "./%s", Io_files[target_filenum].file_name);
+ argsl.attributes.size = strlen(sl_target_path);
+ argsl.to = sl_target_path;
+ argsl.attributes.mode = (NFSMODE_LNK | 0777);
+ argsl.attributes.uid = Cur_uid;
+ argsl.attributes.gid = Cur_gid;
+ argsl.attributes.atime.seconds = (unsigned int)Cur_time.esec;
+ argsl.attributes.atime.useconds = (unsigned int)Cur_time.usec;
+ argsl.attributes.mtime.seconds = (unsigned int)Cur_time.esec;
+ argsl.attributes.mtime.useconds = (unsigned int)Cur_time.usec;
+
+ verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n",
+ sl_target_path, Cur_filename);
+ val_op_symlink(&argsl, &reply.status);
+
+ if (reply.status == NFS_OK) {
+ Cur_file_ptr->state = Exists;
+
+ /* do a lookup to get file handle and attributes */
+ (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ arglp.name = Cur_filename;
+
+ val_op_lookup(&arglp, &reply);
+
+ if (reply.status == NFS_OK) {
+ (void) memmove((char *) &Cur_file_ptr->fh2,
+ (char *) &reply.diropres_u.diropres.file, NFS_FHSIZE);
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes2 =
+ reply.diropres_u.diropres.attributes;
+ (void) compare_sattr(Ops[SYMLINK].name,
+ Io_files[filenum].file_name,
+ &argsl.attributes,
+ &Cur_file_ptr->attributes2);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+ Cur_filename);
+ continue;
+ }
+
+ /* validate readlink */
+ rlreply.readlinkres_u.data = sym_data;
+
+ verror(VAL_VERBOSE, I, "validating readlink %s ...\n",
+ Cur_filename);
+ val_op_readlink(&Cur_file_ptr->fh2, &rlreply);
+
+ if (rlreply.status == NFS_OK) {
+ sym_data[rlreply.readlinkres_u.len] = '\0';
+ if (strcmp(sl_target_path, sym_data)) {
+ verror(VAL_BATCH, W,
+ "readlink %s error, result = %s, should be %s\n",
+ Cur_filename, rlreply.readlinkres_u.data,
+ sl_target_path);
+ }
+ } else {
+ verror(VAL_BATCH, W, "readlink %s failed:%m\n",
+ Cur_filename);
+ }
+
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "symlink %s failed: %m\n",
+ Cur_filename);
+ }
+ }
+ } /* end for each file */
+
+} /* validate_creation */
+
+
+static void
+validate_attributes(void)
+{
+ int filenum;
+ diropargs arglp;
+ diropres lreply;
+ fhandle_t fh;
+ sattrargs argsa;
+ attrstat areply;
+
+ /* validate fsstat */
+
+ /* validate lookup */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ (void) memmove((char *) &arglp.dir, (char *) &Export_dir.fh2,
+ NFS_FHSIZE);
+ arglp.name = Io_files[filenum].file_name;
+
+ verror(VAL_VERBOSE, I, "validating lookup %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_lookup(&arglp, &lreply);
+
+ if (lreply.status == NFS_OK) {
+ if (memcmp((char *) &(Io_files[filenum].fh2),
+ (char *) &(lreply.diropres_u.diropres.file),
+ NFS_FHSIZE)) {
+ verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n",
+ Io_files[filenum].file_name);
+ }
+ (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes2,
+ &lreply.diropres_u.diropres.attributes);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed:%m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+
+ /* validate getattr */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ (void) memmove((char *) &fh, (char *) &Io_files[filenum].fh2,
+ NFS_FHSIZE);
+
+ verror(VAL_VERBOSE, I, "validating getattr %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_getattr(&fh, &areply);
+
+ if (areply.status == NFS_OK) {
+ (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes2,
+ &areply.attrstat_u.attributes);
+ } else {
+ verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+
+ /*validate setattr */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ sfs_gettime(&Cur_time);
+ if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS)
+ argsa.attributes.mode= 0777;
+ else
+ argsa.attributes.mode= 0666;
+ argsa.attributes.uid = 0xFFFFFFFF;
+ argsa.attributes.gid = 0xFFFFFFFF;
+ argsa.attributes.size = 0xFFFFFFFF;
+ argsa.attributes.atime.seconds = (unsigned int)Cur_time.esec;
+ argsa.attributes.atime.useconds = (unsigned int)Cur_time.usec;
+ argsa.attributes.mtime.seconds = (unsigned int)Cur_time.esec;
+ argsa.attributes.mtime.useconds = (unsigned int)Cur_time.usec;
+ (void) memmove((char *) &argsa.file, (char *) &Io_files[filenum].fh2,
+ NFS_FHSIZE);
+
+ verror(VAL_VERBOSE, I, "validating setattr %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_setattr(&argsa, &areply);
+
+ if (areply.status == NFS_OK) {
+ if (argsa.attributes.mode != areply.attrstat_u.attributes.mode){
+ argsa.attributes.mode |=
+ (Io_files[filenum].attributes2.mode & NFSMODE_FMT);
+ argsa.attributes.mode &= Io_files[filenum].attributes2.mode;
+ }
+ Io_files[filenum].attributes2 = areply.attrstat_u.attributes;
+ (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name,
+ &argsa.attributes, &areply.attrstat_u.attributes);
+
+ val_op_getattr(&argsa.file, &areply);
+
+ if (areply.status == NFS_OK) {
+ (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes2,
+ &areply.attrstat_u.attributes);
+ } else {
+ verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ } else {
+ verror(VAL_BATCH, W, "setattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+
+} /* validate_attributes */
+
+
+static void
+validate_read_write(void)
+{
+ struct {
+ uint16_t sum; /* checksum of data */
+ uint16_t len; /* length of len and data */
+ char data[DEFAULT_MAX_BUFSIZE - 2 * sizeof(uint16_t)];
+ } block;
+ writeargs argwr;
+ attrstat wrreply;
+ readargs argrd;
+ readres rdreply;
+ int maxblks;
+ int maxfiles;
+ uint_t i;
+ int numfiles;
+ int filenum;
+ int blocknum;
+ readdirargs argrdir;
+ readdirres rdirreply;
+ int entry_cnt = 9;
+ entry entry_stream[9];
+ char name_stream[9 * SFS_MAXNAMLEN];
+
+ /* validate write */
+
+ /* get the maximum number of blocks sfs will write */
+ maxblks = Io_dist_ptr->max_bufs;
+ maxfiles = maxblks > NUMREGFILES ? NUMREGFILES : maxblks;
+
+ /* write maxblks - filenum + 1 blocks to each regular file */
+ argwr.offset = 0;
+ argwr.beginoffset = 0; /* unused */
+ argwr.totalcount = 0; /* unused */
+ argwr.data.data_val = (char *)█
+
+ for (blocknum = 0; blocknum <= maxblks ; blocknum++) {
+
+ for (i=0; i < sizeof(block.data); i++)
+ block.data[i] = (char)blocknum;
+
+ for (filenum=0; filenum < maxfiles; filenum++) {
+
+ /* Write fewer blocks to files with higher numbers. */
+ if (blocknum > (maxblks - filenum))
+ break;
+
+ /* set the length field */
+ if (blocknum == (maxblks - filenum)) {
+ block.len = ((maxfiles - filenum) *
+ (Bytes_per_block/Kb_per_block)) - (sizeof(block.len)
+ + sizeof(block.sum));
+ } else {
+ block.len = Bytes_per_block - (sizeof(block.len)
+ + sizeof(block.sum));
+ }
+ block.sum = sum((unsigned char *) &block.len,
+ block.len + sizeof(block.len));
+
+ (void) memmove((char *) &argwr.file,
+ (char *) &Io_files[filenum].fh2, NFS_FHSIZE);
+ argwr.data.data_len = block.len +
+ sizeof(block.len) + sizeof(block.sum);
+
+ verror(VAL_VERBOSE, I,
+ "validating write %d bytes @ offset %d to %s ...\n",
+ argwr.data.data_len, argwr.offset,
+ Io_files[filenum].file_name);
+
+ val_op_write(&argwr, &wrreply);
+
+ if (wrreply.status == NFS_OK) {
+ (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes2,
+ &wrreply.attrstat_u.attributes);
+ Io_files[filenum].attributes2 = wrreply.attrstat_u.attributes;
+ } else {
+ verror(VAL_BATCH, W, "write %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+ argwr.offset += Bytes_per_block;
+ }
+
+ /* validate read */
+
+ for (filenum = 0; filenum < maxfiles; filenum++) {
+ (void) memmove((char *) &argrd.file, (char *) &Io_files[filenum].fh2,
+ NFS_FHSIZE);
+ argrd.offset = 0;
+ argrd.count = 0;
+ rdreply.readres_u.reply.data.data_len = 0;
+ maxblks = Io_files[filenum].attributes2.size / Bytes_per_block;
+ for (blocknum = 0; blocknum <= maxblks; blocknum ++) {
+
+ if (argrd.count != rdreply.readres_u.reply.data.data_len) {
+ argrd.count -= rdreply.readres_u.reply.data.data_len;
+ rdreply.readres_u.reply.data.data_val = (char *)&block +
+ rdreply.readres_u.reply.data.data_len;
+ blocknum--;
+ } else {
+ if (blocknum < maxblks)
+ argrd.count = Bytes_per_block;
+ else
+ argrd.count = (maxfiles - filenum)
+ * (Bytes_per_block/Kb_per_block);
+ rdreply.readres_u.reply.data.data_val = (char *)█
+ }
+ argrd.totalcount = argrd.count; /* unused */
+
+ verror(VAL_VERBOSE, I,
+ "validating read %d bytes @ offset %d from %s ...\n",
+ argrd.count, argrd.offset,
+ Io_files[filenum].file_name);
+
+ val_op_read(&argrd, &rdreply);
+
+ if (rdreply.status == NFS_OK) {
+ (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes2,
+ &rdreply.readres_u.reply.attributes);
+ Io_files[filenum].attributes2 =
+ rdreply.readres_u.reply.attributes;
+ argrd.offset += rdreply.readres_u.reply.data.data_len;
+ } else {
+ verror(VAL_BATCH, W, "read %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+
+ if (argrd.count ==
+ (block.sum != sum((unsigned char *) &block.len,
+ block.len + sizeof(block.len)))) {
+ verror(VAL_BATCH, W, "read %s checksum mismatch\n",
+ Io_files[filenum].file_name);
+ }
+ }
+ }
+
+ /* validate readdir */
+ numfiles = 0;
+
+ (void) memmove((char *) &argrdir.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+ (void) memset((char *) argrdir.cookie, '\0', NFS_COOKIESIZE);
+ argrdir.count = DEFAULT_MAX_BUFSIZE;
+
+ (void) memset((char *) &rdirreply, '\0', sizeof(rdirreply));
+ rdirreply.readdirres_u.reply.max_entries = entry_cnt;
+ rdirreply.readdirres_u.reply.entries = entry_stream;
+ for (i = 0; i < entry_cnt; i++) {
+ entry_stream[i].name = &name_stream[i * SFS_MAXNAMLEN];
+ }
+
+ do {
+ verror(VAL_VERBOSE, I, "validating readdir %d entries of %s ...\n",
+ rdirreply.readdirres_u.reply.max_entries, Testdirname);
+ val_op_readdir(&argrdir, &rdirreply);
+
+ if (rdirreply.status == NFS_OK) {
+ for (i = 0; i < rdirreply.readdirres_u.reply.max_entries; i++) {
+ numfiles++;
+ entry_stream[i].name[entry_stream[i].name_len] = '\0';
+ if (!entry_stream[i].valid) {
+ verror(VAL_BATCH, W,
+ "readdir %s error: entry %d (%s) is marked invalid\n",
+ Testdirname, i, entry_stream[i].name);
+ }
+ if ((!strcmp(entry_stream[i].name, ".")) ||
+ (!strcmp(entry_stream[i].name, "..")) ) {
+ numfiles--;
+ continue;
+ }
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) {
+ if (entry_stream[i].fileid !=
+ Io_files[filenum].attributes2.fileid) {
+ verror(VAL_BATCH, E,
+ "readdir %s error: file %s fileid mismatch\n",
+ Testdirname, entry_stream[i].name);
+
+ verror(VAL_BATCH, W,
+ " fileid: got = %d, original = %d\n",
+ entry_stream[i].fileid,
+ Io_files[filenum].attributes2.fileid);
+ }
+ break;
+ }
+ }
+ if (filenum == NUMFILES) {
+ verror(VAL_BATCH, W,
+ "readdir %s error: file \"%s\" was not created within sfs\n",
+ Testdirname, entry_stream[i].name);
+ }
+ }
+
+ if (i < entry_cnt && entry_stream[i].valid) {
+ verror(VAL_BATCH, W,
+ "readdir %s error: valid entries exceeded maximum\n",
+ Testdirname);
+ }
+
+ } else {
+ verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname);
+ }
+
+ (void) memmove((char *)argrdir.cookie,
+ (char *)entry_stream[rdirreply.readdirres_u.reply.max_entries-1].cookie,
+ NFS_COOKIESIZE);
+
+ } while (rdirreply.readdirres_u.reply.eof == 0);
+
+ if (numfiles != NUMFILES) {
+ verror(VAL_BATCH, W,
+ "readdir %s error: the number of files found\n\
+does not match with the number of files created within sfs\n", Testdirname);
+ }
+
+} /* validate_read_write */
+
+
+static void
+validate_rename(void)
+{
+ renameargs argrn;
+ nfsstat rnreply;
+ int filenum;
+ char newname[SFS_MAXNAMLEN];
+ int rncount = 0;
+
+ (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+ (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+
+ for (filenum=0; filenum < NUMFILES; filenum++) {
+ if (Io_files[filenum].state != Exists)
+ continue;
+
+ rncount++;
+ (void) sprintf(newname, "n%s", Io_files[filenum].file_name);
+ argrn.from.name = Io_files[filenum].file_name;
+ argrn.to.name = newname;
+
+ verror(VAL_VERBOSE, I, "validating rename %s %s ...\n",
+ argrn.from.name, argrn.to.name);
+
+ val_op_rename(&argrn, &rnreply);
+
+ if (rnreply == NFS_OK) {
+ (void) strcpy(Io_files[filenum].file_name, newname);
+ } else {
+ verror(VAL_BATCH, W, "rename %s to %s failed: %m\n",
+ Io_files[filenum].file_name, newname);
+ }
+
+ }
+
+ if (!rncount) {
+ verror(VAL_BATCH, E, "validate_rename: no files renamed\n");
+ verror(VAL_BATCH, W, " due to previous operation error\n");
+ }
+
+} /* validate_rename */
+
+
+static int
+compare_fattr(
+ char * op,
+ char * fname,
+ fattr * attr1,
+ fattr * attr2)
+{
+ int ret = TRUE;
+ int prev_warn = FALSE; /* -1 info warning */
+ int flag_error = FALSE;
+
+ if (attr1->type != attr2->type) {
+ if (attr1->type == 0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " type: current = %d, previous = %d\n",
+ attr2->type, attr1->type);
+ attr1->type = attr2->type;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " type: current = %d, previous = %d\n",
+ attr2->type, attr1->type);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if ((attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) {
+ if (attr1->mode == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " mode: current = %7o, previous = %7o\n",
+ attr2->mode, attr1->mode);
+ attr1->mode = attr2->mode;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " mode: current = %d, previous = %d\n",
+ attr2->mode, attr1->mode);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->nlink != attr2->nlink) {
+ if (attr1->nlink == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " nlink: current = %d, previous = %d\n",
+ attr2->nlink, attr1->nlink);
+ attr1->nlink = attr2->nlink;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " nlink: current = %d, previous = %d\n",
+ attr2->nlink, attr1->nlink);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+
+ /*
+ * Check for user "nobody", UID -2, which may be untranslated from
+ * sixteen-bit two's complement.
+ */
+ if (attr1->uid != attr2->uid && !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+ attr2->uid == 65534) && attr1->uid ==0)) {
+ if (attr1->uid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " uid: current = %d, previous = %d\n",
+ attr2->uid, attr1->uid);
+ attr1->uid = attr2->uid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " uid: current = %d, previous = %d\n",
+ attr2->uid, attr1->uid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->gid != attr2->gid && attr2->gid != 0) {
+/*
+ if (attr1->gid != attr2->gid) {
+*/
+ if (attr1->gid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " gid: current = %d, previous = %d\n",
+ attr2->gid, attr1->gid);
+ attr1->gid = attr2->gid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " gid: current = %d, previous = %d\n",
+ attr2->gid, attr1->gid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->size != attr2->size) {
+ if (strcmp(op, Ops[WRITE].name)) {
+ if (attr1->size == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " size: current = %d, previous = %d\n",
+ attr2->size, attr1->size);
+ attr1->size = attr2->size;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " size: current = %d, previous = %d\n",
+ attr2->size, attr1->size);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+ }
+
+ if (attr1->blocksize != attr2->blocksize) {
+ if (attr1->blocksize == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " blocksize: current = %d, previous = %d\n",
+ attr2->blocksize, attr1->blocksize);
+ attr1->blocksize = attr2->blocksize;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " blocksize: current = %d, previous = %d\n",
+ attr2->blocksize, attr1->blocksize);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+
+ /* compare rdev only if type == NFCHR or NFBLK */
+ if ((attr1->type == NFCHR || attr1->type == NFBLK) &&
+ attr1->rdev != attr2->rdev) {
+ if (attr1->rdev == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " rdev: current = %d, previous = %d\n",
+ attr2->rdev, attr1->rdev);
+ attr1->rdev = attr2->rdev;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " rdev: current = %d, previous = %d\n",
+ attr2->rdev, attr1->rdev);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+
+ /*
+ * The NFS specification does not require that the number of blocks
+ * associated with a file remain constant. Certain file systems
+ * may pre-allocate more blocks than necessary then trim them
+ * back ("garbage collect") or even blow holes in files that have
+ * all zero blocks.
+ * We must check that we never get back -1.
+ */
+ if (attr1->blocks != attr2->blocks) {
+ if (strcmp(op, Ops[WRITE].name)) {
+ if (attr1->blocks == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " blocks: current = %d, previous = %d\n",
+ attr2->blocks, attr1->blocks);
+ attr1->blocks = attr2->blocks;
+ ret = FALSE;
+ }
+ }
+ }
+
+
+ if (attr1->fsid != attr2->fsid) {
+ if (attr1->fsid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " fsid: current = %d, previous = %d\n",
+ attr2->fsid, attr1->fsid);
+ attr1->fsid = attr2->fsid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " fsid: current = %d, previous = %d\n",
+ attr2->fsid, attr1->fsid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->fileid != attr2->fileid) {
+ if (attr1->fileid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " fileid: current = %d, previous = %d\n",
+ attr2->fileid, attr1->fileid);
+ attr1->fileid = attr2->fileid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " fileid: current = %d, previous = %d\n",
+ attr2->fileid, attr1->fileid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (prev_warn) {
+ verror(VAL_BATCH, I,
+ "\n Warning: the previous value of a field is -1,\n");
+ verror(VAL_BATCH, I,
+ " this resulted from an unused field returned by\n");
+ verror(VAL_BATCH, I,
+ " the previous operation on this file/directory.\n");
+ verror(VAL_BATCH, I,
+ " The current value is now stored for future comparison\n\n");
+ }
+
+ if (flag_error)
+ verror(VAL_BATCH, W,"\n");
+
+ return(flag_error);
+
+} /* ckompare_fattr */
+
+
+static int
+compare_sattr(
+ char * op,
+ char * fname,
+ sattr * attr1,
+ fattr * attr2)
+{
+ int ret = TRUE;
+ char msg[80];
+
+ msg[0] = '\0';
+
+ if (attr1->mode != (unsigned int)0xFFFFFFFF &&
+ (attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+
+ }
+ verror(VAL_BATCH, E, " mode: returned = %7o, specified = %7o\n",
+ attr2->mode, attr1->mode);
+ ret = FALSE;
+ }
+
+ /*
+ * Check for user "nobody", UID -2, which may be untranslated from
+ * sixteen-bit two's complement.
+ */
+ if (attr1->uid != (unsigned int)0xFFFFFFFF && attr1->uid != attr2->uid &&
+ !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+ attr2->uid == 65534) &&
+ attr1->uid == 0)) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ if (attr1->uid == 0)
+ (void) strcat(msg," (is root UID mapped to other UID?)");
+ verror(VAL_BATCH, E, " uid: returned = %d, specified = %d %s\n",
+ attr2->uid, attr1->uid, msg);
+ ret = FALSE;
+ }
+ if (attr1->gid != (unsigned int)0xFFFFFFFF &&
+ attr1->gid != attr2->gid &&
+ attr2->gid != 0) {
+/*
+ if (attr1->gid != 0xFFFFFFFF && attr1->gid != attr2->gid) {
+*/
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " gid: returned = %d, specified = %d\n",
+ attr2->gid, attr1->gid);
+ ret = FALSE;
+ }
+
+ if (attr1->size != (unsigned int)0xFFFFFFFF &&
+ attr1->size != attr2->size) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " size: returned = %d, specified = %d\n",
+ attr2->size, attr1->size);
+ ret = FALSE;
+ }
+
+ if (!ret)
+ verror(VAL_BATCH, W,"\n");
+
+ return(ret);
+
+} /* compare_sattr */
+
+
+/*
+ * Return the BSD checksum of buf[0..len-1]
+ */
+static uint16_t
+sum(
+ unsigned char * buf,
+ uint_t len)
+{
+ uint16_t cksum;
+
+ cksum = 0;
+ for (; len--; buf++) {
+ if (cksum & 1)
+ cksum = (cksum >> 1) + 0x8000;
+ else
+ cksum >>= 1;
+ cksum += (uint16_t) *buf;
+ cksum &= 0xFFFF;
+ }
+ return(cksum);
+} /* sum */
+
+
+static void
+validate_remove(void)
+{
+ diropargs args;
+ nfsstat reply;
+ diropres lreply;
+ int filenum;
+ char * op;
+
+ (void) memmove((char *) &args.dir, (char *) &Export_dir.fh2, NFS_FHSIZE);
+
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if (Io_files[filenum].state != Exists)
+ continue;
+
+ args.name = Io_files[filenum].file_name;
+
+ if (Io_files[filenum].attributes2.type == NFDIR) {
+ op = Ops[RMDIR].name;
+ verror(VAL_VERBOSE, I, "validating rmdir %s ...\n",
+ args.name);
+ val_op_rmdir(&args, &reply);
+ } else {
+ op = Ops[REMOVE].name;
+ verror(VAL_VERBOSE, I, "validating remove %s ...\n",
+ args.name);
+ val_op_remove(&args, &reply);
+ }
+
+ if (reply == NFS_OK) {
+ /* make sure the file is removed from the directory */
+ val_op_lookup(&args, &lreply);
+
+ if (lreply.status == NFSERR_NOENT) {
+ Io_files[filenum].state = Nonexistent;
+ } else if (lreply.status == NFS_OK) {
+ verror(VAL_BATCH, W, "%s %s: file not removed\n",
+ op, Io_files[filenum].file_name);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+ Io_files[filenum].file_name);
+
+ }
+ } else {
+ verror(VAL_BATCH, W, "%s %s failed: %m\n", op,
+ Io_files[filenum].file_name);
+ }
+
+ }
+
+} /* validate_remove */
+
+
+static void
+validate_cleanup(void)
+{
+ free(Io_files);
+ free(Non_io_files);
+ free(Dirs);
+ free(Symlinks);
+ clnt_destroy(NFS_client);
+
+} /* validate_cleanup */
+
+
+static void
+validate_exit(void)
+{
+ if (!Validate_errors) {
+ verror(VAL_BATCH, I, "validation completed successfully.\n");
+ exit(0);
+ } else {
+ verror(VAL_BATCH, I, "validation terminated with errors\n");
+ exit(1);
+ }
+
+} /* validate_exit */
+
+
+/* PRINTFLIKE3 */
+static void
+verror(
+ int opt,
+ ValMsgType msgtype,
+ char * fmt,
+ ...)
+{
+ va_list ap;
+ char buf[1024];
+ char * bp = buf;
+ char * fp;
+ int repeat;
+ char * sp;
+
+ va_start(ap, fmt);
+
+ /*
+ * Expand the "%m" format character into the descriptive string
+ * for the current value of errno. Printf handles the other "%"
+ * formatting characters.
+ */
+ if (Validate >= opt) {
+ for (fp = fmt; *fp; fp++) {
+ if (*fp == '%' && fp[1] == 'm') {
+ if ((sp = strerror(errno)) == NULL) {
+ (void) sprintf(bp, "unknown error %d", errno);
+ } else {
+ (void) strcpy(bp, sp);
+ }
+ bp = buf + strlen(buf);
+ fp++;
+ } else {
+ *bp++ = *fp;
+ }
+ }
+ *bp = '\0';
+ (void) vprintf(buf, ap);
+ }
+ va_end(ap);
+
+ if (msgtype != I)
+ Validate_errors++;
+
+ if (msgtype == W && Validate == VAL_INTERACTIVE) {
+ repeat = 1;
+ while (repeat) {
+ char ans[80];
+
+ (void) fprintf(stderr, "continue? (y or n) ");
+ if (!fgets(ans,80,stdin)) {
+ (void) fprintf(stderr, "\n");
+ continue;
+ }
+ if (ans[0] == 'n' || ans[0] == 'N') {
+ validate_exit();
+ exit(1);
+ } else if (ans[0] == 'y' || ans[0] == 'Y') {
+ repeat = 0;
+ break;
+ }
+ }
+ }
+
+} /* verror */
+
+
+static void
+val_op_null(void)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_NULL,
+ xdr_void, (char *)0, xdr_void, (char *)0,
+ Nfs_timers[Ops[NULLCALL].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "null");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_getattr(fhandle_t *args, attrstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_GETATTR,
+ xdr_getattr, (char *)args, xdr_getattr, (char *)reply,
+ Nfs_timers[Ops[GETATTR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "getattr");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_setattr(sattrargs *args, attrstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SETATTR,
+ xdr_setattr, (char *)args, xdr_setattr, (char *)reply,
+ Nfs_timers[Ops[SETATTR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "setattr");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_lookup(diropargs *args, diropres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_LOOKUP,
+ xdr_lookup, (char *)args, xdr_lookup, (char *)reply,
+ Nfs_timers[Ops[LOOKUP].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "lookup");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_readlink(fhandle_t *args, readlinkres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READLINK,
+ xdr_readlink, (char *)args, xdr_readlink, (char *)reply,
+ Nfs_timers[Ops[READLINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "readlink");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_read(readargs *args, readres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READ,
+ xdr_read, (char *)args, xdr_read, (char *)reply,
+ Nfs_timers[Ops[READ].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "read");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_write(writeargs *args, attrstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client,
+ NFSPROC_WRITE, xdr_write, (char *)args, xdr_write, (char *)reply,
+ Nfs_timers[Ops[WRITE].call_class]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "write");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_create(createargs *args, diropres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client,
+ NFSPROC_CREATE, xdr_create, (char *)args, xdr_create, (char *)reply,
+ Nfs_timers[Ops[CREATE].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "create");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_remove(diropargs *args, nfsstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client,
+ NFSPROC_REMOVE, xdr_remove, (char *)args, xdr_remove, (char *)reply,
+ Nfs_timers[Ops[REMOVE].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "remove");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_rename(renameargs *args, nfsstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_RENAME,
+ xdr_rename, (char *)args, xdr_rename, (char *)reply,
+ Nfs_timers[Ops[RENAME].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "rename");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_link(linkargs *args, nfsstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_LINK,
+ xdr_link, (char *)args, xdr_link, (char *)reply,
+ Nfs_timers[Ops[LINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "link");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_symlink(symlinkargs *args, nfsstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_SYMLINK,
+ xdr_symlink, (char *)args, xdr_symlink, (char *)reply,
+ Nfs_timers[Ops[SYMLINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "symlink");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_mkdir(mkdirargs *args, diropres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_MKDIR,
+ xdr_mkdir, (char *)args, xdr_mkdir, (char *)reply,
+ Nfs_timers[Ops[MKDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "mkdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_rmdir(diropargs *args, nfsstat *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_RMDIR,
+ xdr_rmdir, (char *)args, xdr_rmdir, (char *)reply,
+ Nfs_timers[Ops[RMDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "rmdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_readdir(readdirargs *args, readdirres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_READDIR,
+ xdr_readdir, (char *)args, xdr_readdir, (char *)reply,
+ Nfs_timers[Ops[READDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "readdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_statfs(fhandle_t *args, statfsres *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC_STATFS,
+ xdr_statfs, (char *)args, xdr_statfs, (char *)reply,
+ Nfs_timers[Ops[FSSTAT].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "statfs");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+static void
+create_tmp_handles(void)
+{
+ int filenum;
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if(Io_files[filenum].fh_data == (sfs_fh_data *)0)
+ {
+ Io_files[filenum].fh_data =
+ calloc(1,sizeof(sfs_fh_data));
+ Io_files[filenum].attributes2.type = NFNON;
+ Io_files[filenum].attributes3.type = NF3NON;
+ }
+ }
+}
+
+static void
+delete_tmp_handles()
+{
+ int filenum;
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if(Io_files[filenum].fh_data != (sfs_fh_data *)0)
+ {
+ free(Io_files[filenum].fh_data);
+ Io_files[filenum].fh_data=(sfs_fh_data *)0;
+ }
+ }
+}
+
+
+
+
+
+
+
+
--- /dev/null
+#ifndef lint
+static char sfs_c_xdrSid[] = "@(#)sfs_2_xdr.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * -------------------------- sfs_c_xdr.c --------------------------
+ *
+ * XDR routines for the nfs protocol.
+ *
+ *.Exported_routines
+ * bool_t xdr_fhstatus(XDR *, struct fhstatus *)
+ * bool_t xdr_path(XDR *, char **)
+ * bool_t xdr_fattr(XDR *, fattr *)
+ * bool_t xdr_sattr(XDR *, sattr *)
+ * bool_t xdr_null(void)
+ * bool_t xdr_getattr(XDR *, char *)
+ * bool_t xdr_setattr(XDR *, char *)
+ * bool_t xdr_root(void)
+ * bool_t xdr_lookup(XDR *, char *)
+ * bool_t xdr_readlink(XDR *, char *)
+ * bool_t xdr_read(XDR *, char *)
+ * bool_t xdr_write(XDR *, char *)
+ * bool_t xdr_create(XDR *, char *)
+ * bool_t xdr_remove(XDR *, char *)
+ * bool_t xdr_rename(XDR *, char *)
+ * bool_t xdr_link(XDR *, char *)
+ * bool_t xdr_symlink(XDR *, char *)
+ * bool_t xdr_mkdir(XDR *, char *)
+ * bool_t xdr_rmdir(XDR *, char *)
+ * bool_t xdr_readdir(XDR *, char *)
+ * bool_t xdr_statfs(XDR *, char *)
+ *
+ *.Local_routines
+ * bool_t xdr_timeval(XDR *, nfstime *)
+ * bool_t xdr_nfsstat(XDR *, nfsstat *)
+ * bool_t xdr_ftype(XDR *, ftype *)
+ * bool_t xdr_diropargs(XDR *, diropargs *)
+ * bool_t xdr_diropres(XDR *, diropres *)
+ * bool_t xdr_attrstat(XDR *, attrstat *)
+ *
+ *.Revision_History
+ * 28-NOv-91 Teelucksingh ANSI C
+ * 25-Jun-91 Santa Wiryaman Changed return values to TRUE when
+ * status != NFS_OK. This way we can
+ * decode NFS error messages.
+ * 17-May-90 Richard Bean Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * ----------------------- Forward Definitions -----------------------
+ */
+
+static bool_t xdr_f_handle(XDR *, fhandle_t *);
+static bool_t xdr_fattr(XDR *, fattr *);
+static bool_t xdr_sattr(XDR *, sattr *);
+static bool_t xdr_timeval(XDR *, nfstime *);
+static bool_t xdr_nfsstat(XDR *, nfsstat *);
+static bool_t xdr_ftype(XDR *, ftype *);
+static bool_t xdr_diropargs(XDR *, diropargs *);
+static bool_t xdr_diropres(XDR *, diropres *);
+static bool_t xdr_attrstat(XDR *, attrstat *);
+
+/*
+ * ----------------------- SFS XDR Routines -------------------------
+ */
+
+
+static bool_t
+xdr_f_handle(
+ XDR * xdrs,
+ fhandle_t * fhandle_ptr)
+{
+ return(xdr_opaque(xdrs, (char *)fhandle_ptr, NFS_FHSIZE));
+}
+
+
+static bool_t
+xdr_timeval(
+ XDR * xdrs,
+ nfstime * timeval_ptr)
+{
+ return(xdr_int32_t(xdrs, (int32_t *) &timeval_ptr->seconds) &&
+ xdr_int32_t(xdrs, (int32_t *) &timeval_ptr->useconds));
+}
+
+
+static bool_t
+xdr_nfsstat(
+ XDR * xdrs,
+ nfsstat * nfsstat_ptr)
+{
+ return(xdr_enum(xdrs, (enum_t *) nfsstat_ptr));
+}
+
+
+static bool_t
+xdr_ftype(
+ XDR * xdrs,
+ ftype * ftype_ptr)
+{
+ return(xdr_enum(xdrs, (enum_t *) ftype_ptr));
+}
+
+
+bool_t
+xdr_fhstatus(
+ XDR * xdrs,
+ struct fhstatus * fhsp)
+{
+ if (!xdr_nfsstat(xdrs, (nfsstat *) &fhsp->fhs_status)) {
+ return(FALSE);
+ }
+ if (fhsp->fhs_status != (int)NFS_OK) {
+ return(TRUE);
+ }
+ return(xdr_f_handle(xdrs, &fhsp->fhs_fh));
+}
+
+
+bool_t
+xdr_path(
+ XDR * xdrs,
+ char ** pathp)
+{
+ return(xdr_string(xdrs, pathp, NFS_MAXPATHLEN));
+}
+
+
+static bool_t
+xdr_fattr(
+ XDR * xdrs,
+ fattr * fattr_ptr)
+{
+ return(xdr_ftype(xdrs, &fattr_ptr->type) &&
+ xdr_u_int(xdrs, &fattr_ptr->mode) &&
+ xdr_u_int(xdrs, &fattr_ptr->nlink) &&
+ xdr_u_int(xdrs, &fattr_ptr->uid) &&
+ xdr_u_int(xdrs, &fattr_ptr->gid) &&
+ xdr_u_int(xdrs, &fattr_ptr->size) &&
+ xdr_u_int(xdrs, &fattr_ptr->blocksize) &&
+ xdr_u_int(xdrs, &fattr_ptr->rdev) &&
+ xdr_u_int(xdrs, &fattr_ptr->blocks) &&
+ xdr_u_int(xdrs, &fattr_ptr->fsid) &&
+ xdr_u_int(xdrs, &fattr_ptr->fileid) &&
+ xdr_timeval(xdrs, &fattr_ptr->atime) &&
+ xdr_timeval(xdrs, &fattr_ptr->mtime) &&
+ xdr_timeval(xdrs, &fattr_ptr->ctime));
+}
+
+
+static bool_t
+xdr_sattr(
+ XDR * xdrs,
+ sattr * sattr_ptr)
+{
+ return(xdr_u_int(xdrs, &sattr_ptr->mode) &&
+ xdr_u_int(xdrs, &sattr_ptr->uid) &&
+ xdr_u_int(xdrs, &sattr_ptr->gid) &&
+ xdr_u_int(xdrs, &sattr_ptr->size) &&
+ xdr_timeval(xdrs, &sattr_ptr->atime) &&
+ xdr_timeval(xdrs, &sattr_ptr->mtime));
+}
+
+
+static bool_t
+xdr_diropargs(
+ XDR * xdrs,
+ diropargs * dir_args_ptr)
+{
+ return(xdr_f_handle(xdrs, &dir_args_ptr->dir) &&
+ xdr_path(xdrs, &dir_args_ptr->name));
+}
+
+
+static bool_t
+xdr_diropres(
+ XDR * xdrs,
+ diropres * dir_res_ptr)
+{
+ if (!xdr_nfsstat(xdrs, &dir_res_ptr->status)) {
+ return(FALSE);
+ }
+
+ if (dir_res_ptr->status == NFS_OK) {
+ return(xdr_f_handle(xdrs, &dir_res_ptr->diropres_u.diropres.file) &&
+ xdr_fattr(xdrs, &dir_res_ptr->diropres_u.diropres.attributes));
+ }
+ return(TRUE);
+}
+
+
+static bool_t
+xdr_attrstat(
+ XDR * xdrs,
+ attrstat * attrstat_ptr)
+{
+ if (!xdr_nfsstat(xdrs, &attrstat_ptr->status)) {
+ return(FALSE);
+ }
+
+ if (attrstat_ptr->status == NFS_OK) {
+ return(xdr_fattr(xdrs, &attrstat_ptr->attrstat_u.attributes));
+ }
+ return(TRUE);
+}
+
+
+bool_t
+xdr_getattr(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ fhandle_t * fhandle_ptr;
+ attrstat * attrstat_ptr;
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ fhandle_ptr = (fhandle_t *) params_ptr;
+ return(xdr_f_handle(xdrs, fhandle_ptr));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ attrstat_ptr = (attrstat *) params_ptr;
+ return(xdr_attrstat(xdrs, attrstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_setattr(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ sattrargs * sattrargs_ptr;
+ attrstat * attrstat_ptr;
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ sattrargs_ptr = (sattrargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &sattrargs_ptr->file) &&
+ xdr_sattr(xdrs, &sattrargs_ptr->attributes));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ attrstat_ptr = (attrstat *) params_ptr;
+ return(xdr_attrstat(xdrs, attrstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_lookup(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ diropargs * diropargs_ptr;
+ diropres * diropres_ptr;
+
+
+ switch(xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ diropargs_ptr = (diropargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &diropargs_ptr->dir) &&
+ xdr_path(xdrs, &diropargs_ptr->name));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ diropres_ptr = (diropres *) params_ptr;
+ return(xdr_diropres(xdrs, diropres_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+bool_t
+xdr_readlink(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ fhandle_t * fhandle_ptr;
+ readlinkres * readlinkres_ptr;
+
+ switch(xdrs->x_op) {
+ case XDR_ENCODE:
+ fhandle_ptr = (fhandle_t *) params_ptr;
+ return(xdr_f_handle(xdrs, fhandle_ptr));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ readlinkres_ptr = (readlinkres *) params_ptr;
+ if (!xdr_nfsstat(xdrs, &readlinkres_ptr->status)) {
+ return(FALSE);
+ }
+ if (readlinkres_ptr->status != NFS_OK) {
+ return(TRUE);
+ }
+ return(xdr_bytes(xdrs, &readlinkres_ptr->readlinkres_u.data,
+ (unsigned int *)
+ &readlinkres_ptr->readlinkres_u.len,
+ (unsigned int) NFS_MAXPATHLEN));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_read(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ readargs * readargs_ptr;
+ readres * readres_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ readargs_ptr = (readargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &readargs_ptr->file) &&
+ xdr_u_int(xdrs, &readargs_ptr->offset) &&
+ xdr_u_int(xdrs, &readargs_ptr->count) &&
+ xdr_u_int(xdrs, &readargs_ptr->totalcount));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ readres_ptr = (readres *) params_ptr;
+ if (!xdr_nfsstat(xdrs, &readres_ptr->status)) {
+ return(FALSE);
+ }
+ if (readres_ptr->status != NFS_OK) {
+ return(TRUE);
+ }
+ return(xdr_fattr(xdrs, &readres_ptr->readres_u.reply.attributes) &&
+ xdr_bytes(xdrs, &readres_ptr->readres_u.reply.data.data_val,
+ &readres_ptr->readres_u.reply.data.data_len,
+ (unsigned int) NFS_MAXDATA));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+bool_t
+xdr_write(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ writeargs * writeargs_ptr;
+ attrstat * attrstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ writeargs_ptr = (writeargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &writeargs_ptr->file) &&
+ xdr_u_int(xdrs, &writeargs_ptr->beginoffset) &&
+ xdr_u_int(xdrs, &writeargs_ptr->offset) &&
+ xdr_u_int(xdrs, &writeargs_ptr->totalcount) &&
+ xdr_bytes(xdrs, &writeargs_ptr->data.data_val,
+ &writeargs_ptr->data.data_len,
+ (unsigned int) NFS_MAXDATA));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ attrstat_ptr = (attrstat *) params_ptr;
+ return(xdr_attrstat(xdrs, attrstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_create(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ createargs * createargs_ptr;
+ diropres * diropres_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ createargs_ptr = (createargs *) params_ptr;
+ return(xdr_diropargs(xdrs, &createargs_ptr->where) &&
+ xdr_sattr(xdrs, &createargs_ptr->attributes));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ diropres_ptr = (diropres *) params_ptr;
+ return(xdr_diropres(xdrs, diropres_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_remove(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ diropargs * diropargs_ptr;
+ nfsstat * nfsstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ diropargs_ptr = (diropargs *) params_ptr;
+ return(xdr_diropargs (xdrs, diropargs_ptr));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ nfsstat_ptr = (nfsstat *) params_ptr;
+ return(xdr_nfsstat(xdrs, nfsstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_rename(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ renameargs * renameargs_ptr;
+ nfsstat * nfsstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ renameargs_ptr = (renameargs *) params_ptr;
+ return(xdr_diropargs(xdrs, &renameargs_ptr->from) &&
+ xdr_diropargs(xdrs, &renameargs_ptr->to));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ nfsstat_ptr = (nfsstat *) params_ptr;
+ return(xdr_nfsstat(xdrs, nfsstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_link(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ linkargs * linkargs_ptr;
+ nfsstat * nfsstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ linkargs_ptr = (linkargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &linkargs_ptr->from) &&
+ xdr_diropargs(xdrs, &linkargs_ptr->to));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ nfsstat_ptr = (nfsstat *) params_ptr;
+ return(xdr_nfsstat(xdrs, nfsstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_symlink(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ symlinkargs * symlinkargs_ptr;
+ nfsstat * nfsstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ symlinkargs_ptr = (symlinkargs *) params_ptr;
+ return(xdr_diropargs(xdrs, &symlinkargs_ptr->from) &&
+ xdr_path(xdrs, &symlinkargs_ptr->to) &&
+ xdr_sattr(xdrs, &symlinkargs_ptr->attributes));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ nfsstat_ptr = (nfsstat *) params_ptr;
+ return(xdr_nfsstat(xdrs, nfsstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_mkdir(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ mkdirargs * mkdirargs_ptr;
+ diropres * diropres_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ mkdirargs_ptr = (mkdirargs *) params_ptr;
+ return(xdr_diropargs(xdrs, &mkdirargs_ptr->where) &&
+ xdr_sattr(xdrs, &mkdirargs_ptr->attributes));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ diropres_ptr = (diropres *) params_ptr;
+ return(xdr_diropres(xdrs, diropres_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_rmdir(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ diropargs * diropargs_ptr;
+ nfsstat * nfsstat_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ diropargs_ptr = (diropargs *) params_ptr;
+ return(xdr_diropargs(xdrs, diropargs_ptr));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ nfsstat_ptr = (nfsstat *) params_ptr;
+ return(xdr_nfsstat(xdrs, nfsstat_ptr));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+bool_t
+xdr_readdir(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ readdirargs * readdirargs_ptr;
+ readdirres * readdirres_ptr;
+ entry * entry_ptr;
+ int n; /* entry ctr */
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ /* LINTED pointer cast */
+ readdirargs_ptr = (readdirargs *) params_ptr;
+ return(xdr_f_handle(xdrs, &readdirargs_ptr->dir) &&
+ xdr_opaque(xdrs, (char *) readdirargs_ptr->cookie,
+ NFS_COOKIESIZE) &&
+ xdr_u_int(xdrs, &readdirargs_ptr->count));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ readdirres_ptr = (readdirres *) params_ptr;
+ if (!xdr_nfsstat(xdrs, &readdirres_ptr->status)) {
+ return(FALSE);
+ }
+ if (readdirres_ptr->status != NFS_OK) {
+ return(TRUE);
+ }
+
+ /*
+ * go thru the stream of entries until hit an invalid one
+ * or have gotten all the user asked for.
+ *
+ * max_entries is read to obtain a maximum. it is written
+ * to return how many entries were decoded.
+ */
+ entry_ptr = readdirres_ptr->readdirres_u.reply.entries;
+
+ n = 0;
+ while (n < readdirres_ptr->readdirres_u.reply.max_entries) {
+ if (!xdr_bool(xdrs, &entry_ptr->valid)) {
+ return(FALSE);
+ }
+
+ if (!entry_ptr->valid) {
+ break;
+ }
+
+ if (!xdr_u_int(xdrs, &entry_ptr->fileid)) {
+ return(FALSE);
+ }
+
+ if (!xdr_uint16_t(xdrs, &entry_ptr->name_len)) {
+ return(FALSE);
+ }
+
+ if (!xdr_opaque(xdrs, entry_ptr->name, entry_ptr->name_len)) {
+ return(FALSE);
+ }
+
+ if (!xdr_opaque(xdrs, entry_ptr->cookie, NFS_COOKIESIZE)) {
+ return(FALSE);
+ }
+
+ n++;
+ entry_ptr++;
+ } /* while extracting entries */
+
+ /* If we are at the user's data buffer limit, stop right now. */
+ if (n == readdirres_ptr->readdirres_u.reply.max_entries) {
+ return(TRUE);
+ }
+
+ /* Return how many entries were gotten for the dirlist */
+ readdirres_ptr->readdirres_u.reply.max_entries = n;
+
+ /* check the EOF flag for the dirlist */
+ if(!xdr_bool(xdrs, &readdirres_ptr->readdirres_u.reply.eof)) {
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+bool_t
+xdr_statfs(
+ XDR * xdrs,
+ char * params_ptr)
+{
+ fhandle_t * fhandle_ptr;
+ statfsres * statfsres_ptr;
+
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ fhandle_ptr = (fhandle_t *) params_ptr;
+ return(xdr_f_handle(xdrs, fhandle_ptr));
+
+ case XDR_DECODE:
+ /* LINTED pointer cast */
+ statfsres_ptr = (statfsres *) params_ptr;
+ if (!xdr_nfsstat(xdrs, &statfsres_ptr->status)) {
+ return(FALSE);
+ }
+ if (statfsres_ptr->status != NFS_OK) {
+ return(TRUE);
+ }
+ return(xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.tsize) &&
+ xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bsize) &&
+ xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.blocks) &&
+ xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bfree) &&
+ xdr_u_int(xdrs, &statfsres_ptr->statfsres_u.reply.bavail));
+
+ default:
+ return(FALSE);
+ } /* switch on operation */
+}
+
+
+/* sfs_c_xdr.c */
--- /dev/null
+#ifndef lint
+static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_3_ops.c ---------------------
+ *
+ * RPC routines to implement the NFS protocol.
+ *
+ *.Local Routines
+ * int op_null(void)
+ * int op_getattr(void)
+ * int op_setattr(int)
+ * int op_lookup(void)
+ * int op_access(void)
+ * int op_readlink(void)
+ * int op_read(int)
+ * int op_write(int, int, stable_how)
+ * int op_create(void)
+ * int op_mkdir(void);
+ * int op_symlink(void);
+ * int op_mknod(void);
+ * int op_remove(void);
+ * int op_rmdir(void);
+ * int op_rename(void);
+ * int op_link(void);
+ * int op_readdir(void);
+ * int op_readdirplus(void);
+ * int op_fsstat(void);
+ * int op_fsinfo(void);
+ * int op_pathconf(void);
+ * int op_commit(void);
+ *
+ *.Revision_History
+ * 30-Jun-94 ChakChung Ng Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * -------------------- Local NFS ops function --------------------
+ */
+static int op_null(void);
+static int op_getattr(void);
+static int op_setattr(int);
+static int op_lookup(void);
+static int op_access(void);
+static int op_readlink(void);
+static int op_read(int);
+static int op_write(int, int, stable_how);
+static int op_create(void);
+static int op_mkdir(void);
+static int op_symlink(void);
+static int op_mknod(void);
+static int op_remove(void);
+static int op_rmdir(void);
+static int op_rename(void);
+static int op_link(void);
+static int op_readdir(void);
+static int op_readdirplus(void);
+static int op_fsstat(void);
+static int op_fsinfo(void);
+static int op_pathconf(void);
+static int op_commit(void);
+static int op_nosys(void);
+static char *nfs3_strerror(int);
+
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+static sfs_op_type nfsv3_Ops[] = {
+
+/* name mix function op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 11, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 27, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 18, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 9, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 2, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 7, op_access, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 5, op_commit, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 1, op_fsinfo, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, op_mknod, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, op_pathconf, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 9, op_readdirplus, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+
+sfs_op_type *Ops;
+
+/*
+ * -------------------- RPC routines for NFS protocol --------------------
+ */
+
+void
+init_ops(void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/*
+ * The routines below attempt to do over-the-wire operations.
+ * Each op tries to cause one or more of a particular
+ * NFS operation to go over the wire. OPs return the success
+ * of their NFS call(s). Each OP records how many calls it
+ * actually made in global data.
+ *
+ * An array of file information is kept for files existing in
+ * the test directory. File handles, attributes, names, etc
+ * are stored in this array.
+ *
+ */
+
+/*
+ * Generic catch all for operations not covered by this protocol.
+ */
+static int
+op_nosys(int i)
+{
+ RFS_ASSERT (dep_tab[i].flag == TO_BE_SENT);
+ dep_tab[i].flag = DONE;
+ Ops[TOTAL].results.bad_calls++;
+ if (i==min_index)
+ adjust_min_index ();
+ return(0);
+}
+
+static struct biod_req *
+get_biod_reqp (int dep_tab_index)
+{
+ static int index = 0;
+ int i;
+
+ for (i=0; i<max_biod_reqs; i++, index = (index+1)%max_biod_reqs) {
+ if (biod_reqp[index].in_use == FALSE) {
+ biod_reqp[index].in_use = TRUE;
+ dep_tab[dep_tab_index].biod_index = index;
+ biod_req[index].dep_tab_index = dep_tab_index;
+ return (biod_reqp+index);
+ }
+ }
+}
+
+static int
+op_null(int i)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ struct biod_req * reqp;
+ struct ladtime call_timeout;
+
+ if (dep_tab[i].flag == SENT)
+ goto RECEIVE_REPLY;
+
+ op_ptr = &Ops[NULLCALL];
+ reqp = get_biod_reqp(i);
+
+ call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+
+ ret = 0;
+
+ /* make the call */
+ sfs_gettime(&reqp->start);
+ reqp->xid = boid_clnt_call(NFS_client, NFS3PROC_NULL, xdr_void, (char *)0);
+ if (reqp->xid != 0) {
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ num_out_reqs++;
+ dep_tab[i].flag = SENT;
+ } else
+ RFS_ASSERT (0);
+
+ return 0;
+
+RECEIVE_REPLY:
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL,
+ xdr_void, (char *)0, xdr_void, (char *)0,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: null_op call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_null */
+
+
+static int
+op_getattr(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ GETATTR3args args; /* fh to do op on */
+ GETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[GETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR,
+ xdr_GETATTR3args, (char *) &args,
+ xdr_GETATTR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_getattr */
+
+
+/*
+ * perform an RPC setattr operation. If 'truncate_size' is non-negative,
+ * truncate the file to that size.
+ */
+static int
+op_setattr(
+ int truncate_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ SETATTR3args args;
+ SETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.new_attributes.mode.set_it = TRUE;
+ args.new_attributes.mode.mode = (uint32_t) 0666;
+ args.new_attributes.uid.set_it = FALSE;
+ args.new_attributes.uid.uid = (uint32_t) -1;
+ args.new_attributes.gid.set_it = FALSE;
+ args.new_attributes.gid.gid = (uint32_t) -1;
+ args.new_attributes.size.set_it = FALSE;
+ args.new_attributes.size.size._p._u = (uint32_t) ~0;
+ args.new_attributes.size.size._p._l = (uint32_t) -1;
+ args.new_attributes.atime.set_it = TRUE;
+ args.new_attributes.atime.atime.seconds = Cur_time.esec;
+ args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.new_attributes.mtime.set_it = TRUE;
+ args.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+ args.guard.check = FALSE;
+
+ /* handle file truncations */
+ if (truncate_size >= 0) {
+ args.new_attributes.size.set_it = TRUE;
+ args.new_attributes.size.size._p._u = (uint32_t) 0;
+ if (truncate_size > Cur_file_ptr->attributes3.size._p._l)
+ args.new_attributes.size.size._p._l = (uint32_t) 0;
+ else
+ args.new_attributes.size.size._p._l =
+ (uint32_t) Cur_file_ptr->attributes3.size._p._l -
+ truncate_size;
+ }
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR,
+ xdr_SETATTR3args, (char *) &args,
+ xdr_SETATTR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_wcc.after.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_setattr */
+
+
+static int
+op_lookup(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LOOKUP];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.what.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.what.name = Cur_filename;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.object,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ /* We do lookup Nonexistent and this is not an error */
+ if (reply.status != NFS3ERR_NOENT ||
+ Cur_file_ptr->state != Nonexistent) {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: lookup call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_lookup */
+
+
+static int
+op_access(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ ACCESS3args args;
+ ACCESS3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[ACCESS];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.access = ACCESS3_MODIFY;
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS,
+ xdr_ACCESS3args, (char *) &args,
+ xdr_ACCESS3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: access call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: access call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_access */
+
+
+static int
+op_readlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READLINK3args args; /* the args */
+ READLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN];
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ /*
+ * Note: this symlink may be bogus because SYMLINK does
+ * not return a symlink ... only a status. So unless we have
+ * done a LOOKUP on this guy, the symlink will probably be bad.
+ * If it is bad it shows up as a symlink error in the results.
+ */
+ (void) memmove((char *) &args.symlink,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.data = sym_data;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK,
+ xdr_READLINK3args, (char *) &args,
+ xdr_READLINK3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READLINK on %s returned %s\n",
+ sfs_Myname, Cur_filename, sym_data);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readlink */
+
+
+/*
+ * perform an RPC read operation of length 'xfer_size'
+ */
+static int
+op_read(
+ int xfer_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int cur_cnt;
+ int max_cnt; /* packet ctrs */
+ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
+ READ3args args;
+ READ3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int size;
+ int j;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READ];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /*
+ * Don't allow a read of less than one block size
+ */
+ if (xfer_size < Bytes_per_block)
+ xfer_size = Bytes_per_block;
+
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ args.offset._p._u = 0;
+ if (Cur_file_ptr->attributes3.size._p._l > xfer_size)
+ args.offset._p._l = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes3.size._p._l - xfer_size)
+ / Bytes_per_block) + 1));
+ else
+ args.offset._p._l = 0;
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.data.data_len = 0;
+ reply.resok.data.data_val = buf;
+
+ /* first read the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block -1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ args.count = size;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "read: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READ,
+ xdr_READ3args, (char *) &args,
+ xdr_READ3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS3_OK)
+ ? reply.resok.data.data_len : 0;
+ Dump_offset = args.offset._p._l;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ size = reply.resok.data.data_len;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READ %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ args.offset._p._l += size;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call NFS error %s on file %d\n",
+ sfs_Myname,
+ nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for reading max_cnt packets */
+ } /* for buffers and fragments */
+ return(ret);
+
+} /* op_read */
+
+char *
+init_write_buffer(
+ void)
+{
+ uint32_t *bp;
+ static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)];
+ uint32_t *be = write_buf + (sizeof(write_buf) /
+ sizeof(uint32_t));
+
+ if (write_buf[0] != (uint32_t)0xdeadbeef) {
+ for (bp = write_buf; bp < be; bp++)
+ *bp = (uint32_t)0xdeadbeef;
+ }
+ return (char *)write_buf;
+}
+
+
+/*
+ * Perform and RPC write operation of length 'xfer_size'. If 'append_flag'
+ * is true, then write the data to the end of the file.
+ *
+ * If the stab_flag is set to UNSTABLE we issue the requests and then
+ * issue a op_commit to sync the data.
+ */
+static int
+op_write(
+ int xfer_size,
+ int append_flag,
+ stable_how stab_flag)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ static char *buf = NULL; /* the data buffer */
+ unsigned int size; /* size of data write */
+ int cur_cnt; /* controls # NFS calls */
+ int max_cnt;
+ WRITE3args args;
+ WRITE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int j;
+ int ret; /* ret val == call success */
+
+ /*
+ * For now we treat DATA_SYNC to be the same as FILE_SYNC.
+ */
+ if (stab_flag == DATA_SYNC)
+ stab_flag = FILE_SYNC;
+
+ /*
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+ op_ptr = &Ops[WRITE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = 0;
+ if (append_flag == 1) {
+ args.offset._p._l = Cur_file_ptr->attributes3.size._p._l;
+ } else {
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (Cur_file_ptr->attributes3.size._p._l > xfer_size) {
+ args.offset._p._l = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes3.size._p._l - xfer_size)
+ / Bytes_per_block) + 1));
+ } else
+ args.offset._p._l = 0;
+ }
+
+ /* stab_flag has to be set in op() in sfs_3_chd.c */
+ args.stable = stab_flag;
+
+ /* first write the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block - 1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ args.count = size;
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "write: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ if (DEBUG_CHILD_RPC) {
+(void) fprintf(stderr, "%s: WRITE %s offset %u count %lu stable %d\n",
+sfs_Myname, Cur_filename, args.offset._p._l, args.count, args.stable);
+ (void) fflush(stderr);
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args,
+ xdr_WRITE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = args.data.data_len;
+ Dump_offset = args.offset._p._l;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ args.offset._p._l += size;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: WRITE %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for writing max_cnt packets */
+ } /* for buffers and fragments */
+
+ /*
+ * If we have not gotten an error and we were asked for an async write
+ * send a commit operation.
+ */
+ if (ret && stab_flag != FILE_SYNC)
+ ret += op_commit();
+
+ return(ret);
+
+} /* op_write */
+
+
+static int
+op_create(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ CREATE3args args;
+ CREATE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[CREATE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666);
+ args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ args.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0;
+ args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE,
+ xdr_CREATE3args, (char *) &args,
+ xdr_CREATE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: create call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_create */
+
+
+static int
+op_mkdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ MKDIR3args args;
+ MKDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[MKDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.attributes.mode.set_it = TRUE;
+ args.attributes.mode.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid.set_it = TRUE;
+ args.attributes.uid.uid = Cur_uid;
+ args.attributes.gid.set_it = TRUE;
+ args.attributes.gid.gid = Cur_gid;
+ args.attributes.size.set_it = TRUE;
+ args.attributes.size.size._p._u = 0;
+ args.attributes.size.size._p._l = 512;
+ args.attributes.atime.set_it = TRUE;
+ args.attributes.atime.atime.seconds = Cur_time.esec;
+ args.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.attributes.mtime.set_it = TRUE;
+ args.attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR,
+ xdr_MKDIR3args, (char *) &args,
+ xdr_MKDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Empty_dir;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: mkdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_mkdir */
+
+
+static int
+op_symlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target file */
+ SYMLINK3args args;
+ SYMLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SYMLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target_fileinfo_ptr->file_name);
+ args.symlink.symlink_attributes.size.set_it = TRUE;
+ args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0;
+ args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data);
+ args.symlink.symlink_data = sym_data;
+
+ args.symlink.symlink_attributes.mode.set_it = TRUE;
+ args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+ args.symlink.symlink_attributes.uid.set_it = TRUE;
+ args.symlink.symlink_attributes.uid.uid = Cur_uid;
+ args.symlink.symlink_attributes.gid.set_it = TRUE;
+ args.symlink.symlink_attributes.gid.gid = Cur_gid;
+ args.symlink.symlink_attributes.atime.set_it = TRUE;
+ args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.symlink.symlink_attributes.mtime.set_it = TRUE;
+ args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK,
+ xdr_SYMLINK3args, (char *) &args,
+ xdr_SYMLINK3res, (char *) &reply,
+ ((int)Current_test_phase < (int)Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ /*
+ * SYMLINK doesn't return a fh. If we try to access this symlink
+ * (eg, remove(), readlink()) before we do a lookup, we won't have
+ * a fh to use. So, we do a lookup call here. If it fails, we fill
+ * in what we can.
+ */
+ Cur_file_ptr->state = Exists;
+ if (op_lookup() == 0) {
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes3.type = NF3LNK;
+ Cur_file_ptr->attributes3.mode = (NFSMODE_LNK|0777);
+ Cur_file_ptr->attributes3.uid = Cur_uid;
+ Cur_file_ptr->attributes3.gid = Cur_gid;
+ Cur_file_ptr->attributes3.atime.seconds = Cur_time.esec;
+ Cur_file_ptr->attributes3.atime.nseconds =
+ Cur_time.usec * 1000;
+ Cur_file_ptr->attributes3.mtime =
+ Cur_file_ptr->attributes3.atime;
+ } else
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: symlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: symlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_symlink */
+
+
+static int
+op_mknod(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ MKNOD3args args;
+ MKNOD3res reply;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[MKNOD];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.what.type = NF3FIFO;
+ args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777);
+ args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid;
+ args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid;
+ args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._l =
+ (uint32_t) 512;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD,
+ xdr_MKNOD3args, (char *) &args,
+ xdr_MKNOD3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: mknod call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: mknod call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_mknod */
+
+
+static int
+op_remove(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ REMOVE3args args;
+ REMOVE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[REMOVE];
+ ret = 0;
+
+ /* set up the arguments */
+ args.object.name = Cur_filename;
+ (void) memmove((char *) &args.object.dir,(char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE,
+ xdr_REMOVE3args, (char *) &args,
+ xdr_REMOVE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: remove call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_remove */
+
+
+static int
+op_rmdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ RMDIR3args args;
+ RMDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RMDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.object.name = Cur_file_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR,
+ xdr_RMDIR3args, (char *) &args,
+ xdr_RMDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rmdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rmdir */
+
+
+static int
+op_rename(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target name */
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RENAME];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent,
+ Sfs_non_io_file);
+
+ args.from.name = Cur_file_ptr->file_name;
+ (void) sprintf(target_fileinfo_ptr->file_name, Filespec,
+ target_fileinfo_ptr->unique_num);
+ args.to.name = target_fileinfo_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ target_fileinfo_ptr->state = Exists;
+ (void) memmove((char *) &target_fileinfo_ptr->fh3,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ target_fileinfo_ptr->attributes3 = Cur_file_ptr->attributes3;
+ target_fileinfo_ptr->size = fh_size(Cur_file_ptr);
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rename call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rename */
+
+
+static int
+op_link(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target */
+ LINK3args args;
+ LINK3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.file, (char *) &target_fileinfo_ptr->fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &args.link.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.link.name = Cur_filename;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK,
+ xdr_LINK3args, (char *) &args,
+ xdr_LINK3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &target_fileinfo_ptr->fh3,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ target_fileinfo_ptr->attributes3.nlink++;
+ Cur_file_ptr->attributes3 = target_fileinfo_ptr->attributes3;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: link call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_link */
+
+
+static int
+op_readdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READDIR3args args;
+ READDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int i;
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+ bool_t hit_eof;
+ /* array of entries */
+ entry3 entry_stream[SFS_MAXDIRENTS];
+ entry3 *entries; /* ptr to the dir entry */
+
+ op_ptr = &Ops[READDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args.count = DEFAULT_MAX_BUFSIZE;
+
+ /* Have lower layers fill in the data directly. */
+ (void) memset((char *) &reply, '\0', sizeof (reply));
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entry3) * SFS_MAXDIRENTS);
+ reply.resok.count = SFS_MAXDIRENTS;
+ reply.resok.reply.entries = entry_stream;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR,
+ xdr_READDIR3args, (char *) &args,
+ xdr_READDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+
+ if (DEBUG_CHILD_RPC) {
+ hit_eof = reply.resok.reply.eof;
+ entries = reply.resok.reply.entries;
+ for (i = 0; i < reply.resok.count; i++) {
+ (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n",
+ sfs_Myname, hit_eof, entries[i].name);
+ }
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readdir */
+
+
+static int
+op_readdirplus(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READDIRPLUS3args args;
+ READDIRPLUS3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int i;
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+ bool_t hit_eof;
+ /* array of entries */
+ entryplus3 entry_stream[SFS_MAXDIRENTS];
+ entryplus3 *entries;
+
+ op_ptr = &Ops[READDIRPLUS];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entryplus3) * SFS_MAXDIRENTS);
+ args.dircount = DEFAULT_MAX_BUFSIZE;
+ args.maxcount = DEFAULT_MAX_BUFSIZE;
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.count = SFS_MAXDIRENTS;
+ reply.resok.reply.entries = entry_stream;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIRPLUS,
+ xdr_READDIRPLUS3args, (char *) &args,
+ xdr_READDIRPLUS3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+
+ if (DEBUG_CHILD_RPC) {
+ hit_eof = reply.resok.reply.eof;
+ entries = reply.resok.reply.entries;
+ for (i = 0; i < reply.resok.count; i++) {
+ (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n",
+ sfs_Myname, hit_eof, entries[i].name);
+ }
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readdirplus */
+
+
+static int
+op_fsstat(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ FSSTAT3args args;
+ FSSTAT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[FSSTAT];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT,
+ xdr_FSSTAT3args, (char *) &args,
+ xdr_FSSTAT3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: fsstat call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_fsstat */
+
+
+static int
+op_fsinfo(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ FSINFO3args args;
+ FSINFO3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[FSINFO];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO,
+ xdr_FSINFO3args, (char *) &args,
+ xdr_FSINFO3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: fsinfo call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_fsinfo */
+
+
+static int
+op_pathconf(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ PATHCONF3args args;
+ PATHCONF3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[PATHCONF];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF,
+ xdr_PATHCONF3args, (char *) &args,
+ xdr_PATHCONF3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: pathconf call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_pathconf */
+
+
+static int
+op_commit(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int32_t size; /* size of data write */
+ COMMIT3args args;
+ COMMIT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[COMMIT];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = Cur_file_ptr->attributes3.size._p._l;
+ size = args.count;
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT,
+ xdr_COMMIT3args, (char *) &args,
+ xdr_COMMIT3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: WRITE %s %ld bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: write call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+
+ return(ret);
+
+} /* op_commit */
+
+
+#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES))
+
+/*
+ * Reliably lookup a file in the current directory
+ * Return:
+ * -1 RPC error
+ * 1 File doesn't exist
+ * 0 File exists
+ */
+int
+lad_lookup(sfs_fh_type *file_ptr, char *name)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.what.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status == NFS3ERR_NOENT) {
+ return(1);
+ }
+
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+
+ file_ptr->state = Exists;
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.object,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ return(0);
+}
+
+/*
+ * Reliably remove a file in the current directory
+ */
+int
+lad_remove(sfs_fh_type *file_ptr, char *name)
+{
+ REMOVE3args args;
+ REMOVE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does exist
+ */
+ if (file_ptr->attributes3.type == NF3DIR)
+ return (lad_rmdir(file_ptr, name));
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.object.name = name;
+ (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3,
+ sizeof(nfs_fh3));
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE,
+ xdr_REMOVE3args, (char *) &args,
+ xdr_REMOVE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably remove a directory in the current directory
+ */
+int
+lad_rmdir(sfs_fh_type *file_ptr, char *name)
+{
+ RMDIR3args args;
+ RMDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does exist and is empty
+ */
+ if (file_ptr->attributes3.type != NF3DIR)
+ return (lad_remove(file_ptr, name));
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_rmdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.object.name = name;
+ (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR,
+ xdr_RMDIR3args, (char *) &args,
+ xdr_RMDIR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably create a symlink in the current directory
+ */
+int
+lad_symlink(sfs_fh_type *file_ptr, char *target, char *name)
+{
+ SYMLINK3args args;
+ SYMLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_symlink: %lx[%lx] %s -> %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name, target);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target);
+ args.symlink.symlink_attributes.size.set_it = TRUE;
+ args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0;
+ args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data);
+ args.symlink.symlink_data = sym_data;
+
+ args.symlink.symlink_attributes.mode.set_it = TRUE;
+ args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+ args.symlink.symlink_attributes.uid.set_it = TRUE;
+ args.symlink.symlink_attributes.uid.uid = Cur_uid;
+ args.symlink.symlink_attributes.gid.set_it = TRUE;
+ args.symlink.symlink_attributes.gid.gid = Cur_gid;
+ args.symlink.symlink_attributes.atime.set_it = TRUE;
+ args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.symlink.symlink_attributes.mtime.set_it = TRUE;
+ args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK,
+ xdr_SYMLINK3args, (char *) &args,
+ xdr_SYMLINK3res, (char *) &reply,
+ Nfs_timers[Init]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n",
+ target, name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ /*
+ * SYMLINK may not return a fh. If we try to
+ * access this symlink (eg, remove(), readlink())
+ * before we do a lookup, we won't have a fh to use.
+ * So, we do a lookup call here.
+ * If it fails, we fill in what we can.
+ */
+ return (lad_lookup(file_ptr, name));
+}
+
+/*
+ * Reliably create a directory in the current directory
+ */
+int
+lad_mkdir(sfs_fh_type *file_ptr, char *name)
+{
+ MKDIR3args args;
+ MKDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+ args.attributes.mode.set_it = TRUE;
+ args.attributes.mode.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid.set_it = TRUE;
+ args.attributes.uid.uid = Cur_uid;
+ args.attributes.gid.set_it = TRUE;
+ args.attributes.gid.gid = Cur_gid;
+ args.attributes.size.set_it = TRUE;
+ args.attributes.size.size._p._u = 0;
+ args.attributes.size.size._p._l = 512;
+ args.attributes.atime.set_it = TRUE;
+ args.attributes.atime.atime.seconds = Cur_time.esec;
+ args.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.attributes.mtime.set_it = TRUE;
+ args.attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR,
+ xdr_MKDIR3args, (char *) &args,
+ xdr_MKDIR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFS3ERR_EXIST)
+ return(1);
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first mkdir suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ }
+ file_ptr->state = Empty_dir;
+
+ return(0);
+}
+
+/*
+ * Reliably commit a file
+ */
+static int
+lad_commit(sfs_fh_type *file_ptr)
+{
+ COMMIT3args args;
+ COMMIT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_commit: %lx[%lx]\n",
+ sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir);
+ (void) fflush(stderr);
+ }
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = file_ptr->attributes3.size._p._l;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT,
+ xdr_COMMIT3args, (char *) &args,
+ xdr_COMMIT3res, (char *) &reply,
+ Nfs_timers[Init]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_commit() RPC call failed : %s\n",
+ clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Reliably write a file in the current directory
+ */
+int
+lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length)
+{
+ static char *buf = NULL; /* the data buffer */
+ int32_t size; /* size of data write */
+ int32_t cur_cnt;
+ WRITE3args args;
+ WRITE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int async = 1;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n",
+ sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, offset, length);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does exist
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+ /*
+ * If a short file write don't bother with the commit, just write sync.
+ */
+ if (length <= Bytes_per_block)
+ async = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = 0;
+ args.offset._p._l = offset;
+ if (async)
+ args.stable = UNSTABLE;
+ else
+ args.stable = FILE_SYNC;
+
+ size = Bytes_per_block;
+ for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) {
+ if ((cur_cnt + size) > length)
+ size = length - cur_cnt;
+
+ if (size == 0)
+ break;
+
+ args.count = size;
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* make the call now */
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args,
+ xdr_WRITE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_write() RPC call failed : %s\n",
+ clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_write() NFS call failed : %s\n",
+ nfs3_strerror(reply.status));
+ return(-1);
+ }
+ file_ptr->state = Exists;
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+
+ args.offset._p._l += size;
+ }
+
+ if (async)
+ (void) lad_commit(file_ptr);
+ return(0);
+}
+
+/*
+ * Reliably create a file in the current directory
+ */
+int
+lad_create(sfs_fh_type *file_ptr, char *name)
+{
+ CREATE3args args;
+ CREATE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666);
+ args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ args.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.size.size._p._u =
+ (uint32_t) 0;
+ args.how.createhow3_u.obj_attributes.size.size._p._l =
+ (uint32_t) 0;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE,
+ xdr_CREATE3args, (char *) &args,
+ xdr_CREATE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFS3ERR_EXIST) {
+ return(1);
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first create suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ }
+
+ file_ptr->state = Exists;
+ /*
+ * Directories are created as Empty_dir, when a file is created it
+ * becomes an Exists.
+ */
+ file_ptr->dir->state = Exists;
+
+ return(0);
+}
+
+/*
+ * Reliably set the size of a file in the current directory
+ */
+int
+lad_truncate(sfs_fh_type *file_ptr, int32_t size)
+{
+ SETATTR3args args;
+ SETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ /*
+ * This function presumes that the file name already exists
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, size);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /*
+ * set up the arguments
+ * Set the mode and times as well
+ */
+ (void) memmove((char *) &args.object, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.new_attributes.mode.set_it = TRUE;
+ args.new_attributes.mode.mode = (uint32_t) 0666;
+ args.new_attributes.uid.set_it = FALSE;
+ args.new_attributes.uid.uid = (uint32_t) -1;
+ args.new_attributes.gid.set_it = FALSE;
+ args.new_attributes.gid.gid = (uint32_t) -1;
+ args.new_attributes.size.set_it = TRUE;
+ args.new_attributes.size.size._p._u = 0;
+ args.new_attributes.size.size._p._l = size;
+ args.new_attributes.atime.set_it = TRUE;
+ args.new_attributes.atime.atime.seconds = Cur_time.esec;
+ args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.new_attributes.mtime.set_it = TRUE;
+ args.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+ args.guard.check = FALSE;
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR,
+ xdr_SETATTR3args, (char *) &args,
+ xdr_SETATTR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void)fprintf(stderr,
+ "lad_truncate(%ld) RPC call failed : %s\n",
+ size, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n",
+ size, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ (void) memmove(&file_ptr->attributes3,
+ &reply.resok.obj_wcc.after.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+
+ return(0);
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "RFS timeout");
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/* sfs_3_ops.c */
--- /dev/null
+#ifndef lint
+static char sfs_3_opsSid[] = "@(#)sfs_3_ops.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_3_ops.c ---------------------
+ *
+ * RPC routines to implement the NFS protocol.
+ *
+ *.Local Routines
+ * int op_null(void)
+ * int op_getattr(void)
+ * int op_setattr(int)
+ * int op_lookup(void)
+ * int op_access(void)
+ * int op_readlink(void)
+ * int op_read(int)
+ * int op_write(int, int, stable_how)
+ * int op_create(void)
+ * int op_mkdir(void);
+ * int op_symlink(void);
+ * int op_mknod(void);
+ * int op_remove(void);
+ * int op_rmdir(void);
+ * int op_rename(void);
+ * int op_link(void);
+ * int op_readdir(void);
+ * int op_readdirplus(void);
+ * int op_fsstat(void);
+ * int op_fsinfo(void);
+ * int op_pathconf(void);
+ * int op_commit(void);
+ *
+ *.Revision_History
+ * 30-Jun-94 ChakChung Ng Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * -------------------- Local NFS ops function --------------------
+ */
+static int op_null(void);
+static int op_getattr(void);
+static int op_setattr(int);
+static int op_lookup(void);
+static int op_access(void);
+static int op_readlink(void);
+static int op_read(int);
+static int op_write(int, int, stable_how);
+static int op_create(void);
+static int op_mkdir(void);
+static int op_symlink(void);
+static int op_mknod(void);
+static int op_remove(void);
+static int op_rmdir(void);
+static int op_rename(void);
+static int op_link(void);
+static int op_readdir(void);
+static int op_readdirplus(void);
+static int op_fsstat(void);
+static int op_fsinfo(void);
+static int op_pathconf(void);
+static int op_commit(void);
+static int op_nosys(void);
+static char *nfs3_strerror(int);
+
+
+/*
+ * -------------------- NFS ops vector --------------------
+ */
+/*
+ * per operation information
+ */
+static sfs_op_type nfsv3_Ops[] = {
+
+/* name mix function op call no req req req results */
+/* pcnt class targ call pcnt cnt targ */
+
+ { "null", 0, op_null, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "getattr", 11, op_getattr, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "setattr", 1, op_setattr, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "root", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "lookup", 27, op_lookup, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readlink", 7, op_readlink, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "read", 18, op_read, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "wrcache", 0, op_nosys, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "write", 9, op_write, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "create", 1, op_create, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "remove", 1, op_remove, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rename", 0, op_rename, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "link", 0, op_link, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "symlink", 0, op_symlink, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mkdir", 0, op_mkdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "rmdir", 0, op_rmdir, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdir", 2, op_readdir, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsstat", 1, op_fsstat, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "access", 7, op_access, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "commit", 5, op_commit, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "fsinfo", 1, op_fsinfo, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "mknod", 0, op_mknod, Write, 0, 0, 0.0, 0, 0, { 0, }},
+ { "pathconf", 0, op_pathconf, Lookup, 0, 0, 0.0, 0, 0, { 0, }},
+ { "readdirplus", 9, op_readdirplus, Read, 0, 0, 0.0, 0, 0, { 0, }},
+ { "TOTAL", 100, 0, Lookup, 0, 0, 0.0, 0, 0, { 0, }}
+};
+
+sfs_op_type *Ops;
+
+/*
+ * -------------------- RPC routines for NFS protocol --------------------
+ */
+
+void
+init_ops(void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/*
+ * The routines below attempt to do over-the-wire operations.
+ * Each op tries to cause one or more of a particular
+ * NFS operation to go over the wire. OPs return the success
+ * of their NFS call(s). Each OP records how many calls it
+ * actually made in global data.
+ *
+ * An array of file information is kept for files existing in
+ * the test directory. File handles, attributes, names, etc
+ * are stored in this array.
+ *
+ */
+
+/*
+ * Generic catch all for operations not covered by this protocol.
+ */
+static int
+op_nosys(void)
+{
+ Ops[TOTAL].results.bad_calls++;
+ return(0);
+}
+
+static int
+op_null(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[NULLCALL];
+ ret = 0;
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL,
+ xdr_void, (char *)0, xdr_void, (char *)0,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: null_op call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_null */
+
+
+static int
+op_getattr(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ GETATTR3args args; /* fh to do op on */
+ GETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[GETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR,
+ xdr_GETATTR3args, (char *) &args,
+ xdr_GETATTR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: getattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_getattr */
+
+
+/*
+ * perform an RPC setattr operation. If 'truncate_size' is non-negative,
+ * truncate the file to that size.
+ */
+static int
+op_setattr(
+ int truncate_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ SETATTR3args args;
+ SETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SETATTR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.new_attributes.mode.set_it = TRUE;
+ args.new_attributes.mode.mode = (uint32_t) 0666;
+ args.new_attributes.uid.set_it = FALSE;
+ args.new_attributes.uid.uid = (uint32_t) -1;
+ args.new_attributes.gid.set_it = FALSE;
+ args.new_attributes.gid.gid = (uint32_t) -1;
+ args.new_attributes.size.set_it = FALSE;
+ args.new_attributes.size.size._p._u = (uint32_t) ~0;
+ args.new_attributes.size.size._p._l = (uint32_t) -1;
+ args.new_attributes.atime.set_it = TRUE;
+ args.new_attributes.atime.atime.seconds = Cur_time.esec;
+ args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.new_attributes.mtime.set_it = TRUE;
+ args.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+ args.guard.check = FALSE;
+
+ /* handle file truncations */
+ if (truncate_size >= 0) {
+ args.new_attributes.size.set_it = TRUE;
+ args.new_attributes.size.size._p._u = (uint32_t) 0;
+ if (truncate_size > Cur_file_ptr->attributes3.size._p._l)
+ args.new_attributes.size.size._p._l = (uint32_t) 0;
+ else
+ args.new_attributes.size.size._p._l =
+ (uint32_t) Cur_file_ptr->attributes3.size._p._l -
+ truncate_size;
+ }
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR,
+ xdr_SETATTR3args, (char *) &args,
+ xdr_SETATTR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_wcc.after.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: setattr call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_setattr */
+
+
+static int
+op_lookup(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LOOKUP];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.what.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.what.name = Cur_filename;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.object,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ /* We do lookup Nonexistent and this is not an error */
+ if (reply.status != NFS3ERR_NOENT ||
+ Cur_file_ptr->state != Nonexistent) {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: lookup call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: lookup call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_lookup */
+
+
+static int
+op_access(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ ACCESS3args args;
+ ACCESS3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[ACCESS];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.access = ACCESS3_MODIFY;
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS,
+ xdr_ACCESS3args, (char *) &args,
+ xdr_ACCESS3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: access call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: access call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_access */
+
+
+static int
+op_readlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READLINK3args args; /* the args */
+ READLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN];
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ /*
+ * Note: this symlink may be bogus because SYMLINK does
+ * not return a symlink ... only a status. So unless we have
+ * done a LOOKUP on this guy, the symlink will probably be bad.
+ * If it is bad it shows up as a symlink error in the results.
+ */
+ (void) memmove((char *) &args.symlink,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.data = sym_data;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK,
+ xdr_READLINK3args, (char *) &args,
+ xdr_READLINK3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READLINK on %s returned %s\n",
+ sfs_Myname, Cur_filename, sym_data);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readlink */
+
+
+/*
+ * perform an RPC read operation of length 'xfer_size'
+ */
+static int
+op_read(
+ int xfer_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int cur_cnt;
+ int max_cnt; /* packet ctrs */
+ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
+ READ3args args;
+ READ3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int size;
+ int j;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[READ];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /*
+ * Don't allow a read of less than one block size
+ */
+ if (xfer_size < Bytes_per_block)
+ xfer_size = Bytes_per_block;
+
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ args.offset._p._u = 0;
+ if (Cur_file_ptr->attributes3.size._p._l > xfer_size)
+ args.offset._p._l = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes3.size._p._l - xfer_size)
+ / Bytes_per_block) + 1));
+ else
+ args.offset._p._l = 0;
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.data.data_len = 0;
+ reply.resok.data.data_val = buf;
+
+ /* first read the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block -1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ args.count = size;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "read: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READ,
+ xdr_READ3args, (char *) &args,
+ xdr_READ3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = (rpc_stat == RPC_SUCCESS && reply.status == NFS3_OK)
+ ? reply.resok.data.data_len : 0;
+ Dump_offset = args.offset._p._l;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ size = reply.resok.data.data_len;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READ %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ args.offset._p._l += size;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call NFS error %s on file %d\n",
+ sfs_Myname,
+ nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: read call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for reading max_cnt packets */
+ } /* for buffers and fragments */
+ return(ret);
+
+} /* op_read */
+
+char *
+init_write_buffer(
+ void)
+{
+ uint32_t *bp;
+ static uint32_t write_buf[DEFAULT_MAX_BUFSIZE / sizeof(uint32_t)];
+ uint32_t *be = write_buf + (sizeof(write_buf) /
+ sizeof(uint32_t));
+
+ if (write_buf[0] != (uint32_t)0xdeadbeef) {
+ for (bp = write_buf; bp < be; bp++)
+ *bp = (uint32_t)0xdeadbeef;
+ }
+ return (char *)write_buf;
+}
+
+
+/*
+ * Perform and RPC write operation of length 'xfer_size'. If 'append_flag'
+ * is true, then write the data to the end of the file.
+ *
+ * If the stab_flag is set to UNSTABLE we issue the requests and then
+ * issue a op_commit to sync the data.
+ */
+static int
+op_write(
+ int xfer_size,
+ int append_flag,
+ stable_how stab_flag)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ static char *buf = NULL; /* the data buffer */
+ unsigned int size; /* size of data write */
+ int cur_cnt; /* controls # NFS calls */
+ int max_cnt;
+ WRITE3args args;
+ WRITE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int j;
+ int ret; /* ret val == call success */
+
+ /*
+ * For now we treat DATA_SYNC to be the same as FILE_SYNC.
+ */
+ if (stab_flag == DATA_SYNC)
+ stab_flag = FILE_SYNC;
+
+ /*
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+ op_ptr = &Ops[WRITE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = 0;
+ if (append_flag == 1) {
+ args.offset._p._l = Cur_file_ptr->attributes3.size._p._l;
+ } else {
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (Cur_file_ptr->attributes3.size._p._l > xfer_size) {
+ args.offset._p._l = Bytes_per_block * (sfs_random() %
+ (((Cur_file_ptr->attributes3.size._p._l - xfer_size)
+ / Bytes_per_block) + 1));
+ } else
+ args.offset._p._l = 0;
+ }
+
+ /* stab_flag has to be set in op() in sfs_3_chd.c */
+ args.stable = stab_flag;
+
+ /* first write the whole buffers, then the fragment */
+ for (j = 0; j < 2; j++) {
+
+ if (j == 0) {
+ size = Bytes_per_block;
+ max_cnt = xfer_size / Bytes_per_block;
+ } else {
+ /* 1KB - (Kb_per_block - 1) KB fragment */
+ size = xfer_size % Bytes_per_block;
+ max_cnt = 1;
+ }
+ if (size == 0)
+ continue;
+
+ args.count = size;
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0) {
+ if ((op_ptr->results.good_calls + max_cnt)
+ > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+ }
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "write: %d buffers\n", max_cnt);
+ (void) fflush(stderr);
+ }
+
+ /* make the call(s) now */
+ for (cur_cnt = 0; cur_cnt < max_cnt; cur_cnt++) {
+
+ if (DEBUG_CHILD_RPC) {
+(void) fprintf(stderr, "%s: WRITE %s offset %u count %lu stable %d\n",
+sfs_Myname, Cur_filename, args.offset._p._l, args.count, args.stable);
+ (void) fflush(stderr);
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args,
+ xdr_WRITE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ /* capture count and offset for possible dump */
+ Dump_count = args.data.data_len;
+ Dump_offset = args.offset._p._l;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ args.offset._p._l += size;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: WRITE %s %d bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ } /* for writing max_cnt packets */
+ } /* for buffers and fragments */
+
+ /*
+ * If we have not gotten an error and we were asked for an async write
+ * send a commit operation.
+ */
+ if (ret && stab_flag != FILE_SYNC)
+ ret += op_commit();
+
+ return(ret);
+
+} /* op_write */
+
+
+static int
+op_create(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ CREATE3args args;
+ CREATE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[CREATE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666);
+ args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ args.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.atime.atime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.size.size._p._u = (uint32_t) 0;
+ args.how.createhow3_u.obj_attributes.size.size._p._l = (uint32_t) 0;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE,
+ xdr_CREATE3args, (char *) &args,
+ xdr_CREATE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: create call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: create call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_create */
+
+
+static int
+op_mkdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ MKDIR3args args;
+ MKDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[MKDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.attributes.mode.set_it = TRUE;
+ args.attributes.mode.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid.set_it = TRUE;
+ args.attributes.uid.uid = Cur_uid;
+ args.attributes.gid.set_it = TRUE;
+ args.attributes.gid.gid = Cur_gid;
+ args.attributes.size.set_it = TRUE;
+ args.attributes.size.size._p._u = 0;
+ args.attributes.size.size._p._l = 512;
+ args.attributes.atime.set_it = TRUE;
+ args.attributes.atime.atime.seconds = Cur_time.esec;
+ args.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.attributes.mtime.set_it = TRUE;
+ args.attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR,
+ xdr_MKDIR3args, (char *) &args,
+ xdr_MKDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Empty_dir;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: mkdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: mkdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_mkdir */
+
+
+static int
+op_symlink(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target file */
+ SYMLINK3args args;
+ SYMLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[SYMLINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(SYMLINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target_fileinfo_ptr->file_name);
+ args.symlink.symlink_attributes.size.set_it = TRUE;
+ args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0;
+ args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data);
+ args.symlink.symlink_data = sym_data;
+
+ args.symlink.symlink_attributes.mode.set_it = TRUE;
+ args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+ args.symlink.symlink_attributes.uid.set_it = TRUE;
+ args.symlink.symlink_attributes.uid.uid = Cur_uid;
+ args.symlink.symlink_attributes.gid.set_it = TRUE;
+ args.symlink.symlink_attributes.gid.gid = Cur_gid;
+ args.symlink.symlink_attributes.atime.set_it = TRUE;
+ args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.symlink.symlink_attributes.mtime.set_it = TRUE;
+ args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK,
+ xdr_SYMLINK3args, (char *) &args,
+ xdr_SYMLINK3res, (char *) &reply,
+ ((int)Current_test_phase < (int)Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ /*
+ * SYMLINK doesn't return a fh. If we try to access this symlink
+ * (eg, remove(), readlink()) before we do a lookup, we won't have
+ * a fh to use. So, we do a lookup call here. If it fails, we fill
+ * in what we can.
+ */
+ Cur_file_ptr->state = Exists;
+ if (op_lookup() == 0) {
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes3.type = NF3LNK;
+ Cur_file_ptr->attributes3.mode = (NFSMODE_LNK|0777);
+ Cur_file_ptr->attributes3.uid = Cur_uid;
+ Cur_file_ptr->attributes3.gid = Cur_gid;
+ Cur_file_ptr->attributes3.atime.seconds = Cur_time.esec;
+ Cur_file_ptr->attributes3.atime.nseconds =
+ Cur_time.usec * 1000;
+ Cur_file_ptr->attributes3.mtime =
+ Cur_file_ptr->attributes3.atime;
+ } else
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: symlink call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: symlink call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_symlink */
+
+
+static int
+op_mknod(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ MKNOD3args args;
+ MKNOD3res reply;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[MKNOD];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = Cur_filename;
+ args.what.type = NF3FIFO;
+ args.what.mknoddata3_u.pipe_attributes.mode.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mode.mode = (NFSMODE_FIFO | 0777);
+ args.what.mknoddata3_u.pipe_attributes.uid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.uid.uid = Cur_uid;
+ args.what.mknoddata3_u.pipe_attributes.gid.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.gid.gid = Cur_gid;
+ args.what.mknoddata3_u.pipe_attributes.size.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._u = (uint32_t) 0;
+ args.what.mknoddata3_u.pipe_attributes.size.size._p._l =
+ (uint32_t) 512;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.what.mknoddata3_u.pipe_attributes.atime.set_it = TRUE;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.what.mknoddata3_u.pipe_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD,
+ xdr_MKNOD3args, (char *) &args,
+ xdr_MKNOD3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: mknod call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: mknod call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_mknod */
+
+
+static int
+op_remove(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ REMOVE3args args;
+ REMOVE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[REMOVE];
+ ret = 0;
+
+ /* set up the arguments */
+ args.object.name = Cur_filename;
+ (void) memmove((char *) &args.object.dir,(char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE,
+ xdr_REMOVE3args, (char *) &args,
+ xdr_REMOVE3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: remove call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: remove call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_remove */
+
+
+static int
+op_rmdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ RMDIR3args args;
+ RMDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RMDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.object.name = Cur_file_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR,
+ xdr_RMDIR3args, (char *) &args,
+ xdr_RMDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rmdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rmdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rmdir */
+
+
+static int
+op_rename(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target name */
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[RENAME];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.from.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &args.to.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ target_fileinfo_ptr = randfh(RENAME, 0, 0, Nonexistent,
+ Sfs_non_io_file);
+
+ args.from.name = Cur_file_ptr->file_name;
+ (void) sprintf(target_fileinfo_ptr->file_name, Filespec,
+ target_fileinfo_ptr->unique_num);
+ args.to.name = target_fileinfo_ptr->file_name;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ target_fileinfo_ptr->state = Exists;
+ (void) memmove((char *) &target_fileinfo_ptr->fh3,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ target_fileinfo_ptr->attributes3 = Cur_file_ptr->attributes3;
+ target_fileinfo_ptr->size = fh_size(Cur_file_ptr);
+ Cur_file_ptr->state = Nonexistent;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: rename call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: rename call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_rename */
+
+
+static int
+op_link(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ sfs_fh_type *target_fileinfo_ptr; /* target */
+ LINK3args args;
+ LINK3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[LINK];
+ ret = 0;
+
+ /* set up the arguments */
+ target_fileinfo_ptr = randfh(LINK, 0, 0, Exists, Sfs_non_io_file);
+ (void) memmove((char *) &args.file, (char *) &target_fileinfo_ptr->fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &args.link.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.link.name = Cur_filename;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK,
+ xdr_LINK3args, (char *) &args,
+ xdr_LINK3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &target_fileinfo_ptr->fh3,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ target_fileinfo_ptr->attributes3.nlink++;
+ Cur_file_ptr->attributes3 = target_fileinfo_ptr->attributes3;
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: link call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: link call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_link */
+
+
+static int
+op_readdir(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READDIR3args args;
+ READDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int i;
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+ bool_t hit_eof;
+ /* array of entries */
+ entry3 entry_stream[SFS_MAXDIRENTS];
+ entry3 *entries; /* ptr to the dir entry */
+
+ op_ptr = &Ops[READDIR];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ args.count = DEFAULT_MAX_BUFSIZE;
+
+ /* Have lower layers fill in the data directly. */
+ (void) memset((char *) &reply, '\0', sizeof (reply));
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entry3) * SFS_MAXDIRENTS);
+ reply.resok.count = SFS_MAXDIRENTS;
+ reply.resok.reply.entries = entry_stream;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR,
+ xdr_READDIR3args, (char *) &args,
+ xdr_READDIR3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+
+ if (DEBUG_CHILD_RPC) {
+ hit_eof = reply.resok.reply.eof;
+ entries = reply.resok.reply.entries;
+ for (i = 0; i < reply.resok.count; i++) {
+ (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n",
+ sfs_Myname, hit_eof, entries[i].name);
+ }
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readdir */
+
+
+static int
+op_readdirplus(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ READDIRPLUS3args args;
+ READDIRPLUS3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int i;
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+ bool_t hit_eof;
+ /* array of entries */
+ entryplus3 entry_stream[SFS_MAXDIRENTS];
+ entryplus3 *entries;
+
+ op_ptr = &Ops[READDIRPLUS];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.dir, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.cookie._p._l = args.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) args.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ (void) memset((char *) entry_stream, '\0',
+ sizeof (entryplus3) * SFS_MAXDIRENTS);
+ args.dircount = DEFAULT_MAX_BUFSIZE;
+ args.maxcount = DEFAULT_MAX_BUFSIZE;
+
+ /* Have lower layers fill in the data directly. */
+ reply.resok.count = SFS_MAXDIRENTS;
+ reply.resok.reply.entries = entry_stream;
+
+ /* make the call now */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIRPLUS,
+ xdr_READDIRPLUS3args, (char *) &args,
+ xdr_READDIRPLUS3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+
+ if (DEBUG_CHILD_RPC) {
+ hit_eof = reply.resok.reply.eof;
+ entries = reply.resok.reply.entries;
+ for (i = 0; i < reply.resok.count; i++) {
+ (void) fprintf(stderr, "%s:READDIR (eof=%d) entry %s\n",
+ sfs_Myname, hit_eof, entries[i].name);
+ }
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: readdir call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_readdirplus */
+
+
+static int
+op_fsstat(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ FSSTAT3args args;
+ FSSTAT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[FSSTAT];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT,
+ xdr_FSSTAT3args, (char *) &args,
+ xdr_FSSTAT3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: fsstat call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_fsstat */
+
+
+static int
+op_fsinfo(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ FSINFO3args args;
+ FSINFO3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[FSINFO];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.fsroot, (char *) &Cur_file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO,
+ xdr_FSINFO3args, (char *) &args,
+ xdr_FSINFO3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: fsinfo call RPC error %d\n",
+ sfs_Myname, rpc_stat);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_fsinfo */
+
+
+static int
+op_pathconf(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ PATHCONF3args args;
+ PATHCONF3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[PATHCONF];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.object, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF,
+ xdr_PATHCONF3args, (char *) &args,
+ xdr_PATHCONF3args, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: pathconf call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ return(ret);
+
+} /* op_pathconf */
+
+
+static int
+op_commit(void)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int32_t size; /* size of data write */
+ COMMIT3args args;
+ COMMIT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ int ret; /* ret val == call success */
+
+ op_ptr = &Ops[COMMIT];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = Cur_file_ptr->attributes3.size._p._l;
+ size = args.count;
+
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT,
+ xdr_COMMIT3args, (char *) &args,
+ xdr_COMMIT3res, (char *) &reply,
+ (Current_test_phase < Warmup_phase)
+ ? Nfs_timers[Init]
+ : Nfs_timers[op_ptr->call_class]);
+ sfs_gettime(&stop);
+ Cur_time = stop;
+
+ if (rpc_stat == RPC_SUCCESS) {
+ if (reply.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ Cur_file_ptr->size = fh_size(Cur_file_ptr);
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: WRITE %s %ld bytes\n",
+ sfs_Myname, Cur_filename, size);
+ (void) fflush(stderr);
+ }
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr,
+ "%s: write call NFS error %s on file %d\n",
+ sfs_Myname, nfs3_strerror(reply.status),
+ Cur_file_ptr->unique_num);
+ }
+ }
+ sfs_elapsedtime(op_ptr, &start, &stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ } else {
+ if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: write call RPC error %d on file %d\n",
+ sfs_Myname, rpc_stat, Cur_file_ptr->unique_num);
+ }
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+
+ return(ret);
+
+} /* op_commit */
+
+
+#define LAD_RETRIABLE(stat) (((stat) == RPC_TIMEDOUT) || ((stat) == RPC_CANTDECODERES))
+
+/*
+ * Reliably lookup a file in the current directory
+ * Return:
+ * -1 RPC error
+ * 1 File doesn't exist
+ * 0 File exists
+ */
+int
+lad_lookup(sfs_fh_type *file_ptr, char *name)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_lookup: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.what.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_lookup(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status == NFS3ERR_NOENT) {
+ return(1);
+ }
+
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_lookup(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+
+ file_ptr->state = Exists;
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.object,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ return(0);
+}
+
+/*
+ * Reliably remove a file in the current directory
+ */
+int
+lad_remove(sfs_fh_type *file_ptr, char *name)
+{
+ REMOVE3args args;
+ REMOVE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does exist
+ */
+ if (file_ptr->attributes3.type == NF3DIR)
+ return (lad_rmdir(file_ptr, name));
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_remove: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.object.name = name;
+ (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3,
+ sizeof(nfs_fh3));
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE,
+ xdr_REMOVE3args, (char *) &args,
+ xdr_REMOVE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_remove(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_remove(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably remove a directory in the current directory
+ */
+int
+lad_rmdir(sfs_fh_type *file_ptr, char *name)
+{
+ RMDIR3args args;
+ RMDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does exist and is empty
+ */
+ if (file_ptr->attributes3.type != NF3DIR)
+ return (lad_remove(file_ptr, name));
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_rmdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ args.object.name = name;
+ (void) memmove((char *) &args.object.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR,
+ xdr_RMDIR3args, (char *) &args,
+ xdr_RMDIR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_rmdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_NOENT || !retried) {
+ (void) fprintf(stderr, "lad_rmdir(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ file_ptr->state = Nonexistent;
+
+ return(0);
+}
+
+/*
+ * Reliably create a symlink in the current directory
+ */
+int
+lad_symlink(sfs_fh_type *file_ptr, char *target, char *name)
+{
+ SYMLINK3args args;
+ SYMLINK3res reply; /* the reply */
+ char sym_data[NFS_MAXPATHLEN]; /* symlink data */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_symlink: %lx[%lx] %s -> %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name, target);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+
+ (void) strcpy(sym_data, "./");
+ (void) strcat(sym_data, target);
+ args.symlink.symlink_attributes.size.set_it = TRUE;
+ args.symlink.symlink_attributes.size.size._p._u = (uint32_t) 0;
+ args.symlink.symlink_attributes.size.size._p._l = strlen(sym_data);
+ args.symlink.symlink_data = sym_data;
+
+ args.symlink.symlink_attributes.mode.set_it = TRUE;
+ args.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+ args.symlink.symlink_attributes.uid.set_it = TRUE;
+ args.symlink.symlink_attributes.uid.uid = Cur_uid;
+ args.symlink.symlink_attributes.gid.set_it = TRUE;
+ args.symlink.symlink_attributes.gid.gid = Cur_gid;
+ args.symlink.symlink_attributes.atime.set_it = TRUE;
+ args.symlink.symlink_attributes.atime.atime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.symlink.symlink_attributes.mtime.set_it = TRUE;
+ args.symlink.symlink_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.symlink.symlink_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK,
+ xdr_SYMLINK3args, (char *) &args,
+ xdr_SYMLINK3res, (char *) &reply,
+ Nfs_timers[Init]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_symlink(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_symlink(%s, %s) NFS call failed : %s\n",
+ target, name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ }
+
+ /*
+ * SYMLINK may not return a fh. If we try to
+ * access this symlink (eg, remove(), readlink())
+ * before we do a lookup, we won't have a fh to use.
+ * So, we do a lookup call here.
+ * If it fails, we fill in what we can.
+ */
+ return (lad_lookup(file_ptr, name));
+}
+
+/*
+ * Reliably create a directory in the current directory
+ */
+int
+lad_mkdir(sfs_fh_type *file_ptr, char *name)
+{
+ MKDIR3args args;
+ MKDIR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_mkdir: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+ args.attributes.mode.set_it = TRUE;
+ args.attributes.mode.mode = (NFSMODE_DIR | 0777);
+ args.attributes.uid.set_it = TRUE;
+ args.attributes.uid.uid = Cur_uid;
+ args.attributes.gid.set_it = TRUE;
+ args.attributes.gid.gid = Cur_gid;
+ args.attributes.size.set_it = TRUE;
+ args.attributes.size.size._p._u = 0;
+ args.attributes.size.size._p._l = 512;
+ args.attributes.atime.set_it = TRUE;
+ args.attributes.atime.atime.seconds = Cur_time.esec;
+ args.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.attributes.mtime.set_it = TRUE;
+ args.attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR,
+ xdr_MKDIR3args, (char *) &args,
+ xdr_MKDIR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_mkdir(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFS3ERR_EXIST)
+ return(1);
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_mkdir(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first mkdir suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ }
+ file_ptr->state = Empty_dir;
+
+ return(0);
+}
+
+/*
+ * Reliably commit a file
+ */
+static int
+lad_commit(sfs_fh_type *file_ptr)
+{
+ COMMIT3args args;
+ COMMIT3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_commit: %lx[%lx]\n",
+ sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir);
+ (void) fflush(stderr);
+ }
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = args.offset._p._l = (uint32_t) 0;
+ args.count = file_ptr->attributes3.size._p._l;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT,
+ xdr_COMMIT3args, (char *) &args,
+ xdr_COMMIT3res, (char *) &reply,
+ Nfs_timers[Init]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_commit() RPC call failed : %s\n",
+ clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Reliably write a file in the current directory
+ */
+int
+lad_write(sfs_fh_type *file_ptr, int32_t offset, int32_t length)
+{
+ static char *buf = NULL; /* the data buffer */
+ int32_t size; /* size of data write */
+ int32_t cur_cnt;
+ WRITE3args args;
+ WRITE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int async = 1;
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_write: %lx[%lx] %ld %ld\n",
+ sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, offset, length);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * This function presumes that the file name does exist
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+ /*
+ * If a short file write don't bother with the commit, just write sync.
+ */
+ if (length <= Bytes_per_block)
+ async = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args.file, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.offset._p._u = 0;
+ args.offset._p._l = offset;
+ if (async)
+ args.stable = UNSTABLE;
+ else
+ args.stable = FILE_SYNC;
+
+ size = Bytes_per_block;
+ for (cur_cnt = 0; cur_cnt < length; cur_cnt += size) {
+ if ((cur_cnt + size) > length)
+ size = length - cur_cnt;
+
+ if (size == 0)
+ break;
+
+ args.count = size;
+ args.data.data_len = size;
+ args.data.data_val = buf;
+
+ /* make the call now */
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args,
+ xdr_WRITE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_write() RPC call failed : %s\n",
+ clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_write() NFS call failed : %s\n",
+ nfs3_strerror(reply.status));
+ return(-1);
+ }
+ file_ptr->state = Exists;
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.file_wcc.after.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+
+ args.offset._p._l += size;
+ }
+
+ if (async)
+ (void) lad_commit(file_ptr);
+ return(0);
+}
+
+/*
+ * Reliably create a file in the current directory
+ */
+int
+lad_create(sfs_fh_type *file_ptr, char *name)
+{
+ CREATE3args args;
+ CREATE3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ int retried = 0;
+
+ /*
+ * This function presumes that the file name does not already exist
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_create: %lx[%lx] %s\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, name);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /* set up the arguments */
+ (void) memmove((char *) &args.where.dir, (char *) &file_ptr->dir->fh3,
+ sizeof (nfs_fh3));
+ args.where.name = name;
+ args.how.mode = UNCHECKED;
+ args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mode.mode = (NFSMODE_REG | 0666);
+ args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ args.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ args.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ args.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ args.how.createhow3_u.obj_attributes.size.size._p._u =
+ (uint32_t) 0;
+ args.how.createhow3_u.obj_attributes.size.size._p._l =
+ (uint32_t) 0;
+
+ /* make the call now */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE,
+ xdr_CREATE3args, (char *) &args,
+ xdr_CREATE3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void) fprintf(stderr, "lad_create(%s) RPC call failed : %s\n",
+ name, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ retried++;
+ }
+
+ if (!retried && reply.status == NFS3ERR_EXIST) {
+ return(1);
+ }
+
+ if (reply.status != NFS3_OK) {
+ if (reply.status != NFS3ERR_EXIST || !retried) {
+ (void) fprintf(stderr, "lad_create(%s) NFS call failed : %s\n",
+ name, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ /*
+ * If the first create suceeded but the reply as dropped and
+ * was retransmitted, we still need to lookup the attributes
+ */
+ if (lad_lookup(file_ptr, name))
+ return (-1);
+ } else {
+ (void) memmove((char *) &file_ptr->fh3,
+ (char *) &reply.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(file_ptr->file_name, name);
+ (void) memmove((char *) &file_ptr->attributes3,
+ (char *) &reply.resok.obj_attributes.attr,
+ sizeof(file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+ }
+
+ file_ptr->state = Exists;
+ /*
+ * Directories are created as Empty_dir, when a file is created it
+ * becomes an Exists.
+ */
+ file_ptr->dir->state = Exists;
+
+ return(0);
+}
+
+/*
+ * Reliably set the size of a file in the current directory
+ */
+int
+lad_truncate(sfs_fh_type *file_ptr, int32_t size)
+{
+ SETATTR3args args;
+ SETATTR3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ /*
+ * This function presumes that the file name already exists
+ */
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s:lad_truncate: %lx[%lx] %ld\n", sfs_Myname,
+ (int32_t) file_ptr, (int32_t) file_ptr->dir, size);
+ (void) fflush(stderr);
+ }
+
+ /* CONSTCOND */
+ while (1) {
+ /*
+ * set up the arguments
+ * Set the mode and times as well
+ */
+ (void) memmove((char *) &args.object, (char *) &file_ptr->fh3,
+ sizeof (nfs_fh3));
+ args.new_attributes.mode.set_it = TRUE;
+ args.new_attributes.mode.mode = (uint32_t) 0666;
+ args.new_attributes.uid.set_it = FALSE;
+ args.new_attributes.uid.uid = (uint32_t) -1;
+ args.new_attributes.gid.set_it = FALSE;
+ args.new_attributes.gid.gid = (uint32_t) -1;
+ args.new_attributes.size.set_it = TRUE;
+ args.new_attributes.size.size._p._u = 0;
+ args.new_attributes.size.size._p._l = size;
+ args.new_attributes.atime.set_it = TRUE;
+ args.new_attributes.atime.atime.seconds = Cur_time.esec;
+ args.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ args.new_attributes.mtime.set_it = TRUE;
+ args.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+ args.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+ args.guard.check = FALSE;
+
+ /* make the call */
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR,
+ xdr_SETATTR3args, (char *) &args,
+ xdr_SETATTR3res, (char *) &reply,
+ Nfs_timers[Init]);
+
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ (void)fprintf(stderr,
+ "lad_truncate(%ld) RPC call failed : %s\n",
+ size, clnt_sperrno(rpc_stat));
+ }
+ if (!LAD_RETRIABLE(rpc_stat)) {
+ return(-1);
+ }
+ }
+
+ if (reply.status != NFS3_OK) {
+ (void) fprintf(stderr, "lad_truncate(%ld) NFS call failed : %s\n",
+ size, nfs3_strerror(reply.status));
+ return(-1);
+ }
+ (void) memmove(&file_ptr->attributes3,
+ &reply.resok.obj_wcc.after.attr,
+ sizeof (file_ptr->attributes3));
+ file_ptr->size = fh_size(file_ptr);
+
+ return(0);
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/* sfs_3_ops.c */
--- /dev/null
+#ifndef lint
+static char sfs_3_vldSid[] = "@(#)sfs_3_vld.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ------------------------------ sfs_3_vld.c ---------------------------
+ *
+ * Validation suite for sfs.
+ *
+ *.Exported_routines
+ * void Validate_ops(int, char **)
+ *
+ *.Local_routines
+ * void validate_init_rpc()
+ * void validate_creation(void)
+ * void validate_attributes(void)
+ * void validate_read_write(void)
+ * void validate_rename(void)
+ * int compare_sattr(char *, char *, sattr3 *, fattr3 *)
+ * int compare_fattr(char *, char *, fattr3 *, fattr3 *)
+ * uint16_t sum(unsigned char *, int)
+ * void validate_remove(void)
+ * void validate_cleanup(void)
+ * void validate_exit(void)
+ * void verror(int, ValMsgType, char *, ...)
+ *
+ *.Revision History
+ * 11-Jul-94 ChakChung Ng Created
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+
+extern struct hostent *Server_hostent;
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+
+/* forward definitions for local routines */
+/*
+ * validate options
+ * BATCH - do complete pass of validation, reporting errors if any
+ * VERBOSE - prints step-by-step validation actions being performed
+ * INTERACTIVE - VERBOSE and if any errors encountered, ask to continue
+ * validation or not.
+ */
+#define VAL_BATCH 1
+#define VAL_VERBOSE 2
+#define VAL_INTERACTIVE 3
+
+typedef enum {
+ I = 1,
+ W = 2,
+ E = 3
+} ValMsgType;
+
+#define NUMREGFILES 21
+#define NUMDIRS 10
+#define NUMLINKS 10
+#define NUMSYMLINKS 10
+#define NUMFILES NUMREGFILES + NUMDIRS + NUMLINKS + NUMSYMLINKS
+#define NUMFRAGS 8
+
+static void val_op_null(void);
+static void val_op_getattr(GETATTR3args *, GETATTR3res *);
+static void val_op_setattr(SETATTR3args *, SETATTR3res *);
+static void val_op_lookup(LOOKUP3args *, LOOKUP3res *);
+static void val_op_access(ACCESS3args *, ACCESS3res *);
+static void val_op_readlink(READLINK3args *, READLINK3res *);
+static void val_op_read(READ3args *, READ3res *);
+static void val_op_write(WRITE3args *, WRITE3res *);
+static void val_op_create(CREATE3args *, CREATE3res *);
+static void val_op_mkdir(MKDIR3args *, MKDIR3res *);
+static void val_op_symlink(SYMLINK3args *, SYMLINK3res *);
+static void val_op_mknod(MKNOD3args *, MKNOD3res *);
+static void val_op_remove(REMOVE3args *, REMOVE3res *);
+static void val_op_rmdir(RMDIR3args *, RMDIR3res *);
+static void val_op_rename(RENAME3args *, RENAME3res *);
+static void val_op_link(LINK3args *, LINK3res *);
+static void val_op_readdir(READDIR3args *, READDIR3res *);
+static void val_op_readdirplus(READDIRPLUS3args *, READDIRPLUS3res *);
+static void val_op_fsstat(FSSTAT3args *, FSSTAT3res *);
+static void val_op_fsinfo(FSINFO3args *, FSINFO3res *);
+static void val_op_pathconf(PATHCONF3args *, PATHCONF3res *);
+static void val_op_commit(COMMIT3args *, COMMIT3res *);
+
+static void validate_init_rpc(void);
+static void validate_exit(void);
+static void validate_creation(void);
+static void validate_attributes(void);
+static void validate_read_write(void);
+static void validate_rename(void);
+static void validate_remove(void);
+static void validate_cleanup(void);
+static int compare_sattr(char *, char *, sattr3 *, fattr3 *);
+static int compare_fattr(char *, char *, fattr3 *, fattr3 *);
+static uint16_t sum(unsigned char *, int);
+static void verror(int, ValMsgType, char *, ...);
+static void create_3tmp_handles(void);
+static void delete_3tmp_handles(void);
+
+/*
+ * ---------------------- Static Declarations ----------------------
+ */
+
+int Validate;
+
+static int Validate_errors = 0;
+static char Testdirname[SFS_MAXPATHLEN]; /* test dir component name */
+/*
+ * packed structure to keep track of file status
+ */
+struct sfs_fileinfo {
+ int file_found:1, /* file has been found */
+ file_is_dup:1, /* file has a duplicate */
+ pad:30; /* pad the rest */
+};
+
+typedef struct sfs_fileinfo sfs_fileinfo;
+/*
+ * This vector is used for readdirplus validation currently, but could be
+ * extended to keep track of other interesting pieces of information.
+ */
+static sfs_fileinfo check_files[NUMFILES];
+
+/*
+ * ---------------------- SFS Validation Suite ----------------------
+ */
+
+/*
+ * XXXXX Must make sure that we validate that all servers return back
+ * XXXXX All optional values
+ */
+void
+Validate_ops(
+ int argc,
+ char * argv[])
+{
+ char * valdir;
+ CLIENT * mount_client_ptr;
+ int i;
+
+ if (argc > 1) {
+ verror(VAL_BATCH, E, "Can only validate one directory at a time.\n");
+ exit(1);
+ }
+
+ Num_io_files = NUMFILES;
+ Cur_uid = Real_uid;
+ nfs_version = NFS_V3;
+
+ if (argc == 0)
+ valdir = ".";
+ else
+ valdir = argv++[0];
+
+ (void) sprintf(Testdirname, "%s/validatedir", valdir);
+
+ do {
+ verror(VAL_BATCH, I, "validating sfs on \"%s\" directory ...\n",
+ valdir);
+
+ init_fileinfo();
+ create_3tmp_handles();
+
+ /*
+ * need priv port to do following
+ */
+ mount_client_ptr = lad_getmnt_hand(valdir);
+ if (mount_client_ptr == NULL) {
+ exit(1);
+ }
+ validate_init_rpc();
+
+ /*
+ * should be all done doing priv port stuff
+ */
+ if (setuid(Real_uid) != 0) {
+ (void) fprintf(stderr,"%s: %s%s\n",
+ sfs_Myname, "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, valdir, mount_client_ptr);
+
+ /*
+ * initialize the check_file array
+ */
+ (void) memset((void *) check_files, '\0', sizeof(check_files));
+
+ verror(VAL_VERBOSE, I, "validating null operation ...\n");
+ val_op_null();
+
+ validate_creation();
+ validate_attributes();
+ validate_read_write();
+ validate_rename();
+ validate_remove();
+ argc--;
+ valdir = argv++[0];
+
+ /*
+ * Cleanup mount client handle
+ */
+ clnt_destroy(mount_client_ptr);
+
+ delete_3tmp_handles();
+ validate_cleanup();
+ } while (argc > 0);
+
+ validate_exit();
+
+} /* Validate_ops */
+
+
+/*
+ * allocate and initialize client handles
+ */
+static void
+validate_init_rpc(void)
+{
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) NFS_V3,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ verror(VAL_BATCH, E,
+ "portmap/nfsd server not responding\n");
+ exit(1);
+ }
+
+ NFS_client->cl_auth = authunix_create(lad_hostname, Real_uid,
+ Cur_gid, 0, NULL);
+} /* validate_init_rpc */
+
+
+static void
+validate_creation(void)
+{
+ int filenum;
+ int target_filenum;
+ CREATE3args argcr;
+ CREATE3res repcr;
+ MKDIR3args argmk;
+ MKDIR3res repmk;
+ LINK3args argln;
+ LINK3res repln;
+ SYMLINK3args argsl;
+ SYMLINK3res repsl;
+ LOOKUP3args arglp;
+ LOOKUP3res replp;
+ READLINK3args argrl;
+ READLINK3res reprl;
+ char sl_target_path[NFS_MAXPATHLEN];
+ char sym_data[NFS_MAXPATHLEN];
+
+ for (filenum=0; filenum < NUMFILES ; filenum++) {
+
+ Cur_file_ptr = &Io_files[filenum];
+ sfs_gettime(&Cur_time);
+
+ if (filenum < NUMREGFILES) {
+
+ (void) sprintf(Cur_filename, Filespec, filenum);
+
+ /* regular file creation */
+ (void) memmove((char *) &argcr.where.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argcr.where.name = Cur_filename;
+ argcr.how.mode = UNCHECKED;
+ argcr.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.mode.mode =
+ (NFSMODE_REG | 0666);
+ argcr.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.uid.uid = Cur_uid;
+ argcr.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.gid.gid = Cur_gid;
+ argcr.how.createhow3_u.obj_attributes.atime.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ argcr.how.createhow3_u.obj_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ argcr.how.createhow3_u.obj_attributes.mtime.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ argcr.how.createhow3_u.obj_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+ argcr.how.createhow3_u.obj_attributes.size.set_it = TRUE;
+ argcr.how.createhow3_u.obj_attributes.size.size._p._u =
+ (uint32_t) 0;
+ argcr.how.createhow3_u.obj_attributes.size.size._p._l =
+ (uint32_t) 0;
+
+ (void) memset((char *) &repcr.resok.obj.handle, '\0',
+ sizeof (nfs_fh3));
+ verror(VAL_VERBOSE, I, "validating create file %s ...\n",
+ Cur_filename);
+ val_op_create(&argcr, &repcr);
+
+ if (repcr.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &repcr.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &repcr.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ (void) compare_sattr(Ops[CREATE].name, Io_files[filenum].file_name,
+ &argcr.how.createhow3_u.obj_attributes,
+ &Cur_file_ptr->attributes3);
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ errno = (int)repcr.status;
+ verror(VAL_BATCH, E, "create %s failed: %m\n",
+ Cur_filename);
+ /*
+ * An error in file creation is fatal, because we use the
+ * created files to validate the other operations.
+ */
+ validate_exit();
+ }
+
+ } else if (filenum < NUMREGFILES + NUMDIRS) {
+
+ (void) sprintf(Cur_filename, Dirspec, filenum - NUMREGFILES);
+
+ /* directory creation */
+ (void) memmove((char *) &argmk.where.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argmk.where.name = Cur_filename;
+ argmk.attributes.mode.set_it = TRUE;
+ argmk.attributes.mode.mode = (NFSMODE_DIR | 0777);
+ argmk.attributes.uid.set_it = TRUE;
+ argmk.attributes.uid.uid = Cur_uid;
+ argmk.attributes.gid.set_it = TRUE;
+ argmk.attributes.gid.gid = Cur_gid;
+ argmk.attributes.size.set_it = FALSE;
+ argmk.attributes.size.size._p._u = (uint32_t) 0;
+ argmk.attributes.size.size._p._l = (uint32_t) 0;
+ argmk.attributes.atime.set_it = TRUE;
+ argmk.attributes.atime.atime.seconds = Cur_time.esec;
+ argmk.attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ argmk.attributes.mtime.set_it = TRUE;
+ argmk.attributes.mtime.mtime.seconds = Cur_time.esec;
+ argmk.attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+
+ (void) memset((char *) &repmk.resok.obj.handle, '\0', sizeof (nfs_fh3));
+ verror(VAL_VERBOSE, I, "validating mkdir %s ...\n", Cur_filename);
+ val_op_mkdir(&argmk, &repmk);
+
+ if (repmk.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &repmk.resok.obj.handle,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &repmk.resok.obj_attributes.attr,
+ sizeof(Cur_file_ptr->attributes3));
+ (void) compare_sattr(Ops[MKDIR].name, Io_files[filenum].file_name,
+ &argmk.attributes, &Cur_file_ptr->attributes3);
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "mkdir %s failed:%m\n", Cur_filename);
+ }
+
+ } else if (filenum < NUMREGFILES + NUMDIRS + NUMLINKS ) {
+
+ (void) sprintf(Cur_filename, Filespec, filenum - NUMDIRS);
+
+ /* hard link creation */
+ target_filenum = NUMFILES-NUMSYMLINKS-1-filenum;
+ (void) memmove((char *) &argln.file,
+ (char *) &Io_files[target_filenum].fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &argln.link.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argln.link.name = Cur_filename;
+
+ verror(VAL_VERBOSE, I, "validating link %s %s ...\n",
+ Io_files[target_filenum].file_name, Cur_filename);
+ val_op_link(&argln, &repln);
+
+ if (repln.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &Io_files[target_filenum].fh3,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ Cur_file_ptr->attributes3 = Io_files[target_filenum].attributes3;
+ Io_files[target_filenum].attributes3.nlink++;
+ Cur_file_ptr->attributes3.nlink++;
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "link %s failed: %m\n", Cur_filename);
+ exit(1);
+ }
+
+ } else {
+
+ (void) sprintf(Cur_filename, Symspec, filenum -
+ (NUMREGFILES + NUMDIRS + NUMLINKS));
+
+ /* symbolic link creation */
+ target_filenum = NUMFILES-1-filenum;
+ (void) memmove((char *) &argsl.where.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argsl.where.name = Cur_filename;
+ (void) sprintf(sl_target_path,
+ "./%s", Io_files[target_filenum].file_name);
+ argsl.symlink.symlink_attributes.size.set_it = TRUE;
+ argsl.symlink.symlink_attributes.size.size._p._u =
+ (uint32_t) 0;
+ argsl.symlink.symlink_attributes.size.size._p._l =
+ (uint32_t) strlen(sl_target_path);
+ argsl.symlink.symlink_data = sl_target_path;
+ argsl.symlink.symlink_attributes.mode.set_it = TRUE;
+ argsl.symlink.symlink_attributes.mode.mode = (NFSMODE_LNK | 0777);
+ argsl.symlink.symlink_attributes.uid.set_it = TRUE;
+ argsl.symlink.symlink_attributes.uid.uid = Cur_uid;
+ argsl.symlink.symlink_attributes.gid.set_it = TRUE;
+ argsl.symlink.symlink_attributes.gid.gid = Cur_gid;
+ argsl.symlink.symlink_attributes.atime.set_it = TRUE;
+ argsl.symlink.symlink_attributes.atime.atime.seconds =
+ Cur_time.esec;
+ argsl.symlink.symlink_attributes.atime.atime.nseconds =
+ Cur_time.usec * 1000;
+ argsl.symlink.symlink_attributes.mtime.set_it = TRUE;
+ argsl.symlink.symlink_attributes.mtime.mtime.seconds =
+ Cur_time.esec;
+ argsl.symlink.symlink_attributes.mtime.mtime.nseconds =
+ Cur_time.usec * 1000;
+
+ verror(VAL_VERBOSE, I, "validating symlink %s %s ...\n",
+ sl_target_path, Cur_filename);
+ val_op_symlink(&argsl, &repsl);
+
+ if (repsl.status == NFS3_OK) {
+ Cur_file_ptr->state = Exists;
+
+ /* do a lookup to get file handle and attributes */
+ (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ arglp.what.name = Cur_filename;
+ (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3));
+
+ val_op_lookup(&arglp, &replp);
+
+ if (replp.status == NFS3_OK) {
+ (void) memmove((char *) &Cur_file_ptr->fh3,
+ (char *) &replp.resok.object,
+ sizeof (nfs_fh3));
+ (void) strcpy(Cur_file_ptr->file_name, Cur_filename);
+ (void) memmove((char *) &Cur_file_ptr->attributes3,
+ (char *) &replp.resok.obj_attributes.attr,
+ sizeof (Cur_file_ptr->attributes3));
+ (void) compare_sattr(Ops[SYMLINK].name,
+ Io_files[filenum].file_name,
+ &argsl.symlink.symlink_attributes,
+ &Cur_file_ptr->attributes3);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+ Cur_filename);
+ continue;
+ }
+
+ /* validate readlink */
+ (void) memmove((char *) &argrl.symlink,
+ (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+ reprl.resok.data = sym_data;
+
+ verror(VAL_VERBOSE, I, "validating readlink %s ...\n",
+ Cur_filename);
+ val_op_readlink(&argrl, &reprl);
+
+ if (reprl.status == NFS3_OK) {
+ if (strcmp(sl_target_path, sym_data)) {
+ verror(VAL_BATCH, W,
+ "readlink %s error, result = %s, should be %s\n",
+ Cur_filename, reprl.resok.data,
+ sl_target_path);
+ }
+ } else {
+ verror(VAL_BATCH, W, "readlink %s failed:%m\n",
+ Cur_filename);
+ }
+
+ } else {
+ Cur_file_ptr->state = Nonexistent;
+ verror(VAL_BATCH, W, "symlink %s failed: %m\n", Cur_filename);
+ }
+ }
+ } /* end for each file */
+
+} /* validate_creation */
+
+
+static void
+validate_attributes(void)
+{
+ int filenum;
+ LOOKUP3args arglp;
+ LOOKUP3res replp;
+ GETATTR3args argga;
+ GETATTR3res repga;
+ SETATTR3args argsa;
+ SETATTR3res repsa;
+
+ /* validate fsstat */
+
+ /* validate lookup */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ arglp.what.name = Io_files[filenum].file_name;
+ (void) memset((char *) &replp.resok.object, '\0', sizeof (nfs_fh3));
+
+ verror(VAL_VERBOSE, I, "validating lookup %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_lookup(&arglp, &replp);
+
+ if (replp.status == NFS3_OK) {
+ if (memcmp((char *) &(Io_files[filenum].fh3),
+ (char *) &(replp.resok.object), sizeof (nfs_fh3))) {
+ verror(VAL_BATCH, W, "lookup %s: file handle mismatch\n",
+ Io_files[filenum].file_name);
+ exit(1);
+ }
+ (void) compare_fattr(Ops[LOOKUP].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3,
+ &replp.resok.obj_attributes.attr);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed:%m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+
+ /* validate getattr */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ (void) memmove((char *) &argga.object,
+ (char *) &Io_files[filenum].fh3,
+ sizeof (nfs_fh3));
+
+ verror(VAL_VERBOSE, I, "validating getattr %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_getattr(&argga, &repga);
+
+ if (repga.status == NFS3_OK) {
+ (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3,
+ &repga.resok.obj_attributes);
+ } else {
+ verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+
+ /*validate setattr */
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ sfs_gettime(&Cur_time);
+ (void) memmove((char *) &argsa.object,
+ (char *) &Io_files[filenum].fh3,
+ sizeof (nfs_fh3));
+ argsa.new_attributes.mode.set_it = TRUE;
+ if (filenum >= NUMREGFILES && filenum < NUMREGFILES + NUMDIRS)
+ argsa.new_attributes.mode.mode = 0777;
+ else
+ argsa.new_attributes.mode.mode = 0666;
+ argsa.new_attributes.uid.set_it = FALSE;
+ argsa.new_attributes.uid.uid = 0;
+ argsa.new_attributes.gid.set_it = FALSE;
+ argsa.new_attributes.gid.gid = 0;
+ argsa.new_attributes.size.set_it = FALSE;
+ argsa.new_attributes.size.size._p._u = 0;
+ argsa.new_attributes.size.size._p._l = 0;
+ argsa.new_attributes.atime.set_it = TRUE;
+ argsa.new_attributes.atime.atime.seconds = Cur_time.esec;
+ argsa.new_attributes.atime.atime.nseconds = Cur_time.usec * 1000;
+ argsa.new_attributes.mtime.set_it = TRUE;
+ argsa.new_attributes.mtime.mtime.seconds = Cur_time.esec;
+ argsa.new_attributes.mtime.mtime.nseconds = Cur_time.usec * 1000;
+ argsa.guard.check = FALSE;
+
+ verror(VAL_VERBOSE, I, "validating setattr %s ...\n",
+ Io_files[filenum].file_name);
+ val_op_setattr(&argsa, &repsa);
+
+ if (repsa.status == NFS3_OK) {
+ (void) memmove((char *) &Io_files[filenum].attributes3,
+ (char *) &repsa.resok.obj_wcc.after.attr,
+ sizeof (Io_files[filenum].attributes3));
+ (void) compare_sattr(Ops[SETATTR].name, Io_files[filenum].file_name,
+ &argsa.new_attributes, &repsa.resok.obj_wcc.after.attr);
+
+ (void) memmove((char *) &argga.object,
+ (char *) &Io_files[filenum].fh3,
+ sizeof (nfs_fh3));
+
+ val_op_getattr(&argga, &repga);
+
+ if (repga.status == NFS3_OK) {
+ (void) compare_fattr(Ops[GETATTR].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3,
+ &repga.resok.obj_attributes);
+ } else {
+ verror(VAL_BATCH, W, "getattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ } else {
+ verror(VAL_BATCH, W, "setattr %s failed: %m\n",
+ Io_files[filenum].file_name);
+ exit(1);
+ }
+ }
+
+} /* validate_attributes */
+
+#define WAITFOREOF 1
+/*
+ * need to check if readdirplus returns a reasonable amount of data.
+ */
+static void
+val_rddirplus_retsize(uint32_t dircount, uint32_t maxcount,
+ READDIRPLUS3resok *rp)
+{
+ entryplus3 *esp;
+ static int eof_wait = 0;
+ int i;
+ int size = 0;
+ int tsize = 0;
+ int msize = 0;
+ double mpcnt = 0;
+
+ esp = rp->reply.entries;
+
+ for (i = 0; i < rp->count; i++) {
+ size += sizeof(esp[i].fileid);
+ size += strlen(esp[i].name) * sizeof(char);
+ size += sizeof(esp[i].cookie);
+ tsize += sizeof(esp[i].name_attributes);
+ tsize += sizeof(esp[i].name_handle.handle_follows);
+ tsize += esp[i].name_handle.handle.fh3_length * sizeof(char);
+ }
+
+ msize = size + tsize;
+ mpcnt = (double) msize / (double) maxcount * 100;
+
+ if (rp->reply.eof) {
+ verror(VAL_VERBOSE, I, "readdirplus on %s returned EOF.\n"
+ "\treceived %d bytes of directory information and %d bytes including\n"
+ "\tpost op attributes and filehandle.\n",
+ Testdirname, size, msize);
+ } else if (mpcnt < 80) {
+ eof_wait++;
+ if (eof_wait > WAITFOREOF) {
+ verror(VAL_BATCH, E,
+ "readdirplus on %s did not return a reasonable amount of data.\n"
+ "\treceived %d bytes. should receive close to %d bytes.\n",
+ Testdirname, msize, maxcount);
+ } else {
+ verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n"
+ "\treceived %d bytes of directory information and %d bytes including\n"
+ "\tpost op attributes and filehandle.\n",
+ Testdirname, size, msize);
+ }
+ } else {
+ verror(VAL_VERBOSE, I, "readdirplus on %s did not return EOF.\n"
+ "\treceived %d bytes of directory information and %d bytes including\n"
+ "\tpost op attributes and filehandle.\n",
+ Testdirname, size, msize);
+ }
+}
+
+static void
+validate_read_write(void)
+{
+ struct {
+ uint16_t sum; /* checksum of data */
+ uint16_t len; /* length of len and data */
+ char data[DEFAULT_MAX_BUFSIZE - 2 * sizeof(uint16_t)];
+ } block;
+ WRITE3args argwr;
+ WRITE3res repwr;
+ READ3args argrd;
+ READ3res reprd;
+ READDIR3args argdr;
+ READDIR3res repdr;
+ READDIRPLUS3args argdrp;
+ READDIRPLUS3res repdrp;
+ int maxblks;
+ int maxfiles;
+ int i;
+ int numfiles;
+ int filenum;
+ int blocknum;
+ entry3 entry_stream[SFS_MAXDIRENTS];
+ entryplus3 entryplus_stream[SFS_MAXDIRENTS];
+
+ /* validate write */
+
+ /* get the maximum number of blocks sfs will write */
+ maxblks = Io_dist_ptr->max_bufs;
+ maxfiles = maxblks > NUMREGFILES ? NUMREGFILES : maxblks;
+
+ /* write maxblks - filenum + 1 blocks to each regular file */
+ argwr.offset._p._u = argwr.offset._p._l = (uint32_t) 0;
+ argwr.stable = FILE_SYNC;
+ argwr.data.data_val = (char *)█
+
+ for (blocknum = 0; blocknum <= maxblks ; blocknum++) {
+
+ for (i=0; i < sizeof(block.data); i++)
+ block.data[i] = (char)blocknum;
+
+ for (filenum=0; filenum < maxfiles; filenum++) {
+ /* Write fewer blocks to files with higher numbers. */
+ if (blocknum > (maxblks - filenum))
+ break;
+
+ /* set the length field */
+ if (blocknum == (maxblks - filenum)) {
+ block.len = ((maxfiles - filenum) *
+ (Bytes_per_block/Kb_per_block)) - (sizeof(block.len) +
+ sizeof(block.sum));
+ /*
+ * XXX - write kludge.
+ *
+ * Writes must be less than 8K in
+ * size or else the checksum will incur a buffer overflow
+ */
+ block.len = (block.len % DEFAULT_MAX_BUFSIZE) -
+ (sizeof(block.len) + sizeof(block.sum));
+ } else {
+ block.len = Bytes_per_block - (sizeof(block.len)
+ + sizeof(block.sum));
+ }
+ block.sum = sum((unsigned char *) &block.len,
+ (int)(block.len + sizeof(block.len)));
+
+ (void) memmove((char *) &argwr.file,
+ (char *) &Io_files[filenum].fh3,
+ sizeof (nfs_fh3));
+ argwr.data.data_len = block.len +
+ sizeof(block.len) + sizeof(block.sum);
+ argwr.count = argwr.data.data_len;
+
+ verror(VAL_VERBOSE, I,
+ "validating write %d bytes @ offset %lu to %s ...\n",
+ argwr.data.data_len, argwr.offset._p._l,
+ Io_files[filenum].file_name);
+
+ val_op_write(&argwr, &repwr);
+
+ if (repwr.status == NFS3_OK) {
+ (void) compare_fattr(Ops[WRITE].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3,
+ &repwr.resok.file_wcc.after.attr);
+ Io_files[filenum].attributes3 = repwr.resok.file_wcc.after.attr;
+ /*
+ * XXX-bookeeping kludge.
+ *
+ * Need to update hardlink attributes, so readdirplus vali-
+ * dation doesn't barf. This is necessary because setattr was
+ * validated on all the test files and the attributes in
+ * Io_files[] were updated accordingly. Since the write
+ * op has been validated on just the regular files, we have to
+ * make sure that the corresponding indexes in Io_files[] that
+ * point to the hard links reflect the current file attributes.
+ */
+ if (filenum < NUMLINKS) {
+ Io_files[NUMREGFILES+NUMDIRS+NUMLINKS-1-filenum].attributes3 = Io_files[filenum].attributes3;
+ }
+
+ } else {
+ verror(VAL_BATCH, W, "write %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+ }
+ argwr.offset._p._l += Bytes_per_block;
+ }
+
+ /* validate read */
+
+ for (filenum = 0; filenum < maxfiles; filenum++) {
+ (void) memmove((char *) &argrd.file,
+ (char *) &Io_files[filenum].fh3,
+ sizeof (nfs_fh3));
+ argrd.offset._p._u = argrd.offset._p._l = (uint32_t) 0;
+ argrd.count = 0;
+ reprd.resok.data.data_len = 0;
+ maxblks = Io_files[filenum].attributes3.size._p._l / Bytes_per_block;
+ for (blocknum = 0; blocknum <= maxblks; blocknum ++) {
+
+ if (argrd.count != reprd.resok.data.data_len) {
+ argrd.count -= reprd.resok.data.data_len;
+ reprd.resok.data.data_val = (char *)&block +
+ reprd.resok.data.data_len;
+ blocknum--;
+ } else {
+ if (blocknum < maxblks)
+ argrd.count = Bytes_per_block;
+ else
+ argrd.count = (maxfiles - filenum)
+ * (Bytes_per_block/Kb_per_block);
+ reprd.resok.data.data_val = (char *)█
+ }
+
+ verror(VAL_VERBOSE, I,
+ "validating read %lu bytes @ offset %lu from %s ...\n",
+ argrd.count, argrd.offset._p._l,
+ Io_files[filenum].file_name);
+
+ val_op_read(&argrd, &reprd);
+
+ if (reprd.status == NFS3_OK) {
+ (void) compare_fattr(Ops[READ].name, Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3,
+ &reprd.resok.file_attributes.attr);
+ Io_files[filenum].attributes3 = reprd.resok.file_attributes.attr;
+ argrd.offset._p._l += reprd.resok.data.data_len;
+ } else {
+ verror(VAL_BATCH, W, "read %s failed: %m\n",
+ Io_files[filenum].file_name);
+ }
+
+ if ((argrd.count ==
+ (block.sum != sum((unsigned char *) &block.len,
+ (int)(block.len + sizeof(block.len)))))) {
+ verror(VAL_BATCH, W, "read %s checksum mismatch\n",
+ Io_files[filenum].file_name);
+ }
+ }
+ }
+
+ /* validate readdir */
+ (void) memmove((char *) &argdr.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argdr.cookie._p._l = argdr.cookie._p._u = (uint32_t) 0;
+ (void) memset((char *) argdr.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ argdr.count = DEFAULT_MAX_BUFSIZE;
+
+ do {
+ (void) memset((char *) entry_stream, '\0', sizeof (entry3) * SFS_MAXDIRENTS);
+ (void) memset((char *) &repdr, '\0', sizeof(repdr));
+ repdr.resok.count = SFS_MAXDIRENTS;
+ repdr.resok.reply.entries = entry_stream;
+ verror(VAL_VERBOSE, I, "validating readdir on %s ...\n", Testdirname);
+ val_op_readdir(&argdr, &repdr);
+
+
+ if (repdr.status == NFS3_OK) {
+ for (i = 0; i < repdr.resok.count; i++) {
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ if (!strcmp(entry_stream[i].name, Io_files[filenum].file_name)) { if (entry_stream[i].fileid._p._l !=
+ Io_files[filenum].attributes3.fileid._p._l) {
+ verror(VAL_BATCH, E,
+ "readdir %s error: file %s fileid mismatch\n",
+ Testdirname, entry_stream[i].name);
+ verror(VAL_BATCH, W,
+ " fileid: got = %lu, original = %lu\n",
+ entry_stream[i].fileid._p._l,
+ Io_files[filenum].attributes3.fileid._p._l);
+ }
+ /*
+ * mark file as either found or dup
+ */
+ if (check_files[filenum].file_found &&
+ !check_files[filenum].file_is_dup)
+ check_files[filenum].file_is_dup = 1;
+ else
+ check_files[filenum].file_found = 1;
+ break;
+ }
+ }
+ }
+ } else {
+ verror(VAL_BATCH, W, "readdir %s failed: %m\n", Testdirname);
+ }
+
+ argdr.cookie = entry_stream[repdr.resok.count-1].cookie;
+
+ } while (repdr.resok.reply.eof == 0);
+
+ /*
+ * check if any known files have not been found
+ */
+ for (i = 0; i < NUMFILES; i++) {
+ if (!check_files[i].file_found)
+ verror(VAL_BATCH, E,
+ "readdir %s error: file %s not found\n",
+ Testdirname, Io_files[i].file_name);
+ else {
+ if (check_files[i].file_is_dup)
+ verror(VAL_BATCH, E,
+ "readdir %s error: file %s returned more than once\n",
+ Testdirname, Io_files[i].file_name);
+ }
+ }
+
+ /* validate readdirplus */
+ (void) memset((void *) check_files, '\0', sizeof(check_files));
+
+ argdrp.cookie._p._l = argdrp.cookie._p._u = (uint32_t) 0;
+ (void) memmove((char *) &argdrp.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ (void) memset((char *) argdrp.cookieverf, '\0', NFS3_COOKIEVERFSIZE);
+ /*
+ * We validate readdirplus with dircount and maxcount both set at 8K.
+ * This is not the most efficient way, but this is how readdirplus is
+ * called in SFS. With the numbers set as such, maxcount becomes the
+ * bottleneck and we will not get 8K worth of directory info. What we
+ * should get is 8K worth which includes directory info plus post_op_
+ * attributes and filehandle.
+ */
+ argdrp.dircount = DEFAULT_MAX_BUFSIZE;
+ argdrp.maxcount = DEFAULT_MAX_BUFSIZE;
+ do {
+ (void) memset((char *) entryplus_stream, '\0',
+ sizeof (entryplus_stream));
+
+ (void) memset((char *) &repdrp, '\0', sizeof(repdrp));
+ repdrp.resok.count = SFS_MAXDIRENTS;
+ repdrp.resok.reply.entries = entryplus_stream;
+
+ verror(VAL_VERBOSE, I, "validating readdirplus on %s ...\n",
+ Testdirname);
+ val_op_readdirplus(&argdrp, &repdrp);
+
+ if (repdrp.status == NFS3_OK) {
+ verror(VAL_VERBOSE, I, "readdirplus found %d entries in %s...\n",
+ repdrp.resok.count, Testdirname);
+ val_rddirplus_retsize(argdrp.dircount, argdrp.maxcount,
+ &repdrp.resok);
+ for (i = 0; i < repdrp.resok.count; i++) {
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+ if (!strcmp(entryplus_stream[i].name,
+ Io_files[filenum].file_name)) {
+ if (entryplus_stream[i].fileid._p._l !=
+ Io_files[filenum].attributes3.fileid._p._l) {
+ verror(VAL_BATCH, E,
+ "readdirplus %s error: file %s fileid mismatch\n",
+ Testdirname, entryplus_stream[i].name);
+ verror(VAL_BATCH, W,
+ " fileid: got = %lu, original = %lu\n",
+ entryplus_stream[i].fileid._p._l,
+ Io_files[filenum].attributes3.fileid._p._l);
+ }
+
+ /*
+ * mark file as either found or dup
+ */
+ if (check_files[filenum].file_found &&
+ !check_files[filenum].file_is_dup)
+ check_files[filenum].file_is_dup = 1;
+ else
+ check_files[filenum].file_found = 1;
+
+ /*
+ * check to make sure post op attributes and
+ * file handle are returned.
+ */
+ if (!entryplus_stream[i].name_attributes.attributes)
+ verror(VAL_BATCH, E,
+ "readdirplus %s warning: did not receive post op attributes for file %s\n\n",
+ Testdirname, entryplus_stream[i].name);
+ else
+ (void) compare_fattr(Ops[READDIRPLUS].name,
+ Io_files[filenum].file_name,
+ &Io_files[filenum].attributes3, &entryplus_stream[i].name_attributes.attr);
+
+ if (!entryplus_stream[i].name_handle.handle_follows)
+ verror(VAL_BATCH, E,
+ "readdirplus %s warning: did not receive file handle for file %s\n\n",
+ Testdirname, entryplus_stream[i].name);
+ else
+ if (memcmp((void *) &Io_files[filenum].fh3.fh3_u.data,
+ (void *) &entryplus_stream[i].name_handle.handle.fh3_u.data, Io_files[filenum].fh3.fh3_length) != 0)
+ verror(VAL_BATCH, E,
+ "readdirplus %s error: file %s, filehandles do not match\n\n",
+ Testdirname, entryplus_stream[i].name);
+ break;
+ }
+ }
+ }
+ } else {
+ verror(VAL_BATCH, W, "readdirplus %s failed: %m\n", Testdirname);
+ }
+ argdrp.cookie = entryplus_stream[repdrp.resok.count-1].cookie;
+
+ } while (repdrp.resok.reply.eof == 0);
+ /*
+ * check if any known files have not been found
+ */
+ for (i = 0; i < NUMFILES; i++) {
+ if (!check_files[i].file_found)
+ verror(VAL_BATCH, E,
+ "readdirplus %s error: file %s not found\n",
+ Testdirname, Io_files[i].file_name);
+ else {
+ if (check_files[i].file_is_dup)
+ verror(VAL_BATCH, E,
+ "readdirplus %s error: file %s returned more than once\n",
+ Testdirname, Io_files[i].file_name);
+ }
+ }
+} /* validate_read_write */
+
+
+static void
+validate_rename(void)
+{
+ RENAME3args argrn;
+ RENAME3res reprn;
+ int filenum;
+ char newname[SFS_MAXNAMLEN];
+ int rncount = 0;
+
+ (void) memmove((char *) &argrn.from.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ (void) memmove((char *) &argrn.to.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+
+ for (filenum=0; filenum < NUMFILES; filenum++) {
+ if (Io_files[filenum].state != Exists)
+ continue;
+
+ rncount++;
+ (void) sprintf(newname, "n%s", Io_files[filenum].file_name);
+ argrn.from.name = Io_files[filenum].file_name;
+ argrn.to.name = newname;
+
+ verror(VAL_VERBOSE, I, "validating rename %s %s ...\n",
+ argrn.from.name, argrn.to.name);
+
+ val_op_rename(&argrn, &reprn);
+
+ if (reprn.status == NFS3_OK) {
+ (void) strcpy(Io_files[filenum].file_name, newname);
+ } else {
+ verror(VAL_BATCH, W, "rename %s to %s failed: %m\n",
+ Io_files[filenum].file_name, newname);
+ }
+
+ }
+
+ if (!rncount) {
+ verror(VAL_BATCH, E, "validate_rename: no files renamed\n");
+ verror(VAL_BATCH, W, " due to previous operation error\n");
+ }
+
+} /* validate_rename */
+
+
+static int
+compare_fattr(
+ char * op,
+ char * fname,
+ fattr3 * attr1,
+ fattr3 * attr2)
+{
+ int ret = TRUE;
+ int prev_warn = FALSE; /* -1 info warning */
+ int flag_error = FALSE;
+
+ if (attr1->type != attr2->type) {
+ if (attr1->type == 0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " type: current = %d, previous = %d\n",
+ attr2->type, attr1->type);
+ attr1->type = attr2->type;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " type: current = %d, previous = %d\n",
+ attr2->type, attr1->type);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if ((attr1->mode & NFSMODE_MASK) != (attr2->mode & NFSMODE_MASK)) {
+ if (attr1->mode == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " mode: current = %7lo, previous = %7lo\n",
+ attr2->mode, attr1->mode);
+ attr1->mode = attr2->mode;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " mode: current = %7lo, previous = %7lo\n",
+ attr2->mode, attr1->mode);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->nlink != attr2->nlink) {
+ if (attr1->nlink == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " nlink: current = %lu, previous = %lu\n",
+ attr2->nlink, attr1->nlink);
+ ret = FALSE;
+ attr1->nlink = attr2->nlink;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " nlink: current = %lu, previous = %lu\n",
+ attr2->nlink, attr1->nlink);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+
+ /*
+ * Check for user "nobody", UID -2, which may be untranslated from
+ * sixteen-bit two's complement.
+ */
+ if (attr1->uid != attr2->uid && !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+ attr2->uid == 65534) && attr1->uid ==0)) {
+ if (attr1->uid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " uid: current = %lu, previous = %lu\n",
+ attr2->uid, attr1->uid);
+ attr1->uid = attr2->uid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " uid: current = %lu, previous = %lu\n",
+ attr2->uid, attr1->uid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->gid != attr2->gid && attr2->gid != 0) {
+/*
+ if (attr1->gid != attr2->gid) {
+*/
+ if (attr1->gid == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " gid: current = %lu, previous = %lu\n",
+ attr2->gid, attr1->gid);
+ attr1->gid = attr2->gid;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " gid: current = %lu, previous = %lu\n",
+ attr2->gid, attr1->gid);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->size._p._l != attr2->size._p._l) {
+ if (strcmp(op, Ops[WRITE].name)) {
+ if (attr1->size._p._l == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " size: current = %lu, previous = %lu\n",
+ attr2->size._p._l, attr1->size._p._l);
+ attr1->size._p._l = attr2->size._p._l;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " size: current = %lu, previous = %lu\n",
+ attr2->size._p._l, attr1->size._p._l);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+ }
+
+ /* compare rdev only if type == NFCHR or NFBLK */
+ if ((attr1->type == NF3CHR || attr1->type == NF3BLK) &&
+ (attr1->rdev.specdata1 != attr2->rdev.specdata1 ||
+ attr1->rdev.specdata2 != attr2->rdev.specdata2)) {
+ if (attr1->rdev.specdata1 == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " rdev: current = %lu %lu, previous = %lu %lu\n",
+ attr2->rdev.specdata1, attr2->rdev.specdata2,
+ attr1->rdev.specdata1, attr1->rdev.specdata2);
+ attr1->rdev.specdata1 = attr2->rdev.specdata1;
+ attr1->rdev.specdata2 = attr2->rdev.specdata2;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " rdev: current = %lu %lu, previous = %lu %lu\n",
+ attr2->rdev.specdata1, attr2->rdev.specdata2,
+ attr1->rdev.specdata1, attr1->rdev.specdata2);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->fsid._p._l != attr2->fsid._p._l) {
+ if (attr1->fsid._p._l == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I, " fsid: current = %lu, previous = %lu\n",
+ attr2->fsid._p._l, attr1->fsid._p._l);
+ attr1->fsid._p._l = attr2->fsid._p._l;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " fsid: current = %lu, previous = %lu\n",
+ attr2->fsid._p._l, attr1->fsid._p._l);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (attr1->fileid._p._l != attr2->fileid._p._l) {
+ if (attr1->fileid._p._l == (unsigned int)0xFFFFFFFF) {
+ prev_warn = TRUE;
+ if (ret) {
+ verror(VAL_BATCH, I, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, I,
+ " fileid: current = %lu, previous = %lu\n",
+ attr2->fileid._p._l, attr1->fileid._p._l);
+ attr1->fileid._p._l = attr2->fileid._p._l;
+ ret = FALSE;
+ }
+ else {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E,
+ " fileid: current = %lu, previous = %lu\n",
+ attr2->fileid._p._l, attr1->fileid._p._l);
+ ret = FALSE;
+ flag_error = TRUE;
+ }
+ }
+
+ if (prev_warn) {
+ verror(VAL_BATCH, I,
+ "\n Warning: the previous value of a field is -1,\n");
+ verror(VAL_BATCH, I,
+ " this resulted from an unused field returned by\n");
+ verror(VAL_BATCH, I,
+ " the previous operation on this file/directory.\n");
+ verror(VAL_BATCH, I,
+ " The current value is now stored for future comparison\n\n");
+ }
+
+ if (flag_error)
+ verror(VAL_BATCH, W,"\n");
+
+ return(flag_error);
+
+} /* ckompare_fattr */
+
+
+uint32_t sattr_types[8] = {
+ 0000000, /* NF3NON */
+ 0100000, /* NF3REG */
+ 0040000, /* NF3DIR */
+ 0060000, /* NF3BLK */
+ 0020000, /* NF3CHR */
+ 0120000, /* NF3LNK */
+ 0140000, /* NF3SOCK */
+ 0010000 }; /* NF3FIFO */
+static int
+compare_sattr(
+ char * op,
+ char * fname,
+ sattr3 * attr1,
+ fattr3 * attr2)
+{
+ int ret = TRUE;
+ char msg[80];
+
+ msg[0] = '\0';
+
+#ifdef notyet
+ if (attr1->mode.mode.set_it == TRUE &&
+ (((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[attr2->type]) &&
+ ((attr1->mode.mode & NFSTYPE_FMT) != sattr_types[0]) ||
+ ((attr1->mode.mode & NFSMODE_FMT) != (attr2->mode & NFSMODE_FMT)))) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+
+ }
+ verror(VAL_BATCH, E, " mode: returned = %7o, specified = %7o\n",
+ attr1->mode.mode, attr2->mode);
+ ret = FALSE;
+ }
+#endif
+
+ /*
+ * Check for user "nobody", UID -2, which may be untranslated from
+ * sixteen-bit two's complement.
+ */
+ if (attr1->uid.set_it == TRUE && attr1->uid.uid != attr2->uid &&
+ !((attr2->uid == (unsigned int)0xFFFFFFFE ||
+ attr2->uid == 65534) &&
+ attr1->uid.uid == 0)) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ if (attr1->uid.uid == 0)
+ (void) strcat(msg," (is root UID mapped to other UID?)");
+ verror(VAL_BATCH, E, " uid: returned = %lu, specified = %lu %s\n",
+ attr2->uid, attr1->uid.uid, msg);
+ ret = FALSE;
+ }
+
+ if (attr1->gid.set_it == TRUE && attr1->gid.gid != attr2->gid &&
+ attr2->gid != 0) {
+/*
+ if (attr1->gid.set_it == TRUE && attr1->gid != attr2->gid) {
+*/
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " gid: returned = %lu, specified = %lu\n",
+ attr2->gid, attr1->gid.gid);
+ ret = FALSE;
+ }
+
+ if (attr1->size.set_it == TRUE &&
+ attr1->size.size._p._l != attr2->size._p._l) {
+ if (ret) {
+ verror(VAL_BATCH, E, "%s: %s attribute mismatch\n",
+ fname, op);
+ }
+ verror(VAL_BATCH, E, " size: returned = %lu, specified = %lu\n",
+ attr2->size._p._l, attr1->size.size._p._l);
+ ret = FALSE;
+ }
+
+ if (!ret)
+ verror(VAL_BATCH, W,"\n");
+
+ return(ret);
+
+} /* compare_sattr */
+
+
+/*
+ * Return the BSD checksum of buf[0..len-1]
+ */
+static uint16_t
+sum(
+ unsigned char * buf,
+ int len)
+{
+ uint16_t cksum;
+
+ cksum = 0;
+ for (; len--; buf++) {
+ if (cksum & 01)
+ cksum = (cksum >> 1) + 0x8000;
+ else
+ cksum >>= 1;
+ cksum += (uint16_t) *buf;
+ cksum &= 0xFFFF;
+ }
+ return(cksum);
+
+} /* sum */
+
+
+static void
+validate_remove(void)
+{
+ REMOVE3args argrm;
+ REMOVE3res reprm;
+ RMDIR3args argrd;
+ RMDIR3res reprd;
+ LOOKUP3args arglp;
+ LOOKUP3res replp;
+ nfsstat3 reply;
+ int filenum;
+ char * op;
+
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if (Io_files[filenum].state != Exists)
+ continue;
+
+ if (Io_files[filenum].attributes3.type == NF3DIR) {
+ op = Ops[RMDIR].name;
+ verror(VAL_VERBOSE, I, "validating rmdir %s ...\n",
+ Io_files[filenum].file_name);
+
+ (void) memmove((char *) &argrd.object.dir,
+ (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argrd.object.name = Io_files[filenum].file_name;
+ val_op_rmdir(&argrd, &reprd);
+ reply = reprd.status;
+ } else {
+ op = Ops[REMOVE].name;
+ verror(VAL_VERBOSE, I, "validating remove %s ...\n",
+ Io_files[filenum].file_name);
+
+ (void) memmove((char *) &argrm.object.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ argrm.object.name = Io_files[filenum].file_name;
+ val_op_remove(&argrm, &reprm);
+ reply = reprm.status;
+ }
+
+ if (reply == NFS3_OK) {
+ /* make sure the file is removed from the directory */
+ (void) memmove((char *) &arglp.what.dir, (char *) &Export_dir.fh3,
+ sizeof (nfs_fh3));
+ arglp.what.name = Io_files[filenum].file_name;
+ val_op_lookup(&arglp, &replp);
+
+ if (replp.status == NFS3ERR_NOENT) {
+ Io_files[filenum].state = Nonexistent;
+ } else if (replp.status == NFS3_OK) {
+ verror(VAL_BATCH, W, "%s %s: file not removed\n",
+ op, Io_files[filenum].file_name);
+ } else {
+ verror(VAL_BATCH, W, "lookup %s failed: %m\n",
+ Io_files[filenum].file_name);
+
+ }
+ } else {
+ verror(VAL_BATCH, W, "%s %s failed: %m\n", op,
+ Io_files[filenum].file_name);
+ }
+
+ }
+
+} /* validate_remove */
+
+
+static void
+validate_cleanup(void)
+{
+ free(Io_files);
+ free(Non_io_files);
+ free(Dirs);
+ free(Symlinks);
+ clnt_destroy(NFS_client);
+} /* validate_cleanup */
+
+
+static void
+validate_exit(void)
+{
+ if (!Validate_errors) {
+ verror(VAL_BATCH, I, "validation completed successfully.\n");
+ exit(0);
+ } else {
+ verror(VAL_BATCH, I, "validation terminated with errors\n");
+ exit(1);
+ }
+
+} /* validate_exit */
+
+
+/* PRINTFLIKE3 */
+static void
+verror(
+ int opt,
+ ValMsgType msgtype,
+ char * fmt,
+ ...)
+{
+ va_list ap;
+ char buf[1024];
+ char * bp = buf;
+ char * fp;
+ char * sp;
+ int repeat;
+
+ va_start(ap, fmt);
+
+ /*
+ * Expand the "%m" format character into the descriptive string
+ * for the current value of errno. Printf handles the other "%"
+ * formatting characters.
+ */
+ if (Validate >= opt) {
+ for (fp = fmt; *fp; fp++) {
+ if (*fp == '%' && fp[1] == 'm') {
+ if ((sp = strerror(errno)) == NULL) {
+ (void) sprintf(bp, "unknown error %d", errno);
+ } else {
+ (void) strcpy(bp, sp);
+ }
+ bp = buf + strlen(buf);
+ fp++;
+ } else {
+ *bp++ = *fp;
+ }
+ }
+ *bp = '\0';
+ (void) vfprintf(stderr, buf, ap);
+ }
+ va_end(ap);
+
+ if (msgtype != I)
+ Validate_errors++;
+
+ if (msgtype == W && Validate == VAL_INTERACTIVE) {
+ repeat = 1;
+ while (repeat) {
+ char ans[80];
+
+ (void) fprintf(stderr, "continue? (y or n) ");
+ if (!fgets(ans,80,stdin)) {
+ (void) fprintf(stderr, "\n");
+ continue;
+ }
+ if (ans[0] == 'n' || ans[0] == 'N') {
+ validate_exit();
+ exit(1);
+ } else if (ans[0] == 'y' || ans[0] == 'Y') {
+ repeat = 0;
+ break;
+ }
+ }
+ }
+
+} /* verror */
+
+
+static void
+val_op_null(void)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_NULL,
+ xdr_void, (char *)0, xdr_void, (char *)0,
+ Nfs_timers[Ops[NULLCALL].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "null");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_getattr(GETATTR3args *args, GETATTR3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_GETATTR,
+ xdr_GETATTR3args, (char *)args, xdr_GETATTR3res, (char *)reply,
+ Nfs_timers[Ops[GETATTR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "getattr");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_setattr(SETATTR3args *args, SETATTR3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SETATTR,
+ xdr_SETATTR3args, (char *)args, xdr_SETATTR3res, (char *)reply,
+ Nfs_timers[Ops[SETATTR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "setattr");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_lookup(LOOKUP3args *args, LOOKUP3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *)args, xdr_LOOKUP3res, (char *)reply,
+ Nfs_timers[Ops[LOOKUP].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "lookup");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_access(ACCESS3args *args, ACCESS3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_ACCESS,
+ xdr_ACCESS3args, (char *)args, xdr_ACCESS3res, (char *)reply,
+ Nfs_timers[Ops[ACCESS].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "access");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_readlink(READLINK3args *args, READLINK3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READLINK,
+ xdr_READLINK3args, (char *)args, xdr_READLINK3res, (char *)reply,
+ Nfs_timers[Ops[READLINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "readlink");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_read(READ3args *args, READ3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READ,
+ xdr_READ3args, (char *)args, xdr_READ3res, (char *)reply,
+ Nfs_timers[Ops[READ].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "read");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_write(WRITE3args *args, WRITE3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *)args, xdr_WRITE3res, (char *)reply,
+ Nfs_timers[Ops[WRITE].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "write");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_create(CREATE3args *args, CREATE3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_CREATE,
+ xdr_CREATE3args, (char *)args, xdr_CREATE3res, (char *)reply,
+ Nfs_timers[Ops[CREATE].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "create");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_mkdir(MKDIR3args *args, MKDIR3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKDIR,
+ xdr_MKDIR3args, (char *)args, xdr_MKDIR3res, (char *)reply,
+ Nfs_timers[Ops[MKDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "mkdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_symlink(SYMLINK3args *args, SYMLINK3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_SYMLINK,
+ xdr_SYMLINK3args, (char *)args, xdr_SYMLINK3res, (char *)reply,
+ Nfs_timers[Ops[SYMLINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "symlink");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_mknod(MKNOD3args *args, MKNOD3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_MKNOD,
+ xdr_MKNOD3args, (char *)args, xdr_MKNOD3res, (char *)reply,
+ Nfs_timers[Ops[MKNOD].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "mknod");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_remove(REMOVE3args *args, REMOVE3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_REMOVE,
+ xdr_REMOVE3args, (char *)args, xdr_REMOVE3res, (char *)reply,
+ Nfs_timers[Ops[REMOVE].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "remove");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_rmdir(RMDIR3args *args, RMDIR3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RMDIR,
+ xdr_RMDIR3args, (char *)args, xdr_RMDIR3res, (char *)reply,
+ Nfs_timers[Ops[RMDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "rmdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_rename(RENAME3args *args, RENAME3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *)args, xdr_RENAME3res, (char *)reply,
+ Nfs_timers[Ops[RENAME].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "rename");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_link(LINK3args *args, LINK3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LINK,
+ xdr_LINK3args, (char *)args, xdr_LINK3res, (char *)reply,
+ Nfs_timers[Ops[LINK].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client,
+ "link");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_readdir(READDIR3args *args, READDIR3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_READDIR,
+ xdr_READDIR3args, (char *)args, xdr_READDIR3res, (char *)reply,
+ Nfs_timers[Ops[READDIR].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "readdir");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client,
+ NFSPROC3_READDIRPLUS, xdr_READDIRPLUS3args, (char *)args,
+ xdr_READDIRPLUS3res, (char *)reply,
+ Nfs_timers[Ops[READDIRPLUS].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client,
+ "readdirplus");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_fsstat(FSSTAT3args *args, FSSTAT3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSSTAT,
+ xdr_FSSTAT3args, (char *)args, xdr_FSSTAT3res, (char *)reply,
+ Nfs_timers[Ops[FSSTAT].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "fsstat");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_fsinfo(FSINFO3args *args, FSINFO3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_FSINFO,
+ xdr_FSINFO3args, (char *)args, xdr_FSINFO3res, (char *)reply,
+ Nfs_timers[Ops[FSINFO].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "fsinfo");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_pathconf(PATHCONF3args *args, PATHCONF3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_PATHCONF,
+ xdr_PATHCONF3args, (char *)args, xdr_PATHCONF3res, (char *)reply,
+ Nfs_timers[Ops[PATHCONF].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "pathconf");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+val_op_commit(COMMIT3args *args, COMMIT3res *reply)
+{
+ int rpc_stat;
+
+ /* CONSTCOND */
+ while (1) {
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_COMMIT,
+ xdr_COMMIT3args, (char *)args, xdr_COMMIT3res, (char *)reply,
+ Nfs_timers[Ops[COMMIT].call_class]);
+ if (rpc_stat == RPC_SUCCESS)
+ break;
+ if (rpc_stat != RPC_TIMEDOUT) {
+ clnt_perror(NFS_client, "commit");
+ Validate_errors++;
+ validate_exit();
+ }
+ }
+}
+
+static void
+create_3tmp_handles(void)
+{
+ int filenum;
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if(Io_files[filenum].fh_data == (sfs_fh_data *)0)
+ {
+ Io_files[filenum].fh_data =
+ calloc(1,sizeof(sfs_fh_data));
+ Io_files[filenum].attributes2.type = NFNON;
+ Io_files[filenum].attributes3.type = NF3NON;
+ }
+ }
+}
+
+static void
+delete_3tmp_handles(void)
+{
+ int filenum;
+ for (filenum = 0; filenum < NUMFILES; filenum++) {
+
+ if(Io_files[filenum].fh_data != (sfs_fh_data *)0)
+ {
+ free(Io_files[filenum].fh_data);
+ Io_files[filenum].fh_data= (sfs_fh_data *)0;
+ }
+ }
+}
+/* sfs_3_vld.c */
--- /dev/null
+#ifndef lint
+static char sfs_3_xdrSid[] = "@(#)sfs_3_xdr.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * -------------------------- sfs_3_xdr.c --------------------------
+ *
+ * XDR routines for the nfs protocol.
+ *
+ *.Exported_routines
+ * xdr_GETATTR3args(XDR *, GETATTR3args *)
+ * xdr_GETATTR3res(XDR *, GETATTR3res *)
+ * xdr_SETATTR3args(XDR *, SETATTR3args *)
+ * xdr_SETATTR3res(XDR *, SETATTR3res *)
+ * xdr_LOOKUP3args(XDR *, LOOKUP3args *)
+ * xdr_LOOKUP3res(XDR *, LOOKUP3res *)
+ * xdr_ACCESS3args(XDR *, ACCESS3args *)
+ * xdr_ACCESS3res(XDR *, ACCESS3res *)
+ * xdr_READLINK3args(XDR *, READLINK3args *)
+ * xdr_READLINK3res(XDR *, READLINK3res *)
+ * xdr_READ3args(XDR *, READ3args *)
+ * xdr_READ3res(XDR *, READ3res *)
+ * xdr_WRITE3args(XDR *, WRITE3args *)
+ * xdr_WRITE3res(XDR *, WRITE3res *)
+ * xdr_CREATE3args(XDR *, CREATE3args *)
+ * xdr_CREATE3res(XDR *, CREATE3res *)
+ * xdr_MKDIR3args(XDR *, MKDIR3args *)
+ * xdr_MKDIR3res(XDR *, MKDIR3res *)
+ * xdr_SYMLINK3args(XDR *, SYMLINK3args *)
+ * xdr_SYMLINK3res(XDR *, SYMLINK3res *)
+ * xdr_MKNOD3args(XDR *, MKNOD3args *)
+ * xdr_MKNOD3res(XDR *, MKNOD3res *)
+ * xdr_REMOVE3args(XDR *, REMOVE3args *)
+ * xdr_REMOVE3res(XDR *, REMOVE3res *)
+ * xdr_RMDIR3args(XDR *, RMDIR3args *)
+ * xdr_RMDIR3res(XDR *, RMDIR3res *)
+ * xdr_RENAME3args(XDR *, RENAME3args *)
+ * xdr_RENAME3res(XDR *, RENAME3res *)
+ * xdr_LINK3args(XDR *, LINK3args *)
+ * xdr_LINK3res(XDR *, LINK3res *)
+ * xdr_READDIR3args(XDR *, READDIR3args *)
+ * xdr_READDIR3res(XDR *, READDIR3res *)
+ * xdr_READDIRPLUS3args(XDR *, READDIRPLUS3args *)
+ * xdr_READDIRPLUS3res(XDR *, READDIRPLUS3res *)
+ * xdr_FSSTAT3args(XDR *, FSSTAT3args *)
+ * xdr_FSSTAT3res(XDR *, FSSTAT3res *)
+ * xdr_FSINFO3args(XDR *, FSINFO3args *)
+ * xdr_FSINFO3res(XDR *, FSINFO3res *)
+ * xdr_PATHCONF3args(XDR *, PATHCONF3args *)
+ * xdr_PATHCONF3res(XDR *, PATHCONF3res *)
+ * xdr_COMMIT3args(XDR *, COMMIT3args *)
+ * xdr_COMMIT3res(XDR *, COMMIT3res *)
+ * xdr_mntres3(XDR *, mountres3 *)
+ *
+ *.Local_routines
+ * xdr_string3(XDR *, char **, unsigned int)
+ * xdr_filename3(XDR *, filename3 *)
+ * xdr_nfspath3(XDR *, nfspath3 *)
+ * xdr_nfs_uint64_t(XDR *, nfs_uint64_t *)
+ * xdr_cookieverf3(XDR *, cookieverf3)
+ * xdr_createverf3(XDR *, createverf3)
+ * xdr_writeverf3(XDR *, writeverf3)
+ * xdr_nfs_fh3(XDR *, nfs_fh3 *)
+ * xdr_diropargs3(XDR *, diropargs3 *)
+ * xdr_nfstime3(XDR *, nfstime3 *)
+ * xdr_specdata3(XDR *, specdata3 *)
+ * xdr_nfsstat3(XDR *, nfsstat3 *)
+ * xdr_ftype3(XDR *, ftype3 *)
+ * xdr_fattr3(XDR *, fattr3 *)
+ * xdr_post_op_attr(XDR *, post_op_attr *)
+ * xdr_wcc_attr(XDR *, wcc_attr *)
+ * xdr_pre_op_attr(XDR *, pre_op_attr *)
+ * xdr_wcc_data(XDR *, wcc_data *)
+ * xdr_post_op_fh3(XDR *, post_op_fh3 *)
+ * xdr_time_how(XDR *, time_how *)
+ * xdr_set_mode3(XDR *, set_mode3 *)
+ * xdr_set_uid3(XDR *, set_uid3 *)
+ * xdr_set_gid3(XDR *, set_gid3 *)
+ * xdr_set_size3(XDR *, set_size3 *)
+ * xdr_set_atime(XDR *, set_atime *)
+ * xdr_set_mtime(XDR *, set_mtime *)
+ * xdr_sattr3(XDR *, sattr3 *)
+ * xdr_GETATTR3resok(XDR *, GETATTR3resok *)
+ * xdr_sattrguard3(XDR *, sattrguard3 *)
+ * xdr_SETATTR3resok(XDR *, SETATTR3resok *)
+ * xdr_SETATTR3resfail(XDR *, SETATTR3resfail *)
+ * xdr_LOOKUP3resok(XDR *, LOOKUP3resok *)
+ * xdr_LOOKUP3resfail(XDR *, LOOKUP3resfail *)
+ * xdr_ACCESS3resok(XDR *, ACCESS3resok *)
+ * xdr_ACCESS3resfail(XDR *, ACCESS3resfail *)
+ * xdr_READLINK3resok(XDR *, READLINK3resok *)
+ * xdr_READLINK3resfail(XDR *, READLINK3resfail *)
+ * xdr_READ3resok(XDR *, READ3resok *)
+ * xdr_READ3resfail(XDR *, READ3resfail *)
+ * xdr_stable_how(XDR *, stable_how *)
+ * xdr_WRITE3resok(XDR *, WRITE3resok *)
+ * xdr_WRITE3resfail(XDR *, WRITE3resfail *)
+ * xdr_createmode3(XDR *, createmode3 *)
+ * xdr_createhow3(XDR *, createhow3 *)
+ * xdr_CREATE3resok(XDR *, CREATE3resok *)
+ * xdr_CREATE3resfail(XDR *, CREATE3resfail *)
+ * xdr_MKDIR3resok(XDR *, MKDIR3resok *)
+ * xdr_MKDIR3resfail(XDR *, MKDIR3resfail *)
+ * xdr_symlinkdata3(XDR *, symlinkdata3 *)
+ * xdr_SYMLINK3resok(XDR *, SYMLINK3resok *)
+ * xdr_SYMLINK3resfail(XDR *, SYMLINK3resfail *)
+ * xdr_devicedata3(XDR *, devicedata3 *)
+ * xdr_mknoddata3(XDR *, mknoddata3 *)
+ * xdr_MKNOD3resok(XDR *, MKNOD3resok *)
+ * xdr_MKNOD3resfail(XDR *, MKNOD3resfail *)
+ * xdr_REMOVE3resok(XDR *, REMOVE3resok *)
+ * xdr_REMOVE3resfail(XDR *, REMOVE3resfail *)
+ * xdr_RMDIR3resok(XDR *, RMDIR3resok *)
+ * xdr_RMDIR3resfail(XDR *, RMDIR3resfail *)
+ * xdr_RENAME3resok(XDR *, RENAME3resok *)
+ * xdr_RENAME3resfail(XDR *, RENAME3resfail *)
+ * xdr_LINK3resok(XDR *, LINK3resok *)
+ * xdr_LINK3resfail(XDR *, LINK3resfail *)
+ * xdr_getdirlist(XDR *, READDIR3resok *)
+ * xdr_READDIR3resok(XDR *, READDIR3resok *)
+ * xdr_READDIR3resfail(XDR *, READDIR3resfail *)
+ * xdr_getdirpluslist(XDR *, READDIRPLUS3resok *)
+ * xdr_READDIRPLUS3resok(XDR *, READDIRPLUS3resok *)
+ * xdr_READDIRPLUS3resfail(XDR *, READDIRPLUS3resfail *)
+ * xdr_FSSTAT3resok(XDR *, FSSTAT3resok *)
+ * xdr_FSSTAT3resfail(XDR *, FSSTAT3resfail *)
+ * xdr_FSINFO3resok(XDR *, FSINFO3resok *)
+ * xdr_FSINFO3resfail(XDR *, FSINFO3resfail *)
+ * xdr_PATHCONF3resok(XDR *, PATHCONF3resok *)
+ * xdr_PATHCONF3resfail(XDR *, PATHCONF3resfail *)
+ * xdr_COMMIT3resok(XDR *, COMMIT3resok *)
+ * xdr_COMMIT3resfail(XDR *, COMMIT3resfail *)
+ *
+ *.Revision_History
+ * 28-Jun-94 ChakChung Ng Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * ----------------------- SFS XDR Routines -------------------------
+ */
+
+/*
+ * xdr_string3 deals with "C strings" - arrays of bytes that are terminated by
+ * a NULL character. The parameter cpp references a pointer to storage. If the
+ * pointer is null, then necessary storage is allocated. The last parameter is
+ * the max allowed length of the string as allowed by the system. The NFS
+ * Version 3 protocol does not place limits on strings, but the implementation
+ * needs to place a reasonable limit to avoid problems.
+ */
+
+static bool_t
+xdr_string3(
+ XDR * xdrs,
+ char ** cpp,
+ unsigned int maxsize)
+{
+ char *sp;
+ unsigned int size, nodesize;
+
+ /*
+ * first deal with the length since xdr strings are counted-strings
+ */
+ sp = *cpp;
+ switch (xdrs->x_op) {
+ case XDR_FREE:
+ if (sp == NULL || sp == nfs3nametoolong)
+ return(TRUE); /* already free */
+ /* FALLTHROUGH */
+ case XDR_ENCODE:
+ size = strlen(sp);
+ break;
+ }
+
+ if (!xdr_u_int(xdrs, &size))
+ return(FALSE);
+
+ /*
+ * now deal with the actual bytes
+ */
+ switch (xdrs->x_op) {
+ case XDR_DECODE:
+ if (size > maxsize) {
+ int xskp = (((((int)size) + 3) / 4) * 4);
+
+ *cpp = nfs3nametoolong;
+ if ((xdrs->x_handy -= xskp) < 0)
+ return(FALSE);
+ xdrs->x_private += xskp;
+ return(TRUE);
+ }
+ nodesize = size + 1;
+ if (nodesize == 0)
+ return(TRUE);
+ if (sp == NULL) {
+ sp = (char *)malloc(nodesize);
+ *cpp = sp;
+ if (sp == NULL)
+ return(FALSE);
+ }
+ sp[size] = 0; /* 0 through size-1 are original string */
+ /* FALLTHROUGH */
+ case XDR_ENCODE:
+ return(xdr_opaque(xdrs, sp, size));
+ case XDR_FREE:
+ nodesize = size + 1;
+ mem_free((caddr_t)sp, nodesize);
+ *cpp = NULL;
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+static bool_t
+xdr_filename3(
+ XDR * xdrs,
+ filename3 * objp)
+{
+
+ return(xdr_string3(xdrs, objp, NFS_MAXNAMLEN));
+}
+
+static bool_t
+xdr_nfspath3(
+ XDR * xdrs,
+ nfspath3 * objp)
+{
+
+ return(xdr_string3(xdrs, objp, NFS_MAXPATHLEN));
+}
+
+static bool_t
+xdr_nfs_uint64_t(
+ XDR *xdrs,
+ nfs_uint64_t * objp)
+{
+ return(xdr_int(xdrs, (int *)&objp->_p._u) &&
+ xdr_int(xdrs, (int *)&objp->_p._l));
+}
+
+static bool_t
+xdr_cookieverf3(
+ XDR * xdrs,
+ cookieverf3 objp)
+{
+ return(xdr_opaque(xdrs, objp, NFS3_COOKIEVERFSIZE));
+}
+
+static bool_t
+xdr_createverf3(
+ XDR * xdrs,
+ createverf3 objp)
+{
+ return(xdr_opaque(xdrs, objp, NFS3_CREATEVERFSIZE));
+}
+
+static bool_t
+xdr_writeverf3(
+ XDR * xdrs,
+ writeverf3 objp)
+{
+ return(xdr_opaque(xdrs, objp, NFS3_WRITEVERFSIZE));
+}
+
+static bool_t
+xdr_nfs_fh3(
+ XDR * xdrs,
+ nfs_fh3 * objp)
+{
+ if (!xdr_u_int(xdrs, &objp->fh3_length))
+ return(FALSE);
+
+ if (objp->fh3_length > NFS3_FHSIZE)
+ return(FALSE);
+ if (xdrs->x_op == XDR_DECODE || xdrs->x_op == XDR_ENCODE)
+ return(xdr_opaque(xdrs, objp->fh3_u.data, objp->fh3_length));
+
+ if (xdrs->x_op == XDR_FREE)
+ return(TRUE);
+
+ return(FALSE);
+}
+
+static bool_t
+xdr_diropargs3(
+ XDR * xdrs,
+ diropargs3 * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->dir))
+ return(xdr_filename3(xdrs, &objp->name));
+ return(FALSE);
+}
+
+static bool_t
+xdr_nfstime3(
+ XDR * xdrs,
+ nfstime3 * objp)
+{
+ if (xdr_uint32_t(xdrs, &objp->seconds))
+ return(xdr_uint32_t(xdrs, &objp->nseconds));
+ return(FALSE);
+}
+
+static bool_t
+xdr_specdata3(
+ XDR * xdrs,
+ specdata3 * objp)
+{
+ if (xdr_uint32_t(xdrs, &objp->specdata1))
+ return(xdr_uint32_t(xdrs, &objp->specdata2));
+ return(FALSE);
+}
+
+static bool_t
+xdr_nfsstat3(
+ XDR * xdrs,
+ nfsstat3 * objp)
+{
+ return(xdr_enum(xdrs, (enum_t *)objp));
+}
+
+static bool_t
+xdr_ftype3(
+ XDR * xdrs,
+ ftype3 * objp)
+{
+ return(xdr_enum(xdrs, (enum_t *)objp));
+}
+
+static bool_t
+xdr_fattr3(
+ XDR * xdrs,
+ fattr3 * objp)
+{
+ if (!xdr_ftype3(xdrs, &objp->type))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->mode))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->nlink))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->uid))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->gid))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->size))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->used))
+ return(FALSE);
+ if (!xdr_specdata3(xdrs, &objp->rdev))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->fsid))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->fileid))
+ return(FALSE);
+ if (!xdr_nfstime3(xdrs, &objp->atime))
+ return(FALSE);
+ if (!xdr_nfstime3(xdrs, &objp->mtime))
+ return(FALSE);
+ if (!xdr_nfstime3(xdrs, &objp->ctime))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_post_op_attr(
+ XDR * xdrs,
+ post_op_attr * objp)
+{
+ if (!xdr_bool(xdrs, &objp->attributes))
+ return(FALSE);
+ switch (objp->attributes) {
+ case TRUE: return(xdr_fattr3(xdrs, &objp->attr));
+ case FALSE: return(TRUE);
+ default: return(FALSE);
+ }
+}
+
+static bool_t
+xdr_wcc_attr(
+ XDR * xdrs,
+ wcc_attr * objp)
+{
+ if (xdr_nfs_uint64_t(xdrs, &objp->size) &&
+ xdr_nfstime3(xdrs, &objp->mtime))
+ return(xdr_nfstime3(xdrs, &objp->ctime));
+ return(FALSE);
+}
+
+static bool_t
+xdr_pre_op_attr(
+ XDR * xdrs,
+ pre_op_attr * objp)
+{
+ if (!xdr_bool(xdrs, &objp->attributes))
+ return(FALSE);
+ switch (objp->attributes) {
+ case TRUE: return(xdr_wcc_attr(xdrs, &objp->attr));
+ case FALSE: return(TRUE);
+ default: return(FALSE);
+ }
+}
+
+static bool_t
+xdr_wcc_data(
+ XDR * xdrs,
+ wcc_data * objp)
+{
+ if (xdr_pre_op_attr(xdrs, &objp->before))
+ return(xdr_post_op_attr(xdrs, &objp->after));
+ return(FALSE);
+}
+
+static bool_t
+xdr_post_op_fh3(
+ XDR * xdrs,
+ post_op_fh3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->handle_follows))
+ return(FALSE);
+ switch (objp->handle_follows) {
+ case TRUE: return(xdr_nfs_fh3(xdrs, &objp->handle));
+ case FALSE: return(TRUE);
+ default: return(FALSE);
+ }
+}
+
+static bool_t
+xdr_time_how(
+ XDR * xdrs,
+ time_how * objp)
+{
+ return(xdr_enum(xdrs, (enum_t *)objp));
+}
+
+static bool_t
+xdr_set_mode3(
+ XDR * xdrs,
+ set_mode3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == TRUE)
+ return(xdr_uint32_t(xdrs, &objp->mode));
+ return(TRUE);
+}
+
+static bool_t
+xdr_set_uid3(
+ XDR * xdrs,
+ set_uid3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == TRUE)
+ return(xdr_uint32_t(xdrs, &objp->uid));
+ return(TRUE);
+}
+
+static bool_t
+xdr_set_gid3(
+ XDR * xdrs,
+ set_gid3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == TRUE)
+ return(xdr_uint32_t(xdrs, &objp->gid));
+ return(TRUE);
+}
+
+static bool_t
+xdr_set_size3(
+ XDR * xdrs,
+ set_size3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == TRUE)
+ return(xdr_nfs_uint64_t(xdrs, &objp->size));
+ return(TRUE);
+}
+
+static bool_t
+xdr_set_atime(
+ XDR * xdrs,
+ set_atime * objp)
+{
+ if (!xdr_time_how(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == SET_TO_CLIENT_TIME)
+ return(xdr_nfstime3(xdrs, &objp->atime));
+ return(TRUE);
+}
+
+static bool_t
+xdr_set_mtime(
+ XDR * xdrs,
+ set_mtime * objp)
+{
+ if (!xdr_time_how(xdrs, &objp->set_it))
+ return(FALSE);
+ if (objp->set_it == SET_TO_CLIENT_TIME)
+ return(xdr_nfstime3(xdrs, &objp->mtime));
+ return(TRUE);
+}
+
+static bool_t
+xdr_sattr3(
+ XDR * xdrs,
+ sattr3 * objp)
+{
+ if (xdr_set_mode3(xdrs, &objp->mode) &&
+ xdr_set_uid3(xdrs, &objp->uid) &&
+ xdr_set_gid3(xdrs, &objp->gid) &&
+ xdr_set_size3(xdrs, &objp->size) &&
+ xdr_set_atime(xdrs, &objp->atime))
+ return(xdr_set_mtime(xdrs, &objp->mtime));
+ return(FALSE);
+}
+
+
+bool_t
+xdr_GETATTR3args(
+ XDR * xdrs,
+ GETATTR3args * objp)
+{
+ return(xdr_nfs_fh3(xdrs, &objp->object));
+}
+
+static bool_t
+xdr_GETATTR3resok(
+ XDR * xdrs,
+ GETATTR3resok * objp)
+{
+ return(xdr_fattr3(xdrs, &objp->obj_attributes));
+}
+
+bool_t
+xdr_GETATTR3res(
+ XDR * xdrs,
+ GETATTR3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_GETATTR3resok(xdrs, &objp->resok));
+ return(TRUE);
+}
+
+
+static bool_t
+xdr_sattrguard3(
+ XDR * xdrs,
+ sattrguard3 * objp)
+{
+ if (!xdr_bool(xdrs, &objp->check))
+ return(FALSE);
+ switch (objp->check) {
+ case TRUE: return(xdr_nfstime3(xdrs, &objp->obj_ctime));
+ case FALSE: return(TRUE);
+ default: return(FALSE);
+ }
+}
+
+bool_t
+xdr_SETATTR3args(
+ XDR * xdrs,
+ SETATTR3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->object) &&
+ xdr_sattr3(xdrs, &objp->new_attributes))
+ return(xdr_sattrguard3(xdrs, &objp->guard));
+ return(FALSE);
+}
+
+static bool_t
+xdr_SETATTR3resok(
+ XDR * xdrs,
+ SETATTR3resok * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->obj_wcc));
+}
+
+static bool_t
+xdr_SETATTR3resfail(
+ XDR * xdrs,
+ SETATTR3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->obj_wcc));
+}
+
+bool_t
+xdr_SETATTR3res(
+ XDR * xdrs,
+ SETATTR3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_SETATTR3resok(xdrs, &objp->resok));
+ return(xdr_SETATTR3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_LOOKUP3args(
+ XDR * xdrs,
+ LOOKUP3args * objp)
+{
+ return(xdr_diropargs3(xdrs, &objp->what));
+}
+
+static bool_t
+xdr_LOOKUP3resok(
+ XDR * xdrs,
+ LOOKUP3resok * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->object) &&
+ xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_post_op_attr(xdrs, &objp->dir_attributes));
+ return(FALSE);
+}
+
+static bool_t
+xdr_LOOKUP3resfail(
+ XDR * xdrs,
+ LOOKUP3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->dir_attributes));
+}
+
+bool_t
+xdr_LOOKUP3res(
+ XDR * xdrs,
+ LOOKUP3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_LOOKUP3resok(xdrs, &objp->resok));
+ return(xdr_LOOKUP3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_ACCESS3args(
+ XDR * xdrs,
+ ACCESS3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->object))
+ return(xdr_uint32_t(xdrs, &objp->access));
+ return(FALSE);
+}
+
+static bool_t
+xdr_ACCESS3resok(
+ XDR * xdrs,
+ ACCESS3resok * objp)
+{
+ if (xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_uint32_t(xdrs, &objp->access));
+ return(FALSE);
+}
+
+static bool_t
+xdr_ACCESS3resfail(
+ XDR * xdrs,
+ ACCESS3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->obj_attributes));
+}
+
+bool_t
+xdr_ACCESS3res(
+ XDR * xdrs,
+ ACCESS3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_ACCESS3resok(xdrs, &objp->resok));
+ return(xdr_ACCESS3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_READLINK3args(
+ XDR * xdrs,
+ READLINK3args * objp)
+{
+ return(xdr_nfs_fh3(xdrs, &objp->symlink));
+}
+
+static bool_t
+xdr_READLINK3resok(
+ XDR * xdrs,
+ READLINK3resok * objp)
+{
+ if (xdr_post_op_attr(xdrs, &objp->symlink_attributes))
+ return(xdr_nfspath3(xdrs, &objp->data));
+ return(FALSE);
+}
+
+static bool_t
+xdr_READLINK3resfail(
+ XDR * xdrs,
+ READLINK3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->symlink_attributes));
+}
+
+bool_t
+xdr_READLINK3res(
+ XDR * xdrs,
+ READLINK3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_READLINK3resok(xdrs, &objp->resok));
+ return(xdr_READLINK3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_READ3args(
+ XDR * xdrs,
+ READ3args * objp)
+{
+
+ if (xdr_nfs_fh3(xdrs, &objp->file) &&
+ xdr_nfs_uint64_t(xdrs, &objp->offset))
+ return(xdr_uint32_t(xdrs, &objp->count));
+ return(FALSE);
+}
+
+static bool_t
+xdr_READ3resok(
+ XDR * xdrs,
+ READ3resok * objp)
+{
+ if (xdr_post_op_attr(xdrs, &objp->file_attributes) &&
+ xdr_uint32_t(xdrs, &objp->count) &&
+ xdr_bool(xdrs, &objp->eof))
+ return(xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (unsigned int *)&objp->data.data_len,
+ ~0));
+ return(FALSE);
+}
+
+static bool_t
+xdr_READ3resfail(
+ XDR * xdrs,
+ READ3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->file_attributes));
+}
+
+bool_t
+xdr_READ3res(
+ XDR * xdrs,
+ READ3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_READ3resok(xdrs, &objp->resok));
+ return(xdr_READ3resfail(xdrs, &objp->resfail));
+}
+
+
+static bool_t
+xdr_stable_how(
+ XDR * xdrs,
+ stable_how * objp)
+{
+ return(xdr_enum(xdrs, (enum_t *)objp));
+}
+
+bool_t
+xdr_WRITE3args(
+ XDR * xdrs,
+ WRITE3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->file) &&
+ xdr_nfs_uint64_t(xdrs, &objp->offset) &&
+ xdr_uint32_t(xdrs, &objp->count) &&
+ xdr_stable_how(xdrs, &objp->stable))
+ return(xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (unsigned int *)&objp->data.data_len,
+ ~0));
+ return(FALSE);
+}
+
+static bool_t
+xdr_WRITE3resok(
+ XDR * xdrs,
+ WRITE3resok * objp)
+{
+ if (xdr_wcc_data(xdrs, &objp->file_wcc) &&
+ xdr_uint32_t(xdrs, &objp->count) &&
+ xdr_stable_how(xdrs, &objp->committed))
+ return(xdr_writeverf3(xdrs, objp->verf));
+ return(FALSE);
+}
+
+static bool_t
+xdr_WRITE3resfail(
+ XDR * xdrs,
+ WRITE3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->file_wcc));
+}
+
+bool_t
+xdr_WRITE3res(
+ XDR * xdrs,
+ WRITE3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_WRITE3resok(xdrs, &objp->resok));
+ return(xdr_WRITE3resfail(xdrs, &objp->resfail));
+}
+
+
+static bool_t
+xdr_createmode3(
+ XDR * xdrs,
+ createmode3 * objp)
+{
+ return(xdr_enum(xdrs, (enum_t *)objp));
+}
+
+static bool_t
+xdr_createhow3(
+ XDR * xdrs,
+ createhow3 * objp)
+{
+ if (!xdr_createmode3(xdrs, &objp->mode))
+ return(FALSE);
+ switch (objp->mode) {
+ case UNCHECKED:
+ case GUARDED:
+ return(xdr_sattr3(xdrs, &objp->createhow3_u.obj_attributes));
+ case EXCLUSIVE:
+ return(xdr_createverf3(xdrs, objp->createhow3_u.verf));
+ default:
+ return(FALSE);
+ }
+}
+
+bool_t
+xdr_CREATE3args(
+ XDR * xdrs,
+ CREATE3args * objp)
+{
+ if (xdr_diropargs3(xdrs, &objp->where))
+ return(xdr_createhow3(xdrs, &objp->how));
+ return(FALSE);
+}
+
+static bool_t
+xdr_CREATE3resok(
+ XDR * xdrs,
+ CREATE3resok * objp)
+{
+ if (xdr_post_op_fh3(xdrs, &objp->obj) &&
+ xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_CREATE3resfail(
+ XDR * xdrs,
+ CREATE3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_CREATE3res(
+ XDR * xdrs,
+ CREATE3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_CREATE3resok(xdrs, &objp->resok));
+ return(xdr_CREATE3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_MKDIR3args(
+ XDR * xdrs,
+ MKDIR3args * objp)
+{
+ if (xdr_diropargs3(xdrs, &objp->where))
+ return(xdr_sattr3(xdrs, &objp->attributes));
+ return(FALSE);
+}
+
+static bool_t
+xdr_MKDIR3resok(
+ XDR * xdrs,
+ MKDIR3resok * objp)
+{
+ if (xdr_post_op_fh3(xdrs, &objp->obj) &&
+ xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_MKDIR3resfail(
+ XDR * xdrs,
+ MKDIR3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_MKDIR3res(
+ XDR * xdrs,
+ MKDIR3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_MKDIR3resok(xdrs, &objp->resok));
+ return(xdr_MKDIR3resfail(xdrs, &objp->resfail));
+}
+
+
+static bool_t
+xdr_symlinkdata3(
+ XDR * xdrs,
+ symlinkdata3 * objp)
+{
+ if (xdr_sattr3(xdrs, &objp->symlink_attributes))
+ return(xdr_nfspath3(xdrs, &objp->symlink_data));
+ return(FALSE);
+}
+
+bool_t
+xdr_SYMLINK3args(
+ XDR * xdrs,
+ SYMLINK3args * objp)
+{
+ if (xdr_diropargs3(xdrs, &objp->where))
+ return(xdr_symlinkdata3(xdrs, &objp->symlink));
+ return(FALSE);
+}
+
+static bool_t
+xdr_SYMLINK3resok(
+ XDR * xdrs,
+ SYMLINK3resok * objp)
+{
+ if (xdr_post_op_fh3(xdrs, &objp->obj) &&
+ xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_SYMLINK3resfail(
+ XDR * xdrs,
+ SYMLINK3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_SYMLINK3res(
+ XDR * xdrs,
+ SYMLINK3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_SYMLINK3resok(xdrs, &objp->resok));
+ return(xdr_SYMLINK3resfail(xdrs, &objp->resfail));
+}
+
+
+static bool_t
+xdr_devicedata3(
+ XDR * xdrs,
+ devicedata3 * objp)
+{
+ if (xdr_sattr3(xdrs, &objp->dev_attributes))
+ return(xdr_specdata3(xdrs, &objp->spec));
+ return(FALSE);
+}
+
+static bool_t
+xdr_mknoddata3(
+ XDR * xdrs,
+ mknoddata3 * objp)
+{
+ if (!xdr_ftype3(xdrs, &objp->type))
+ return(FALSE);
+ switch (objp->type) {
+ case NF3CHR:
+ case NF3BLK:
+ if (!xdr_devicedata3(xdrs, &objp->mknoddata3_u.device))
+ return(FALSE);
+ break;
+ case NF3SOCK:
+ case NF3FIFO:
+ if (!xdr_sattr3(xdrs, &objp->mknoddata3_u.pipe_attributes))
+ return(FALSE);
+ break;
+ }
+ return(TRUE);
+}
+
+bool_t
+xdr_MKNOD3args(
+ XDR * xdrs,
+ MKNOD3args * objp)
+{
+ if (xdr_diropargs3(xdrs, &objp->where))
+ return(xdr_mknoddata3(xdrs, &objp->what));
+ return(FALSE);
+}
+
+static bool_t
+xdr_MKNOD3resok(
+ XDR * xdrs,
+ MKNOD3resok * objp)
+{
+ if (xdr_post_op_fh3(xdrs, &objp->obj) &&
+ xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_MKNOD3resfail(
+ XDR * xdrs,
+ MKNOD3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_MKNOD3res(
+ XDR * xdrs,
+ MKNOD3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_MKNOD3resok(xdrs, &objp->resok));
+ return(xdr_MKNOD3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_REMOVE3args(
+ XDR * xdrs,
+ REMOVE3args * objp)
+{
+ return(xdr_diropargs3(xdrs, &objp->object));
+}
+
+static bool_t
+xdr_REMOVE3resok(
+ XDR * xdrs,
+ REMOVE3resok * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+static bool_t
+xdr_REMOVE3resfail(
+ XDR * xdrs,
+ REMOVE3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_REMOVE3res(
+ XDR * xdrs,
+ REMOVE3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_REMOVE3resok(xdrs, &objp->resok));
+ return(xdr_REMOVE3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_RMDIR3args(
+ XDR * xdrs,
+ RMDIR3args * objp)
+{
+ return(xdr_diropargs3(xdrs, &objp->object));
+}
+
+static bool_t
+xdr_RMDIR3resok(
+ XDR * xdrs,
+ RMDIR3resok * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+static bool_t
+xdr_RMDIR3resfail(
+ XDR * xdrs,
+ RMDIR3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->dir_wcc));
+}
+
+bool_t
+xdr_RMDIR3res(
+ XDR * xdrs,
+ RMDIR3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_RMDIR3resok(xdrs, &objp->resok));
+ return(xdr_RMDIR3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_RENAME3args(
+ XDR * xdrs,
+ RENAME3args * objp)
+{
+ if (xdr_diropargs3(xdrs, &objp->from))
+ return(xdr_diropargs3(xdrs, &objp->to));
+ return(FALSE);
+}
+
+static bool_t
+xdr_RENAME3resok(
+ XDR * xdrs,
+ RENAME3resok * objp)
+{
+ if (xdr_wcc_data(xdrs, &objp->fromdir_wcc))
+ return(xdr_wcc_data(xdrs, &objp->todir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_RENAME3resfail(
+ XDR * xdrs,
+ RENAME3resfail * objp)
+{
+ if (xdr_wcc_data(xdrs, &objp->fromdir_wcc))
+ return(xdr_wcc_data(xdrs, &objp->todir_wcc));
+ return(FALSE);
+}
+
+bool_t
+xdr_RENAME3res(
+ XDR * xdrs,
+ RENAME3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_RENAME3resok(xdrs, &objp->resok));
+ return(xdr_RENAME3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_LINK3args(
+ XDR * xdrs,
+ LINK3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->file))
+ return(xdr_diropargs3(xdrs, &objp->link));
+ return(FALSE);
+}
+
+static bool_t
+xdr_LINK3resok(
+ XDR * xdrs,
+ LINK3resok * objp)
+{
+ if (xdr_post_op_attr(xdrs, &objp->file_attributes))
+ return(xdr_wcc_data(xdrs, &objp->linkdir_wcc));
+ return(FALSE);
+}
+
+static bool_t
+xdr_LINK3resfail(
+ XDR * xdrs,
+ LINK3resfail * objp)
+{
+ if (xdr_post_op_attr(xdrs, &objp->file_attributes))
+ return(xdr_wcc_data(xdrs, &objp->linkdir_wcc));
+ return(FALSE);
+}
+
+bool_t
+xdr_LINK3res(
+ XDR * xdrs,
+ LINK3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_LINK3resok(xdrs, &objp->resok));
+ return(xdr_LINK3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_READDIR3args(
+ XDR * xdrs,
+ READDIR3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->dir) &&
+ xdr_nfs_uint64_t(xdrs, &objp->cookie) &&
+ xdr_cookieverf3(xdrs, objp->cookieverf))
+ return(xdr_uint32_t(xdrs, &objp->count));
+ return(FALSE);
+}
+
+#define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1))
+
+/*
+ * DECODE ONLY
+ */
+static bool_t
+xdr_getdirlist(
+ XDR * xdrs,
+ READDIR3resok * objp)
+{
+ register int i;
+ bool_t valid;
+ unsigned int namlen;
+ char name[SFS_MAXNAMLEN];
+ nfs_uint64_t fileid, cookie;
+ entry3 *dp;
+
+ i = 0;
+ dp = objp->reply.entries;
+ for (;;) {
+ if (!xdr_bool(xdrs, &valid))
+ return(FALSE);
+ if (!valid)
+ break;
+ if (!xdr_nfs_uint64_t(xdrs, &fileid) ||
+ !xdr_u_int(xdrs, &namlen))
+ return(FALSE);
+ if (namlen >= SFS_MAXNAMLEN)
+ namlen = SFS_MAXNAMLEN - 1;
+ if (!xdr_opaque(xdrs, name, namlen) ||
+ !xdr_nfs_uint64_t(xdrs, &cookie))
+ return(FALSE);
+ name[namlen] = '\0';
+ if (i < SFS_MAXDIRENTS) {
+ dp[i].fileid = fileid;
+ (void)memmove(dp[i].name, name, (namlen+1));
+ dp[i].cookie = cookie;
+ i++;
+ }
+ }
+ objp->count = i;
+ if (!xdr_bool(xdrs, &objp->reply.eof))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_READDIR3resok(
+ XDR * xdrs,
+ READDIR3resok * objp)
+{
+ if (!xdr_post_op_attr(xdrs, &objp->dir_attributes))
+ return(FALSE);
+ if (!xdr_cookieverf3(xdrs, objp->cookieverf))
+ return(FALSE);
+ if (xdrs->x_op == XDR_DECODE)
+ return(xdr_getdirlist(xdrs, objp));
+ return(TRUE);
+}
+
+static bool_t
+xdr_READDIR3resfail(
+ XDR * xdrs,
+ READDIR3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->dir_attributes));
+}
+
+bool_t
+xdr_READDIR3res(
+ XDR * xdrs,
+ READDIR3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_READDIR3resok(xdrs, &objp->resok));
+ return(xdr_READDIR3resfail(xdrs, &objp->resfail));
+}
+
+bool_t
+xdr_READDIRPLUS3args(
+ XDR * xdrs,
+ READDIRPLUS3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->dir) &&
+ xdr_nfs_uint64_t(xdrs, &objp->cookie) &&
+ xdr_cookieverf3(xdrs, objp->cookieverf) &&
+ xdr_uint32_t(xdrs, &objp->dircount))
+ return(xdr_uint32_t(xdrs, &objp->maxcount));
+ return(FALSE);
+}
+
+/*
+ * copy post_op_attr from s2 to s1
+ */
+static void
+copy_post_op_attr(post_op_attr *s1, post_op_attr *s2)
+{
+ s1->attributes = s2->attributes;
+ (void) memmove((void *) &s1->attr, (void *) &s2->attr,
+ sizeof (fattr3));
+}
+
+/*
+ * copy post_op_fh3 from s2 to s1
+ */
+static void
+copy_post_op_fh3(post_op_fh3 *s1, post_op_fh3 *s2)
+{
+ s1->handle_follows = s2->handle_follows;
+ (void) memmove((void *) &s1->handle, (void *) &s2->handle,
+ sizeof (nfs_fh3));
+}
+
+/*
+ * DECODE ONLY
+ */
+static bool_t
+xdr_getdirpluslist(
+ XDR * xdrs,
+ READDIRPLUS3resok * objp)
+{
+ register int i;
+ bool_t valid;
+ unsigned int namlen;
+ char name[SFS_MAXNAMLEN];
+ nfs_uint64_t fileid, cookie;
+ entryplus3 *dp;
+ post_op_attr at;
+ post_op_fh3 fh;
+
+ i = 0;
+ dp = objp->reply.entries;
+ for (;;) {
+ if (!xdr_bool(xdrs, &valid))
+ return(FALSE);
+ if (!valid)
+ break;
+ if (!xdr_nfs_uint64_t(xdrs, &fileid) ||
+ !xdr_u_int(xdrs, &namlen))
+ return(FALSE);
+ if (namlen >= SFS_MAXNAMLEN)
+ namlen = SFS_MAXNAMLEN - 1;
+ if (!xdr_opaque(xdrs, name, namlen) ||
+ !xdr_nfs_uint64_t(xdrs, &cookie))
+ return(FALSE);
+ name[namlen] = '\0';
+ if (!xdr_post_op_attr(xdrs, &at))
+ return(FALSE);
+ if (!xdr_post_op_fh3(xdrs, &fh))
+ return(FALSE);
+ if (i < SFS_MAXDIRENTS) {
+ dp[i].fileid = fileid;
+ (void)memmove(dp[i].name, name, (namlen+1));
+ dp[i].cookie = cookie;
+ copy_post_op_attr(&dp[i].name_attributes, &at);
+ copy_post_op_fh3(&dp[i].name_handle, &fh);
+ i++;
+ }
+ }
+
+ objp->count = i;
+ if (!xdr_bool(xdrs, &objp->reply.eof))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_READDIRPLUS3resok(
+ XDR * xdrs,
+ READDIRPLUS3resok * objp)
+{
+
+ if (!xdr_post_op_attr(xdrs, &objp->dir_attributes))
+ return(FALSE);
+ if (!xdr_cookieverf3(xdrs, objp->cookieverf))
+ return(FALSE);
+ if (xdrs->x_op == XDR_DECODE)
+ return(xdr_getdirpluslist(xdrs, objp));
+ return(TRUE);
+}
+
+static bool_t
+xdr_READDIRPLUS3resfail(
+ XDR * xdrs,
+ READDIRPLUS3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->dir_attributes));
+}
+
+bool_t
+xdr_READDIRPLUS3res(
+ XDR * xdrs,
+ READDIRPLUS3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_READDIRPLUS3resok(xdrs, &objp->resok));
+ return(xdr_READDIRPLUS3resfail(xdrs, &objp->resfail));
+}
+
+bool_t
+xdr_FSSTAT3args(
+ XDR * xdrs,
+ FSSTAT3args * objp)
+{
+ return(xdr_nfs_fh3(xdrs, &objp->fsroot));
+}
+
+static bool_t
+xdr_FSSTAT3resok(
+ XDR * xdrs,
+ FSSTAT3resok * objp)
+{
+ if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->tbytes))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->fbytes))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->abytes))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->tfiles))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->ffiles))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->afiles))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->invarsec))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_FSSTAT3resfail(
+ XDR * xdrs,
+ FSSTAT3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->obj_attributes));
+}
+
+bool_t
+xdr_FSSTAT3res(
+ XDR * xdrs,
+ FSSTAT3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_FSSTAT3resok(xdrs, &objp->resok));
+ return(xdr_FSSTAT3resfail(xdrs, &objp->resfail));
+}
+
+bool_t
+xdr_FSINFO3args(
+ XDR * xdrs,
+ FSINFO3args * objp)
+{
+ return(xdr_nfs_fh3(xdrs, &objp->fsroot));
+}
+
+static bool_t
+xdr_FSINFO3resok(
+ XDR * xdrs,
+ FSINFO3resok * objp)
+{
+ if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->rtmax))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->rtpref))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->rtmult))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->wtmax))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->wtpref))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->wtmult))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->dtpref))
+ return(FALSE);
+ if (!xdr_nfs_uint64_t(xdrs, &objp->maxfilesize))
+ return(FALSE);
+ if (!xdr_nfstime3(xdrs, &objp->time_delta))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->properties))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_FSINFO3resfail(
+ XDR * xdrs,
+ FSINFO3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->obj_attributes));
+}
+
+bool_t
+xdr_FSINFO3res(
+ XDR * xdrs,
+ FSINFO3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_FSINFO3resok(xdrs, &objp->resok));
+ return(xdr_FSINFO3resfail(xdrs, &objp->resfail));
+}
+
+bool_t
+xdr_PATHCONF3args(
+ XDR * xdrs,
+ PATHCONF3args * objp)
+{
+ return(xdr_nfs_fh3(xdrs, &objp->object));
+}
+
+static bool_t
+xdr_PATHCONF3resok(
+ XDR * xdrs,
+ PATHCONF3resok * objp)
+{
+ if (!xdr_post_op_attr(xdrs, &objp->obj_attributes))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->link_max))
+ return(FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->name_max))
+ return(FALSE);
+ if (!xdr_bool(xdrs, &objp->no_trunc))
+ return(FALSE);
+ if (!xdr_bool(xdrs, &objp->chown_restricted))
+ return(FALSE);
+ if (!xdr_bool(xdrs, &objp->case_insensitive))
+ return(FALSE);
+ if (!xdr_bool(xdrs, &objp->case_preserving))
+ return(FALSE);
+ return(TRUE);
+}
+
+static bool_t
+xdr_PATHCONF3resfail(
+ XDR * xdrs,
+ PATHCONF3resfail * objp)
+{
+ return(xdr_post_op_attr(xdrs, &objp->obj_attributes));
+}
+
+bool_t
+xdr_PATHCONF3res(
+ XDR * xdrs,
+ PATHCONF3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_PATHCONF3resok(xdrs, &objp->resok));
+ return(xdr_PATHCONF3resfail(xdrs, &objp->resfail));
+}
+
+bool_t
+xdr_COMMIT3args(
+ XDR * xdrs,
+ COMMIT3args * objp)
+{
+ if (xdr_nfs_fh3(xdrs, &objp->file) &&
+ xdr_nfs_uint64_t(xdrs, &objp->offset))
+ return(xdr_uint32_t(xdrs, &objp->count));
+ return(FALSE);
+}
+
+static bool_t
+xdr_COMMIT3resok(
+ XDR * xdrs,
+ COMMIT3resok * objp)
+{
+ if (xdr_wcc_data(xdrs, &objp->file_wcc))
+ return(xdr_writeverf3(xdrs, objp->verf));
+ return(FALSE);
+}
+
+static bool_t
+xdr_COMMIT3resfail(
+ XDR * xdrs,
+ COMMIT3resfail * objp)
+{
+ return(xdr_wcc_data(xdrs, &objp->file_wcc));
+}
+
+bool_t
+xdr_COMMIT3res(
+ XDR * xdrs,
+ COMMIT3res * objp)
+{
+ if (!xdr_nfsstat3(xdrs, &objp->status))
+ return(FALSE);
+ if (objp->status == NFS3_OK)
+ return(xdr_COMMIT3resok(xdrs, &objp->resok));
+ return(xdr_COMMIT3resfail(xdrs, &objp->resfail));
+}
+
+
+bool_t
+xdr_dirpath(
+ XDR * xdrs,
+ dirpath * objp)
+{
+ return(xdr_string(xdrs, objp, MNTPATHLEN));
+}
+
+static bool_t
+xdr_fhandle3(
+ XDR * xdrs,
+ fhandle3 * objp)
+{
+ return(xdr_bytes(xdrs, (char **) &objp->fhandle3_val,
+ (unsigned int *) &objp->fhandle3_len, NFS3_FHSIZE));
+}
+
+static bool_t
+xdr_mntres3_ok(
+ XDR * xdrs,
+ mntres3_ok * objp)
+{
+ if (xdr_fhandle3(xdrs, &objp->fhandle)) {
+ return(xdr_array(xdrs,
+ (void **) &objp->auth_flavors.auth_flavors_val,
+ (unsigned int *) &objp->auth_flavors.auth_flavors_len,
+ ~0, sizeof (int), (xdrproc_t) xdr_int));
+ }
+ return(FALSE);
+}
+
+bool_t
+xdr_mntres3(
+ XDR * xdrs,
+ mountres3 * objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) &objp->fhs_status))
+ return(FALSE);
+ if (objp->fhs_status == MNT_OK)
+ return(xdr_mntres3_ok(xdrs, &objp->mntres3_u.mntinfo));
+ return(TRUE);
+}
+/* sfs_3_xdr.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_bioSid[] = "@(#)sfs_c_bio.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * ---------------------- sfs_c_bio.c ---------------------
+ *
+ * Routines that attempt to simulate biod behavior
+ *
+ * The routines contained here model biod behavior. Simply call
+ * biod_init() to replace regular calls to op_read() and op_write()
+ * with calls to op_biod_read() and op_biod_write(). The variables
+ * max_out_writes and max_out_reads control the maximum number of
+ * outstanding writes and reads respectively.
+ *
+ *.Exported Routines
+ * int biod_init(int, int);
+ * void biod_turn_on(void);
+ * void op_biod_write(int, int, int);
+ * void op_biod_read(int);
+ *
+ *.Local Routines
+ * uint32_t biod_clnt_call(CLIENT *, uint32_t,
+ * xdrproc_t, void *);
+ * struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
+ * void *, struct timeval *);
+ * int biod_poll_wait(CLIENT *, uint32_t);
+ *
+ *.Revision_History
+ * 03-May-94 Robinson
+ * History now kept in SCCS
+ * 03-Mar-92 0.1.0 Corbin
+ * Added biod behavior
+ */
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "rfs_c_def.h"
+
+/*
+ * Information associated with outstanding read/write requests
+ */
+#ifndef RFS
+struct biod_req {
+ uint32_t xid; /* RPC transmission ID */
+ bool_t in_use; /* Indicates if the entry is in use */
+ int dep_tab_index; /* corresponding index in dep_tab */
+ unsigned int count; /* Count saved for Dump routines */
+ unsigned int offset; /* Offset saved for Dump routines */
+ struct ladtime start; /* Time RPC call was made */
+ struct ladtime stop; /* Time RPC reply was received */
+ struct ladtime timeout; /* Time RPC call will time out */
+};
+#endif
+
+/*
+ * ---------------------- Static Declarations ----------------------
+ */
+
+static int max_out_writes;
+static int max_out_reads;
+int max_biod_reqs = 0;
+struct biod_req *biod_reqp;
+
+/* forward definitions for local functions */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
+ void *, struct timeval *);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+
+static int op_biod_write(int, int, int);
+static int op_biod_read(int);
+
+/*
+ * ---------------------- BIOD Support Routines ----------------------
+ */
+
+/*
+ * biod_init()
+ *
+ * This function is called during the initialization phase. It performs
+ * the following tasks:
+ * - Allocate memory to hold outstanding biod request information
+ *
+ * Returns 0 for OK, -1 for failure
+ */
+int
+biod_init(
+ int out_writes,
+ int out_reads)
+{
+ // RFS max_out_writes = MAXIMUM(1, out_writes);
+ // RFS max_out_reads = MAXIMUM(1, out_reads);
+ // RFS max_biod_reqs = MAXIMUM(out_writes, out_reads);
+ max_biod_reqs = MAX_OUTSTANDING_REQ; // RFS
+
+ biod_reqp = (struct biod_req *) calloc(max_biod_reqs,
+ sizeof (struct biod_req));
+ if (biod_reqp == (struct biod_req *)0) {
+ (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname);
+ (void) fflush(stderr);
+ return (-1);
+ }
+
+ return (0);
+} /* biod_init */
+
+#ifndef RFS
+
+/*
+ * - Change the operation functions for reads and writes to use the
+ * biod routines. This step should be done last to allow callers
+ * to still run with the old op functions if the biod initialization
+ * fails.
+ */
+void
+biod_turn_on(void)
+{
+ Ops[WRITE].funct = op_biod_write;
+ Ops[READ].funct = op_biod_read;
+}
+
+#endif
+
+/*
+ * biod_term()
+ *
+ * This function is called during the termination phase to free any resources
+ * allocated by the biod_init() routine. It performs the following tasks:
+ * - Frees memory associated with outstanding biod request information
+ * - Frees the biod client handle
+ */
+void
+biod_term(void)
+{
+ if (max_biod_reqs) {
+ free(biod_reqp);
+ }
+} /* biod_term */
+
+#ifndef RFS
+/*
+ * Perform and RPC biod style write operation of length 'xfer_size'.
+ * If 'append_flag' is true, then write the data to the end of the file.
+ */
+static int
+op_biod_write(
+ int xfer_size,
+ int append_flag,
+ int stab_flag)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ static char *buf = NULL; /* the data buffer */
+ unsigned int size; /* size of data write */
+ int max_cnt;
+ attrstat reply2; /* the reply */
+ writeargs args2;
+ WRITE3res reply3; /* the reply */
+ WRITE3args args3;
+ struct ladtime curr_time;
+ struct ladtime tmp_time;
+ struct ladtime call_timeout;
+ struct biod_req *reqp;
+ int ret; /* ret val == call success */
+ int num_out_reqs; /* # of outstanding writes */
+ int i;
+ int error;
+ int32_t offset;
+ static int calls = 0;
+
+ calls++;
+
+ if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
+ return (0);
+
+ /*
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+
+ /*
+ * For now we treat DATA_SYNC to be the same as FILE_SYNC.
+ * If it is not a V3 op then it must always be stable
+ */
+ if (stab_flag == DATA_SYNC || nfs_version != NFS_V3)
+ stab_flag = FILE_SYNC;
+
+ op_ptr = &Ops[WRITE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ args2.beginoffset = 0; /* unused */
+
+ if (append_flag == 1) {
+ args2.offset = Cur_file_ptr->attributes2.size;
+ args3.offset = Cur_file_ptr->attributes3.size;
+ } else {
+ if (fh_size(Cur_file_ptr) > xfer_size) {
+ offset = Bytes_per_block * (sfs_random() %
+ (((fh_size(Cur_file_ptr) - xfer_size)
+ / Bytes_per_block) + 1));
+ args2.offset = offset;
+ args3.offset._p._u = 0;
+ args3.offset._p._l = offset;
+ } else {
+ args2.offset = 0;
+ args3.offset._p._u = args3.offset._p._l = 0;
+ }
+ }
+
+ size = Bytes_per_block;
+ args2.totalcount = size; /* unused */
+ args2.data.data_len = size;
+ args2.data.data_val = buf;
+ args3.data.data_len = size;
+ args3.data.data_val = buf;
+ args3.count = size;
+ args3.stable = stab_flag;
+
+ /* Calculate the number of NFS writes required */
+ max_cnt = xfer_size / Bytes_per_block;
+ if ((xfer_size % Bytes_per_block) != 0) {
+ max_cnt++;
+ }
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0 &&
+ (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "write: %d buffers xfer_size %d\n",
+ max_cnt, xfer_size);
+ (void) fflush(stderr);
+ }
+
+ /* Mark all request slots as not in use */
+ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
+ reqp->in_use = FALSE;
+ }
+
+ if (Current_test_phase < Warmup_phase) {
+ call_timeout.sec = Nfs_timers[Init].tv_sec;
+ call_timeout.usec = Nfs_timers[Init].tv_usec;
+ } else {
+ call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ /* make the call(s) now */
+ num_out_reqs = 0;
+ while (xfer_size > 0 || num_out_reqs > 0) {
+ /*
+ * Send out calls async until either the maximum number of outstanding
+ * requests has been reached or there are no more requests to make.
+ */
+ while (num_out_reqs < max_out_writes && xfer_size > 0) {
+
+ /* find an empty write request slot */
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ break;
+ }
+ }
+
+ if (xfer_size < size) {
+ size = xfer_size;
+ args2.data.data_len = xfer_size;
+ args2.totalcount = xfer_size; /* unused */
+ args3.data.data_len = xfer_size;
+ args3.count = xfer_size;
+ }
+ xfer_size -= size;
+
+ sfs_gettime(&reqp->start);
+ if (nfs_version == NFS_V3) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args3);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args3.data.data_len;
+ reqp->offset = args3.offset._p._l;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+ if (nfs_version == NFS_VERSION) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC_WRITE,
+ xdr_write, (char *) &args2);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args2.data.data_len;
+ reqp->offset = args2.offset;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec);
+ }
+
+ args2.offset += size;
+ args3.offset._p._l += size;
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ break;
+ }
+ } /* while can make an async call */
+
+ /*
+ * Process replies while there is data on the socket buffer.
+ * Just do polls on the select, no sleeping occurs in this loop.
+ */
+ do {
+ error = biod_poll_wait(NFS_client, 0);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n",
+ sfs_Myname, calls);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case 0:
+ break;
+
+
+ default:
+ if (nfs_version == NFS_VERSION)
+ reqp = biod_get_reply(NFS_client, xdr_write,
+ (char *) &reply2,
+ &Nfs_timers[op_ptr->call_class]);
+ if (nfs_version == NFS_V3)
+ reqp = biod_get_reply(NFS_client, xdr_WRITE3res,
+ (char *) &reply3,
+ &Nfs_timers[op_ptr->call_class]);
+
+ /*
+ * If biod_get_reply returns NULL then we got an RPC
+ * level error, probably a dropped fragment or the
+ * remains of a previous partial request.
+ */
+ if (reqp == (struct biod_req *)NULL) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * We have a valid response, check if procedure completed
+ * correctly.
+ */
+ if ((nfs_version == NFS_VERSION &&
+ reply2.status == NFS_OK) ||
+ (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) {
+ Cur_file_ptr->state = Exists;
+ /*
+ * In updating attributes we may get replies out
+ * of order. We blindly update the attributes
+ * which may cause old attributes to be stored.
+ * XXX We should check for old attributes.
+ */
+ if (nfs_version == NFS_VERSION)
+ Cur_file_ptr->attributes2 =
+ reply2.attrstat_u.attributes;
+ if (nfs_version == NFS_V3)
+ Cur_file_ptr->attributes3 =
+ reply3.res_u.ok.file_wcc.after.attr;
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr,
+ "%s: WRITE %s %d bytes offset %d \n",
+ sfs_Myname, Cur_filename,
+ reqp->count, reqp->offset);
+ (void) fflush(stderr);
+ }
+
+ /* capture count and offset for possible dump */
+ Dump_count = reqp->count;
+ Dump_offset = reqp->offset;
+ sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec,
+ reqp->stop.sec, reqp->stop.usec);
+ }
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec);
+
+ (void) fprintf(stderr,
+ "[%d]:BIOD WRITE FAILED: xid %x",
+ calls, reqp->xid);
+
+ if (nfs_version == NFS_VERSION)
+ (void) fprintf(stderr, " status %d",
+ reply2.status);
+ if (nfs_version == NFS_V3)
+ (void) fprintf(stderr, " status %d",
+ reply3.status);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+ break;
+ }
+ } while (error > 0 && num_out_reqs > 0);
+
+ /* Scan for replies that have timed out */
+ if (num_out_reqs > 0) {
+ sfs_gettime(&curr_time);
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (reqp->timeout.sec < curr_time.sec ||
+ (reqp->timeout.sec == curr_time.sec &&
+ reqp->timeout.usec < curr_time.usec)) {
+
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec,
+ curr_time.sec, curr_time.usec);
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * We go to sleep waiting for a reply if all the requests have
+ * been sent and there are outstanding requests, or we cannot
+ * send any more requests.
+ */
+ if ((xfer_size <= 0 && num_out_reqs > 0) ||
+ num_out_reqs == max_out_writes) {
+ /*
+ * Find the next outstanding request that will timeout
+ * and take a time differential to use for the poll timeout.
+ * If the differential is less than zero, then we go to the
+ * top of the loop. Note that we are not picky on errors
+ * returned by select, after the sleep we return to the top
+ * of the loop so extensive error/status checking is not
+ * needed.
+ */
+ tmp_time.sec = 0;
+ tmp_time.usec = 0;
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (tmp_time.sec == 0 ||
+ (reqp->timeout.sec < tmp_time.sec ||
+ (reqp->timeout.sec == tmp_time.sec &&
+ reqp->timeout.usec < tmp_time.usec))) {
+
+ tmp_time = reqp->timeout;
+ }
+ }
+ if (tmp_time.sec == 0 && tmp_time.usec == 0)
+ continue;
+ sfs_gettime(&curr_time);
+ SUBTIME(tmp_time, curr_time);
+ (void) biod_poll_wait(NFS_client,
+ tmp_time.sec * 1000000 + tmp_time.usec);
+ }
+ } /* while not done */
+
+
+ /*
+ * If we have not gotten an error and we were asked for an async write
+ * send a commit operation.
+ */
+ if (ret && stab_flag != FILE_SYNC)
+ ret += (*Ops[COMMIT].funct)();
+
+ return (ret);
+
+} /* op_biod_write */
+
+
+/*
+ * perform an RPC read operation of length 'xfer_size'
+ */
+static int
+op_biod_read(
+ int xfer_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int max_cnt; /* packet ctrs */
+ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
+ readargs args2;
+ readres reply2; /* the reply */
+ READ3args args3;
+ READ3res reply3; /* the reply */
+ int size;
+ struct ladtime curr_time;
+ struct ladtime call_timeout;
+ struct ladtime tmp_time;
+ struct biod_req *reqp;
+ int ret; /* ret val == call success */
+ int num_out_reqs; /* # of outstanding writes */
+ int i;
+ int error;
+ int32_t offset;
+ static int calls = 0;
+
+ calls++;
+
+ if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
+ return (0);
+
+ op_ptr = &Ops[READ];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /*
+ * Don't allow a read of less than one block size
+ */
+ if (xfer_size < Bytes_per_block)
+ xfer_size = Bytes_per_block;
+
+
+ /* Calculate the number of NFS reads required */
+ max_cnt = xfer_size / Bytes_per_block;
+ if ((xfer_size % Bytes_per_block) != 0) {
+ max_cnt++;
+ }
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0 &&
+ (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+
+ args2.offset = 0;
+ args3.offset._p._l = args3.offset._p._u = 0;
+
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (fh_size(Cur_file_ptr) > xfer_size) {
+ offset = Bytes_per_block * (sfs_random() %
+ (((fh_size(Cur_file_ptr) - xfer_size)
+ / Bytes_per_block) + 1));
+ args2.offset = offset;
+ args3.offset._p._u = 0;
+ args3.offset._p._l = offset;
+ }
+
+ size = Bytes_per_block;
+ args2.count = size;
+ args3.count = size;
+ args2.totalcount = size; /* unused */
+
+ /* Have lower layers fill in the data directly. */
+ reply2.readres_u.reply.data.data_val = buf;
+ reply3.res_u.ok.data.data_val = buf;
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "read: %d buffers xfer_size %d\n",
+ max_cnt, xfer_size);
+ (void) fflush(stderr);
+ }
+
+ /* Mark all request slots as not in use */
+ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
+ reqp->in_use = FALSE;
+ }
+
+ if (Current_test_phase < Warmup_phase) {
+ call_timeout.sec = Nfs_timers[Init].tv_sec;
+ call_timeout.usec = Nfs_timers[Init].tv_usec;
+ } else {
+ call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ /* make the call(s) now */
+ num_out_reqs = 0;
+ while (xfer_size > 0 || num_out_reqs > 0) {
+ /*
+ * Send out calls async until either the maximum number of outstanding
+ * requests has been reached or there are no more requests to make.
+ */
+ while (num_out_reqs < max_out_reads && xfer_size > 0) {
+
+ /* find an empty read request slot */
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ break;
+ }
+ }
+
+ if (xfer_size < size) {
+ size = xfer_size;
+ args2.count = xfer_size;
+ args3.count = xfer_size;
+ args2.totalcount = xfer_size; /* unused */
+ }
+ xfer_size -= size;
+
+ sfs_gettime(&reqp->start);
+ if (nfs_version == NFS_VERSION) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC_READ,
+ xdr_read, (char *) &args2);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args2.count;
+ reqp->offset = args2.offset;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ } else if (nfs_version == NFS_V3) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC3_READ,
+ xdr_READ3args, (char *) &args3);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args3.count;
+ reqp->offset = args3.offset._p._l;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+
+ args2.offset += size;
+ args3.offset._p._l += size;
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ break;
+ }
+ } /* while can make an async call */
+
+ /*
+ * Process replies while there is data on the socket buffer.
+ * Just do polls on the select, no sleeping occurs in this loop.
+ */
+ do {
+ error = biod_poll_wait(NFS_client, 0);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr,
+ "%s:[%d]: biod_poll_wait error\n",
+ sfs_Myname, calls);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case 0:
+ break;
+
+
+ default:
+ if (nfs_version == NFS_VERSION)
+ reqp = biod_get_reply(NFS_client, xdr_read,
+ (char *) &reply2,
+ &Nfs_timers[op_ptr->call_class]);
+ if (nfs_version == NFS_V3)
+ reqp = biod_get_reply(NFS_client, xdr_READ3res,
+ (char *) &reply3,
+ &Nfs_timers[op_ptr->call_class]);
+
+ /*
+ * If biod_get_reply returns NULL then we got an RPC
+ * level error, probably a dropped fragment or the
+ * remains of a previous partial request.
+ */
+ if (reqp == (struct biod_req *)NULL) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * We have a valid response, check if procedure completed
+ * correctly.
+ */
+ if ((nfs_version == NFS_VERSION &&
+ reply2.status == NFS_OK) ||
+ (nfs_version == NFS_V3 &&
+ reply3.status == NFS3_OK)) {
+ Cur_file_ptr->state = Exists;
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n",
+ sfs_Myname, Cur_filename, reqp->count, reqp->offset);
+ (void) fflush(stderr);
+ }
+ /*
+ * In updating attributes we may get replies out
+ * of order. We blindly update the attributes
+ * which may cause old attributes to be stored.
+ * XXX We should check for old attributes.
+ */
+ if (nfs_version == NFS_VERSION) {
+ Cur_file_ptr->attributes2 =
+ reply2.readres_u.reply.attributes;
+ /* capture count and offset for possible dump */
+ Dump_count = reply2.readres_u.reply.data.data_len;
+ }
+ if (nfs_version == NFS_V3) {
+ Cur_file_ptr->attributes3 =
+ reply3.res_u.ok.file_attributes.attr;
+ /* capture count and offset for possible dump */
+ Dump_count = reply3.res_u.ok.data.data_len;
+ }
+
+ Dump_offset = reqp->offset;
+ sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD READ FAILED: xid %x",
+ calls, reqp->xid);
+
+ if (nfs_version == NFS_VERSION)
+ (void) fprintf(stderr, " status %d",
+ reply2.status);
+ if (nfs_version == NFS_V3)
+ (void) fprintf(stderr, " status %d",
+ reply3.status);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+ break;
+ } /* switch */
+ } while (error > 0 && num_out_reqs > 0);
+
+ /* Scan for replies that have timed out */
+ if (num_out_reqs > 0) {
+ sfs_gettime(&curr_time);
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (reqp->timeout.sec < curr_time.sec ||
+ (reqp->timeout.sec == curr_time.sec &&
+ reqp->timeout.usec < curr_time.usec)) {
+
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->timeout.sec, reqp->timeout.usec,
+ curr_time.sec, curr_time.usec);
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * We go to sleep waiting for a reply if all the requests have
+ * been sent and there are outstanding requests, or we cannot
+ * send any more requests.
+ */
+ if ((xfer_size <= 0 && num_out_reqs > 0) ||
+ num_out_reqs == max_out_reads) {
+ /*
+ * Find the next outstanding request that will timeout
+ * and take a time differential to use for the poll timeout.
+ * If the differential is less than zero, then we go to the
+ * top of the loop. Note that we are not picky on errors
+ * returned by select, after the sleep we return to the top
+ * of the loop so extensive error/status checking is not
+ * needed.
+ */
+ tmp_time.sec = 0;
+ tmp_time.usec = 0;
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (tmp_time.sec == 0 ||
+ (reqp->timeout.sec < tmp_time.sec ||
+ (reqp->timeout.sec == tmp_time.sec &&
+ reqp->timeout.usec < tmp_time.usec))) {
+
+ tmp_time = reqp->timeout;
+ }
+ }
+ if (tmp_time.sec == 0 && tmp_time.usec == 0)
+ continue;
+ sfs_gettime(&curr_time);
+ SUBTIME(tmp_time, curr_time);
+ (void) biod_poll_wait(NFS_client,
+ tmp_time.sec * 1000000 + tmp_time.usec);
+ }
+ } /* while not done */
+
+ return(ret);
+
+} /* op_biod_read */
+
+#endif
+
+/*
+ * ---------------------- Async RPC Support Routines ----------------------
+ */
+
+/*
+ * biod_clnt_call()
+ *
+ * Returns XID indicating success, 0 indicating failure.
+ */
+uint32_t
+biod_clnt_call(
+ CLIENT *clnt_handlep,
+ uint32_t proc,
+ xdrproc_t xargs,
+ void *argsp)
+{
+ struct timeval timeout;
+ uint32_t xid;
+
+ /*
+ * Set timeouts to be zero to force message passing semantics.
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL,
+ &xid, timeout)) != RPC_TIMEDOUT) {
+ clnt_perror(clnt_handlep, "biod_clnt_call failed");
+ return (0);
+ }
+
+ return (xid);
+} /* biod_clnt_call */
+
+
+/*
+ * biod_get_reply()
+ *
+ * Returns pointer to the biod_req struct entry that a reply was received
+ * for. Returns NULL if an error was detected.
+ * NOTES:
+ * 1) This routine should only be called when it is known that there is
+ * data waiting on the socket.
+ */
+static struct biod_req *
+biod_get_reply(
+ CLIENT *clnt_handlep,
+ xdrproc_t xresults,
+ void *resultsp,
+ struct timeval *tv)
+{
+ uint32_t xid;
+ int i;
+ int cnt = 0;
+ bool_t res;
+ uint32_t xids[MAX_BIODS];
+
+ /*
+ * Load list of valid outstanding xids
+ */
+ for (i = 0; i < max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == TRUE)
+ xids[cnt++] = biod_reqp[i].xid;
+ }
+
+ if (cnt == 0)
+ return (NULL);
+
+ if ((res = clnt_getreply(clnt_handlep, xresults,
+ resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) {
+ if (DEBUG_CHILD_BIOD) {
+ if (res == RPC_CANTDECODERES) {
+ (void) fprintf(stderr, "No xid matched, found %x\n",
+ xid);
+ }
+ }
+ return (NULL);
+ }
+
+ /*
+ * Scan to find XID matched in the outstanding request queue.
+ */
+ for (i = 0; i < max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) {
+ sfs_gettime(&(biod_reqp[i].stop));
+ return (&biod_reqp[i]);
+ }
+ }
+
+ return ((struct biod_req *)0);
+} /* biod_get_reply */
+
+/*
+ * biod_poll_wait()
+ *
+ * Returns -1 on error, 0 for no data available, > 0 to indicate data available
+ */
+int
+biod_poll_wait(
+ CLIENT *clnt_handlep,
+ uint32_t usecs)
+{
+ return (clnt_poll(clnt_handlep, usecs));
+} /* biod_poll_wait */
+
--- /dev/null
+#ifndef lint
+static char sfs_c_bioSid[] = "@(#)sfs_c_bio.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * ---------------------- sfs_c_bio.c ---------------------
+ *
+ * Routines that attempt to simulate biod behavior
+ *
+ * The routines contained here model biod behavior. Simply call
+ * biod_init() to replace regular calls to op_read() and op_write()
+ * with calls to op_biod_read() and op_biod_write(). The variables
+ * max_out_writes and max_out_reads control the maximum number of
+ * outstanding writes and reads respectively.
+ *
+ *.Exported Routines
+ * int biod_init(int, int);
+ * void biod_turn_on(void);
+ * void op_biod_write(int, int, int);
+ * void op_biod_read(int);
+ *
+ *.Local Routines
+ * uint32_t biod_clnt_call(CLIENT *, uint32_t,
+ * xdrproc_t, void *);
+ * struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
+ * void *, struct timeval *);
+ * int biod_poll_wait(CLIENT *, uint32_t);
+ *
+ *.Revision_History
+ * 03-May-94 Robinson
+ * History now kept in SCCS
+ * 03-Mar-92 0.1.0 Corbin
+ * Added biod behavior
+ */
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * Information associated with outstanding read/write requests
+ */
+struct biod_req {
+ uint32_t xid; /* RPC transmission ID */
+ bool_t in_use; /* Indicates if the entry is in use */
+ unsigned int count; /* Count saved for Dump routines */
+ unsigned int offset; /* Offset saved for Dump routines */
+ struct ladtime start; /* Time RPC call was made */
+ struct ladtime stop; /* Time RPC reply was received */
+ struct ladtime timeout; /* Time RPC call will time out */
+};
+
+/*
+ * ---------------------- Static Declarations ----------------------
+ */
+
+static int max_out_writes;
+static int max_out_reads;
+static int max_biod_reqs = 0;
+static struct biod_req *biod_reqp;
+
+/* forward definitions for local functions */
+static uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
+ void *, struct timeval *);
+static int biod_poll_wait(CLIENT *, uint32_t);
+
+static int op_biod_write(int, int, int);
+static int op_biod_read(int);
+
+/*
+ * ---------------------- BIOD Support Routines ----------------------
+ */
+
+/*
+ * biod_init()
+ *
+ * This function is called during the initialization phase. It performs
+ * the following tasks:
+ * - Allocate memory to hold outstanding biod request information
+ *
+ * Returns 0 for OK, -1 for failure
+ */
+int
+biod_init(
+ int out_writes,
+ int out_reads)
+{
+ max_out_writes = MAXIMUM(1, out_writes);
+ max_out_reads = MAXIMUM(1, out_reads);
+ max_biod_reqs = MAXIMUM(out_writes, out_reads);
+
+ biod_reqp = (struct biod_req *) calloc(max_biod_reqs,
+ sizeof (struct biod_req));
+ if (biod_reqp == (struct biod_req *)0) {
+ (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname);
+ (void) fflush(stderr);
+ return (-1);
+ }
+
+ return (0);
+} /* biod_init */
+
+
+/*
+ * - Change the operation functions for reads and writes to use the
+ * biod routines. This step should be done last to allow callers
+ * to still run with the old op functions if the biod initialization
+ * fails.
+ */
+void
+biod_turn_on(void)
+{
+ Ops[WRITE].funct = op_biod_write;
+ Ops[READ].funct = op_biod_read;
+}
+
+/*
+ * biod_term()
+ *
+ * This function is called during the termination phase to free any resources
+ * allocated by the biod_init() routine. It performs the following tasks:
+ * - Frees memory associated with outstanding biod request information
+ * - Frees the biod client handle
+ */
+void
+biod_term(void)
+{
+ if (max_biod_reqs) {
+ free(biod_reqp);
+ }
+} /* biod_term */
+
+/*
+ * Perform and RPC biod style write operation of length 'xfer_size'.
+ * If 'append_flag' is true, then write the data to the end of the file.
+ */
+static int
+op_biod_write(
+ int xfer_size,
+ int append_flag,
+ int stab_flag)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ static char *buf = NULL; /* the data buffer */
+ unsigned int size; /* size of data write */
+ int max_cnt;
+ attrstat reply2; /* the reply */
+ writeargs args2;
+ WRITE3res reply3; /* the reply */
+ WRITE3args args3;
+ struct ladtime curr_time;
+ struct ladtime tmp_time;
+ struct ladtime call_timeout;
+ struct biod_req *reqp;
+ int ret; /* ret val == call success */
+ int num_out_reqs; /* # of outstanding writes */
+ int i;
+ int error;
+ int32_t offset;
+ static int calls = 0;
+
+ calls++;
+
+ if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
+ return (0);
+
+ /*
+ * Initialize write buffer to known value
+ */
+ if (buf == NULL) {
+ buf = init_write_buffer();
+ }
+
+
+ /*
+ * For now we treat DATA_SYNC to be the same as FILE_SYNC.
+ * If it is not a V3 op then it must always be stable
+ */
+ if (stab_flag == DATA_SYNC || nfs_version != NFS_V3)
+ stab_flag = FILE_SYNC;
+
+ op_ptr = &Ops[WRITE];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ args2.beginoffset = 0; /* unused */
+
+ if (append_flag == 1) {
+ args2.offset = Cur_file_ptr->attributes2.size;
+ args3.offset = Cur_file_ptr->attributes3.size;
+ } else {
+ if (fh_size(Cur_file_ptr) > xfer_size) {
+ offset = Bytes_per_block * (sfs_random() %
+ (((fh_size(Cur_file_ptr) - xfer_size)
+ / Bytes_per_block) + 1));
+ args2.offset = offset;
+ args3.offset._p._u = 0;
+ args3.offset._p._l = offset;
+ } else {
+ args2.offset = 0;
+ args3.offset._p._u = args3.offset._p._l = 0;
+ }
+ }
+
+ size = Bytes_per_block;
+ args2.totalcount = size; /* unused */
+ args2.data.data_len = size;
+ args2.data.data_val = buf;
+ args3.data.data_len = size;
+ args3.data.data_val = buf;
+ args3.count = size;
+ args3.stable = stab_flag;
+
+ /* Calculate the number of NFS writes required */
+ max_cnt = xfer_size / Bytes_per_block;
+ if ((xfer_size % Bytes_per_block) != 0) {
+ max_cnt++;
+ }
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0 &&
+ (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "write: %d buffers xfer_size %d\n",
+ max_cnt, xfer_size);
+ (void) fflush(stderr);
+ }
+
+ /* Mark all request slots as not in use */
+ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
+ reqp->in_use = FALSE;
+ }
+
+ if (Current_test_phase < Warmup_phase) {
+ call_timeout.sec = Nfs_timers[Init].tv_sec;
+ call_timeout.usec = Nfs_timers[Init].tv_usec;
+ } else {
+ call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ /* make the call(s) now */
+ num_out_reqs = 0;
+ while (xfer_size > 0 || num_out_reqs > 0) {
+ /*
+ * Send out calls async until either the maximum number of outstanding
+ * requests has been reached or there are no more requests to make.
+ */
+ while (num_out_reqs < max_out_writes && xfer_size > 0) {
+
+ /* find an empty write request slot */
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ break;
+ }
+ }
+
+ if (xfer_size < size) {
+ size = xfer_size;
+ args2.data.data_len = xfer_size;
+ args2.totalcount = xfer_size; /* unused */
+ args3.data.data_len = xfer_size;
+ args3.count = xfer_size;
+ }
+ xfer_size -= size;
+
+ sfs_gettime(&reqp->start);
+ if (nfs_version == NFS_V3) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC3_WRITE,
+ xdr_WRITE3args, (char *) &args3);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args3.data.data_len;
+ reqp->offset = args3.offset._p._l;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+ if (nfs_version == NFS_VERSION) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC_WRITE,
+ xdr_write, (char *) &args2);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args2.data.data_len;
+ reqp->offset = args2.offset;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec);
+ }
+
+ args2.offset += size;
+ args3.offset._p._l += size;
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ break;
+ }
+ } /* while can make an async call */
+
+ /*
+ * Process replies while there is data on the socket buffer.
+ * Just do polls on the select, no sleeping occurs in this loop.
+ */
+ do {
+ error = biod_poll_wait(NFS_client, 0);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n",
+ sfs_Myname, calls);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case 0:
+ break;
+
+
+ default:
+ if (nfs_version == NFS_VERSION)
+ reqp = biod_get_reply(NFS_client, xdr_write,
+ (char *) &reply2,
+ &Nfs_timers[op_ptr->call_class]);
+ if (nfs_version == NFS_V3)
+ reqp = biod_get_reply(NFS_client, xdr_WRITE3res,
+ (char *) &reply3,
+ &Nfs_timers[op_ptr->call_class]);
+
+ /*
+ * If biod_get_reply returns NULL then we got an RPC
+ * level error, probably a dropped fragment or the
+ * remains of a previous partial request.
+ */
+ if (reqp == (struct biod_req *)NULL) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * We have a valid response, check if procedure completed
+ * correctly.
+ */
+ if ((nfs_version == NFS_VERSION &&
+ reply2.status == NFS_OK) ||
+ (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) {
+ Cur_file_ptr->state = Exists;
+ /*
+ * In updating attributes we may get replies out
+ * of order. We blindly update the attributes
+ * which may cause old attributes to be stored.
+ * XXX We should check for old attributes.
+ */
+ if (nfs_version == NFS_VERSION)
+ Cur_file_ptr->attributes2 =
+ reply2.attrstat_u.attributes;
+ if (nfs_version == NFS_V3)
+ Cur_file_ptr->attributes3 =
+ reply3.res_u.ok.file_wcc.after.attr;
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr,
+ "%s: WRITE %s %d bytes offset %d \n",
+ sfs_Myname, Cur_filename,
+ reqp->count, reqp->offset);
+ (void) fflush(stderr);
+ }
+
+ /* capture count and offset for possible dump */
+ Dump_count = reqp->count;
+ Dump_offset = reqp->offset;
+ sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec,
+ reqp->stop.sec, reqp->stop.usec);
+ }
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec);
+
+ (void) fprintf(stderr,
+ "[%d]:BIOD WRITE FAILED: xid %x",
+ calls, reqp->xid);
+
+ if (nfs_version == NFS_VERSION)
+ (void) fprintf(stderr, " status %d",
+ reply2.status);
+ if (nfs_version == NFS_V3)
+ (void) fprintf(stderr, " status %d",
+ reply3.status);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+ break;
+ }
+ } while (error > 0 && num_out_reqs > 0);
+
+ /* Scan for replies that have timed out */
+ if (num_out_reqs > 0) {
+ sfs_gettime(&curr_time);
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (reqp->timeout.sec < curr_time.sec ||
+ (reqp->timeout.sec == curr_time.sec &&
+ reqp->timeout.usec < curr_time.usec)) {
+
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->start.sec, reqp->start.usec,
+ reqp->timeout.sec, reqp->timeout.usec,
+ curr_time.sec, curr_time.usec);
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * We go to sleep waiting for a reply if all the requests have
+ * been sent and there are outstanding requests, or we cannot
+ * send any more requests.
+ */
+ if ((xfer_size <= 0 && num_out_reqs > 0) ||
+ num_out_reqs == max_out_writes) {
+ /*
+ * Find the next outstanding request that will timeout
+ * and take a time differential to use for the poll timeout.
+ * If the differential is less than zero, then we go to the
+ * top of the loop. Note that we are not picky on errors
+ * returned by select, after the sleep we return to the top
+ * of the loop so extensive error/status checking is not
+ * needed.
+ */
+ tmp_time.sec = 0;
+ tmp_time.usec = 0;
+ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (tmp_time.sec == 0 ||
+ (reqp->timeout.sec < tmp_time.sec ||
+ (reqp->timeout.sec == tmp_time.sec &&
+ reqp->timeout.usec < tmp_time.usec))) {
+
+ tmp_time = reqp->timeout;
+ }
+ }
+ if (tmp_time.sec == 0 && tmp_time.usec == 0)
+ continue;
+ sfs_gettime(&curr_time);
+ SUBTIME(tmp_time, curr_time);
+ (void) biod_poll_wait(NFS_client,
+ tmp_time.sec * 1000000 + tmp_time.usec);
+ }
+ } /* while not done */
+
+
+ /*
+ * If we have not gotten an error and we were asked for an async write
+ * send a commit operation.
+ */
+ if (ret && stab_flag != FILE_SYNC)
+ ret += (*Ops[COMMIT].funct)();
+
+ return (ret);
+
+} /* op_biod_write */
+
+
+/*
+ * perform an RPC read operation of length 'xfer_size'
+ */
+static int
+op_biod_read(
+ int xfer_size)
+{
+ sfs_op_type *op_ptr; /* per operation info */
+ int max_cnt; /* packet ctrs */
+ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
+ readargs args2;
+ readres reply2; /* the reply */
+ READ3args args3;
+ READ3res reply3; /* the reply */
+ int size;
+ struct ladtime curr_time;
+ struct ladtime call_timeout;
+ struct ladtime tmp_time;
+ struct biod_req *reqp;
+ int ret; /* ret val == call success */
+ int num_out_reqs; /* # of outstanding writes */
+ int i;
+ int error;
+ int32_t offset;
+ static int calls = 0;
+
+ calls++;
+
+ if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
+ return (0);
+
+ op_ptr = &Ops[READ];
+ ret = 0;
+
+ /* set up the arguments */
+ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
+ NFS_FHSIZE);
+ (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
+ sizeof (nfs_fh3));
+
+ /*
+ * Don't allow a read of less than one block size
+ */
+ if (xfer_size < Bytes_per_block)
+ xfer_size = Bytes_per_block;
+
+
+ /* Calculate the number of NFS reads required */
+ max_cnt = xfer_size / Bytes_per_block;
+ if ((xfer_size % Bytes_per_block) != 0) {
+ max_cnt++;
+ }
+
+ /* check our stats to see if this would overflow */
+ if (!Timed_run) {
+ if (op_ptr->target_calls > 0 &&
+ (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
+ max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
+ }
+ }
+
+ args2.offset = 0;
+ args3.offset._p._l = args3.offset._p._u = 0;
+
+ /*
+ * randomly choose an offset that is a multiple of the block size
+ * and constrained by making the transfer fit within the file
+ */
+ if (fh_size(Cur_file_ptr) > xfer_size) {
+ offset = Bytes_per_block * (sfs_random() %
+ (((fh_size(Cur_file_ptr) - xfer_size)
+ / Bytes_per_block) + 1));
+ args2.offset = offset;
+ args3.offset._p._u = 0;
+ args3.offset._p._l = offset;
+ }
+
+ size = Bytes_per_block;
+ args2.count = size;
+ args3.count = size;
+ args2.totalcount = size; /* unused */
+
+ /* Have lower layers fill in the data directly. */
+ reply2.readres_u.reply.data.data_val = buf;
+ reply3.res_u.ok.data.data_val = buf;
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "read: %d buffers xfer_size %d\n",
+ max_cnt, xfer_size);
+ (void) fflush(stderr);
+ }
+
+ /* Mark all request slots as not in use */
+ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
+ reqp->in_use = FALSE;
+ }
+
+ if (Current_test_phase < Warmup_phase) {
+ call_timeout.sec = Nfs_timers[Init].tv_sec;
+ call_timeout.usec = Nfs_timers[Init].tv_usec;
+ } else {
+ call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+ }
+
+ /* capture length for possible dump */
+ Dump_length = fh_size(Cur_file_ptr);
+
+ /* make the call(s) now */
+ num_out_reqs = 0;
+ while (xfer_size > 0 || num_out_reqs > 0) {
+ /*
+ * Send out calls async until either the maximum number of outstanding
+ * requests has been reached or there are no more requests to make.
+ */
+ while (num_out_reqs < max_out_reads && xfer_size > 0) {
+
+ /* find an empty read request slot */
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ break;
+ }
+ }
+
+ if (xfer_size < size) {
+ size = xfer_size;
+ args2.count = xfer_size;
+ args3.count = xfer_size;
+ args2.totalcount = xfer_size; /* unused */
+ }
+ xfer_size -= size;
+
+ sfs_gettime(&reqp->start);
+ if (nfs_version == NFS_VERSION) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC_READ,
+ xdr_read, (char *) &args2);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args2.count;
+ reqp->offset = args2.offset;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ } else if (nfs_version == NFS_V3) {
+ reqp->xid = biod_clnt_call(NFS_client,
+ (uint32_t)NFSPROC3_READ,
+ xdr_READ3args, (char *) &args3);
+ if (reqp->xid != 0) {
+ /* capture count and offset for possible dump */
+ reqp->count = args3.count;
+ reqp->offset = args3.offset._p._l;
+ reqp->timeout = reqp->start;
+ ADDTIME(reqp->timeout, call_timeout);
+ reqp->in_use = TRUE;
+ num_out_reqs++;
+ }
+ }
+
+ args2.offset += size;
+ args3.offset._p._l += size;
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ break;
+ }
+ } /* while can make an async call */
+
+ /*
+ * Process replies while there is data on the socket buffer.
+ * Just do polls on the select, no sleeping occurs in this loop.
+ */
+ do {
+ error = biod_poll_wait(NFS_client, 0);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr,
+ "%s:[%d]: biod_poll_wait error\n",
+ sfs_Myname, calls);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case 0:
+ break;
+
+
+ default:
+ if (nfs_version == NFS_VERSION)
+ reqp = biod_get_reply(NFS_client, xdr_read,
+ (char *) &reply2,
+ &Nfs_timers[op_ptr->call_class]);
+ if (nfs_version == NFS_V3)
+ reqp = biod_get_reply(NFS_client, xdr_READ3res,
+ (char *) &reply3,
+ &Nfs_timers[op_ptr->call_class]);
+
+ /*
+ * If biod_get_reply returns NULL then we got an RPC
+ * level error, probably a dropped fragment or the
+ * remains of a previous partial request.
+ */
+ if (reqp == (struct biod_req *)NULL) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * We have a valid response, check if procedure completed
+ * correctly.
+ */
+ if ((nfs_version == NFS_VERSION &&
+ reply2.status == NFS_OK) ||
+ (nfs_version == NFS_V3 &&
+ reply3.status == NFS3_OK)) {
+ Cur_file_ptr->state = Exists;
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n",
+ sfs_Myname, Cur_filename, reqp->count, reqp->offset);
+ (void) fflush(stderr);
+ }
+ /*
+ * In updating attributes we may get replies out
+ * of order. We blindly update the attributes
+ * which may cause old attributes to be stored.
+ * XXX We should check for old attributes.
+ */
+ if (nfs_version == NFS_VERSION) {
+ Cur_file_ptr->attributes2 =
+ reply2.readres_u.reply.attributes;
+ /* capture count and offset for possible dump */
+ Dump_count = reply2.readres_u.reply.data.data_len;
+ }
+ if (nfs_version == NFS_V3) {
+ Cur_file_ptr->attributes3 =
+ reply3.res_u.ok.file_attributes.attr;
+ /* capture count and offset for possible dump */
+ Dump_count = reply3.res_u.ok.data.data_len;
+ }
+
+ Dump_offset = reqp->offset;
+ sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ ret++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD READ FAILED: xid %x",
+ calls, reqp->xid);
+
+ if (nfs_version == NFS_VERSION)
+ (void) fprintf(stderr, " status %d",
+ reply2.status);
+ if (nfs_version == NFS_V3)
+ (void) fprintf(stderr, " status %d",
+ reply3.status);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+ break;
+ } /* switch */
+ } while (error > 0 && num_out_reqs > 0);
+
+ /* Scan for replies that have timed out */
+ if (num_out_reqs > 0) {
+ sfs_gettime(&curr_time);
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (reqp->timeout.sec < curr_time.sec ||
+ (reqp->timeout.sec == curr_time.sec &&
+ reqp->timeout.usec < curr_time.usec)) {
+
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ reqp->in_use = FALSE;
+ num_out_reqs--;
+ if (DEBUG_CHILD_BIOD) {
+ (void) fprintf (stderr,
+"[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n",
+ calls, reqp->xid,
+ reqp->timeout.sec, reqp->timeout.usec,
+ curr_time.sec, curr_time.usec);
+ if (biod_poll_wait(NFS_client, 0) > 0) {
+ (void) fprintf(stderr,
+ "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * We go to sleep waiting for a reply if all the requests have
+ * been sent and there are outstanding requests, or we cannot
+ * send any more requests.
+ */
+ if ((xfer_size <= 0 && num_out_reqs > 0) ||
+ num_out_reqs == max_out_reads) {
+ /*
+ * Find the next outstanding request that will timeout
+ * and take a time differential to use for the poll timeout.
+ * If the differential is less than zero, then we go to the
+ * top of the loop. Note that we are not picky on errors
+ * returned by select, after the sleep we return to the top
+ * of the loop so extensive error/status checking is not
+ * needed.
+ */
+ tmp_time.sec = 0;
+ tmp_time.usec = 0;
+ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
+ if (reqp->in_use == FALSE) {
+ continue;
+ }
+ if (tmp_time.sec == 0 ||
+ (reqp->timeout.sec < tmp_time.sec ||
+ (reqp->timeout.sec == tmp_time.sec &&
+ reqp->timeout.usec < tmp_time.usec))) {
+
+ tmp_time = reqp->timeout;
+ }
+ }
+ if (tmp_time.sec == 0 && tmp_time.usec == 0)
+ continue;
+ sfs_gettime(&curr_time);
+ SUBTIME(tmp_time, curr_time);
+ (void) biod_poll_wait(NFS_client,
+ tmp_time.sec * 1000000 + tmp_time.usec);
+ }
+ } /* while not done */
+
+ return(ret);
+
+} /* op_biod_read */
+
+/*
+ * ---------------------- Async RPC Support Routines ----------------------
+ */
+
+/*
+ * biod_clnt_call()
+ *
+ * Returns XID indicating success, 0 indicating failure.
+ */
+static uint32_t
+biod_clnt_call(
+ CLIENT *clnt_handlep,
+ uint32_t proc,
+ xdrproc_t xargs,
+ void *argsp)
+{
+ struct timeval timeout;
+ uint32_t xid;
+
+ /*
+ * Set timeouts to be zero to force message passing semantics.
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL,
+ &xid, timeout)) != RPC_TIMEDOUT) {
+ clnt_perror(clnt_handlep, "biod_clnt_call failed");
+ return (0);
+ }
+
+ return (xid);
+} /* biod_clnt_call */
+
+
+/*
+ * biod_get_reply()
+ *
+ * Returns pointer to the biod_req struct entry that a reply was received
+ * for. Returns NULL if an error was detected.
+ * NOTES:
+ * 1) This routine should only be called when it is known that there is
+ * data waiting on the socket.
+ */
+static struct biod_req *
+biod_get_reply(
+ CLIENT *clnt_handlep,
+ xdrproc_t xresults,
+ void *resultsp,
+ struct timeval *tv)
+{
+ uint32_t xid;
+ int i;
+ int cnt = 0;
+ bool_t res;
+ uint32_t xids[MAX_BIODS];
+
+ /*
+ * Load list of valid outstanding xids
+ */
+ for (i = 0; i < max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == TRUE)
+ xids[cnt++] = biod_reqp[i].xid;
+ }
+
+ if (cnt == 0)
+ return (NULL);
+
+ if ((res = clnt_getreply(clnt_handlep, xresults,
+ resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) {
+ if (DEBUG_CHILD_BIOD) {
+ if (res == RPC_CANTDECODERES) {
+ (void) fprintf(stderr, "No xid matched, found %x\n",
+ xid);
+ }
+ }
+ return (NULL);
+ }
+
+ /*
+ * Scan to find XID matched in the outstanding request queue.
+ */
+ for (i = 0; i < max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) {
+ sfs_gettime(&(biod_reqp[i].stop));
+ return (&biod_reqp[i]);
+ }
+ }
+
+ return ((struct biod_req *)0);
+} /* biod_get_reply */
+
+
+/*
+ * biod_poll_wait()
+ *
+ * Returns -1 on error, 0 for no data available, > 0 to indicate data available
+ */
+static int
+biod_poll_wait(
+ CLIENT *clnt_handlep,
+ uint32_t usecs)
+{
+ return (clnt_poll(clnt_handlep, usecs));
+} /* biod_poll_wait */
--- /dev/null
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+#include "nfsd_nfsfh_cust.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout);
+extern char * parse_name (char * t, char * buf);
+
+/* forward definitions for local functions */
+static int init_rpc(void);
+
+/* RFS: forward definitions for local functions */
+void init_ops(void);
+static void init_signal();
+extern void init_file_system (void);
+extern void init_dep_tab (void);
+static int read_trace(void);
+static void read_fh_map();
+static void init_play (char * mount_point);
+static void trace_play(void);
+void print_result(void);
+static int get_nextop(void);
+static int check_timeout(void);
+static struct biod_req * get_biod_req(int dep_tab_index);
+static int lookup_biod_req (int xid);
+static void init_time_offset(void);
+void adjust_play_window (int flag, int * poll_timeout);
+static int poll_and_get_reply (int usecs);
+static char * nfs3_strerror(int status);
+static void check_clock(void);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void usage(void);
+void init_dep_tab_entry (int dep_index);
+extern inline fh_map_t * lookup_fh (char * trace_fh );
+static inline void finish_request (int biod_index, int dep_index, int status);
+static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line);
+static inline char * find_lead_trace_fh(int proc, char * line);
+static inline char * find_reply_trace_fh (char * line);
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+
+static char io_buf[BUFSIZ]; /* io buffer for print out messages */
+
+char * sfs_Myname;
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+int Validate = 0; /* fake variable */
+int Child_num = 0; /* fake: child index */
+int Tcp = 0; /* We implement UDP first */
+int Client_num = 1; /* fake: number of client */
+uid_t Real_uid;
+gid_t Cur_gid;
+uid_t Cur_uid;
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14,
+ 13, 21, 10, 15, 11, 12, 16, 23, 17, 20,
+ 22, 19};
+void print_usage(int pos, int argc, char ** argv)
+{
+ int i;
+ printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n");
+ printf("sfs3 -pair_trace trace_file\n");
+ printf("sfs3 -pair_write trace_file\n");
+ printf("sfs3 -help\n");
+ printf ("pos %d argc %d", pos, argc);
+ for (i=0; i<argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf ("\n");
+ exit;
+}
+
+void print_cyclic_buffers ()
+{
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+}
+
+void add_to_dep_tab(int i)
+{
+ char * trace_fh;
+ fh_map_t * fh_map_entry;
+ dep_tab_t * ent;
+
+ trace_fh = strstr (memory_trace[i].line, "fh");
+ if (!trace_fh)
+ printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line);
+ RFS_ASSERT (trace_fh);
+ trace_fh += 3;
+ fh_map_entry = lookup_fh (trace_fh);
+ if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) {
+ req_num_with_discard_fh ++;
+ return;
+ }
+ if (fh_map_entry)
+ req_num_with_init_fh ++;
+ else
+ req_num_with_new_fh ++;
+
+ RFS_ASSERT (!CYCLIC_FULL(dep_tab_index));
+ ent = &(dep_tab[dep_tab_index.head]);
+
+ ent->disk_index = memory_trace[i].disk_index;
+ ent->memory_index = i;
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ ent->trace_status = memory_trace[i].trace_status;
+ ent->reply_trace_fh = memory_trace[i].reply_trace_fh;
+#endif
+ ent->line = memory_trace[i].line;
+ init_dep_tab_entry(dep_tab_index.head);
+
+ if (rfs_debug && (i%100000)==0)
+ printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index);
+ CYCLIC_MOVE_HEAD(memory_trace_index);
+ CYCLIC_MOVE_HEAD(dep_tab_index);
+}
+
+void init_profile_variables ()
+{
+ init_profile ("total_profile", &total_profile);
+ init_profile ("execute_next_request_profile", &execute_next_request_profile);
+ init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile);
+ init_profile ("prepare_argument_profile", &prepare_argument_profile);
+ init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ init_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ init_profile ("decode_reply_profile", &decode_reply_profile);
+ init_profile ("check_reply_profile", &check_reply_profile);
+ init_profile ("add_create_object_profile", &add_create_object_profile);
+ init_profile ("check_timeout_profile", &check_timeout_profile);
+ init_profile ("adjust_play_window_profile",&adjust_play_window_profile);
+ init_profile ("fgets_profile",&fgets_profile);
+ init_profile ("read_line_profile",&read_line_profile);
+ init_profile ("read_trace_profile",&read_trace_profile);
+}
+
+static char trace_file[256]="anon-lair62-011130-1200.txt";
+int print_memory_usage()
+{
+ printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map));
+ printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab));
+ printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace));
+ printf("size of CREATE3args %d\n", sizeof( CREATE3args));
+ printf("size of MKDIR3args %d\n", sizeof( MKDIR3args));
+ printf("size of READ3args %d\n", sizeof( READ3args));
+ printf("size of WRITE3args %d\n", sizeof( WRITE3args));
+ printf("size of RENAME3args %d\n", sizeof( RENAME3args));
+ printf("size of GETATTR3args %d\n", sizeof( GETATTR3args));
+ printf("size of SETATTR3args %d\n", sizeof( SETATTR3args));
+ printf("size of LINK3args %d\n", sizeof( LINK3args));
+ printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args));
+ printf("size of MKNOD3args %d\n", sizeof( MKNOD3args));
+ printf("size of RMDIR3args %d\n", sizeof( RMDIR3args));
+ printf("size of REMOVE3args %d\n", sizeof( REMOVE3args));
+ printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args));
+ printf("size of READDIR3args %d\n", sizeof( READDIR3args));
+ printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args));
+ printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args));
+ printf("size of FSINFO3args %d\n", sizeof( FSINFO3args));
+ printf("size of COMMIT3args %d\n", sizeof( COMMIT3args));
+ printf("size of ACCESS3args %d\n", sizeof( ACCESS3args));
+ printf("size of READLINK3args %d\n", sizeof( READLINK3args));
+
+
+}
+
+int io_thread ()
+{
+/* number of seconds the I/O thread pauses after each time trying to read the requests */
+#define IO_THREAD_PAUSE_TIME 1
+
+ int i;
+ int j = 0;
+
+ disk_io_status = read_trace ();
+ while (disk_io_status == TRACE_BUF_FULL) {
+
+ usleep (10000);
+ if ((j++%200)==0) {
+ printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/10);
+ }
+
+ disk_io_status = read_trace ();
+ //printf ("io_thread, after read_trace, disk_index %d\n", disk_index);
+
+#ifdef SEQUEN_READ
+ for (i=0; i<SEQUEN_READ_NUM; i++) {
+ add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size));
+ }
+#endif
+ //printf ("***************** IO THREAD SLEEP 1 s\n");
+ //print_cyclic_buffers();
+ //pthread_yield();
+ }
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+}
+
+int execute_thread()
+{
+ int i;
+ struct timeval playstart_time, playend_time;
+
+ sleep (10); /* first let io_thread to run for a while */
+ init_time_offset();
+ printf ("trace_play\n");
+
+ gettimeofday(&playstart_time, NULL);
+
+ trace_play ();
+
+ gettimeofday(&playend_time, NULL);
+
+ {
+ int usec, sec;
+ sec = playend_time.tv_sec - playstart_time.tv_sec;
+ usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec);
+ sec = usec / 1000000;
+ usec = usec % 1000000;
+ printf("Total play time: %d sec %d usec\n", sec, usec);
+ }
+
+ print_result();
+}
+
+int init_thread ()
+{
+ pthread_attr_t attr;
+ int arg;
+ int ret = 0;
+ pthread_t io_thread_var;
+ pthread_t execute_thread_var;
+
+ ret = pthread_attr_init (&attr);
+ if (ret!=0) {
+ perror("pthread_attr_init attr");
+ return ret;
+ }
+ ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+
+/*
+ ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+*/
+}
+
+void init_buffers()
+{
+ CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES);
+ CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE);
+ CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE);
+}
+
+int main(int argc, char ** argv)
+{
+ extern char * optarg;
+ int i;
+ struct timeval in={1000000, 100};
+
+ init_profile_variables();
+ if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) {
+ print_usage(0, argc, argv);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_write")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_write(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_trace")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_trace(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_aging")) {
+ if (argc!=3) {
+ print_usage(3, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ check_aging (trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_statistics")) {
+ if (argc!=3) {
+ print_usage(1, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ memset(fh_htable, 0, sizeof (fh_htable));
+ check_statistics (trace_file);
+ exit(0);
+ }
+
+ if (argc!=6) {
+ print_usage(2, argc, argv);
+ exit(0);
+ }
+ PLAY_SCALE = atoi (argv[4]);
+ RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000);
+
+ WARMUP_TIME = atoi (argv[5]);
+ RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000);
+
+ print_memory_usage();
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+ /*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+ //Real_uid = 513;
+ //Cur_gid = 513;
+
+ Nfs_timers = Nfs_udp_timers;
+
+ init_file_system ();
+ init_signal();
+ init_play (argv[1]);
+ //init_play ("capella:/p5/RFSFS");
+ init_fh_map();
+ read_fh_map (argv[3]);
+ //read_fh_map ("fh-path-map-play");
+ strcpy(trace_file, argv[2]);
+
+/* If ordered by TIMESTAMP,
+ * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <=
+ * dep_tab_index.head <= memory_trace_index.head
+ */
+
+ init_buffers();
+ //init_thread();
+ pthread_yield();
+ execute_thread();
+}
+
+void init_ops (void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/* Set up the signal handlers for all signals */
+void init_signal()
+{
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+#else
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ // RFS (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+#endif
+}
+
+void
+init_play (
+ char * mount_point) /* Mount point for remote FS */
+{
+ char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */
+ CLIENT * mount_client_ptr; /* Mount client handle */
+
+ if (!rfs_debug);
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ sfs_Myname = namebuf;
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, mount_point, mount_client_ptr);
+
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ init_counters();
+}
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+
+#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2)
+#define SAFE_BYTES 1000
+#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES)
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+/*
+ if (!(strlen(line_buf[i])>80)) {
+ printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT (strlen(line_buf[i])>80);
+ }
+ if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)))
+ printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH));
+*/
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+#define OPS_FLAG_INC 0
+#define OPS_FLAG_PRINT 1
+int read_write_fh_statistics (int flag, char * buf, int timestamp)
+{
+ static FILE * fp = NULL;
+ char * p;
+ char c;
+ if (!fp) {
+ fp = fopen ("read_write_fh.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (buf, "fh");
+ RFS_ASSERT (p);
+ c = p[40+3];
+ p[40+3]=0;
+ fprintf(fp, "%s\n", p+3+24);
+ p[40+3]=c;
+ };
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+int write_statistics(int flag, char * buf, char * reply_buf, int trace_status)
+{
+ static FILE * fp = NULL;
+ int pre_size, size, count;
+ char * p = reply_buf;
+ if (!fp) {
+ fp = fopen ("write.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ }
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ int timestamp = (int) reply_buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+void ops_statistics (int ops_flag, int proc, int timestamp)
+{
+ static FILE * fp = NULL;
+ static struct {
+ int count;
+ int count_K;
+ int update_flag;
+ char name[32];
+ } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"},
+ {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"},
+ {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"},
+ {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"},
+ {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"},
+ {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}};
+
+ if (ops_flag == OPS_FLAG_INC) {
+ RFS_ASSERT (proc>=0 && proc<NOPS);
+ ops[proc].count ++;
+ if (ops[proc].count == 1000) {
+ ops[proc].count_K ++;
+ ops[proc].count = 0;
+ }
+ }
+ if (ops_flag == OPS_FLAG_PRINT) {
+ int i;
+ int update_K=0, update=0, total_K=0, total=0;
+ float f1, f2;
+ int disk_index = proc;
+
+ if (!fp) {
+ fp = fopen ("mix.output", "w");
+ RFS_ASSERT (fp);
+ }
+ for (i=0; i<NOPS; i++) {
+ total_K += ops[i].count_K;
+ total += ops[i].count;
+ if (ops[i].update_flag) {
+ update_K += ops[i].count_K;
+ update += ops[i].count;
+ }
+ }
+ update_K += update/1000;
+ update = update%1000;
+ total_K += total/1000;
+ total = total%1000;
+
+ f1 = total_K;
+ f1 = f1*1000+total;
+ for (i=0; i<NOPS; i++) {
+ f2 = ops[i].count_K;
+ f2 = f2*1000+ops[i].count;
+ fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ }
+ f2 = update_K;
+ f2 = f2*1000+update;
+ fprintf (fp, " total %8d,%03d\n", total_K, total);
+ fprintf (stderr, " total %8d,%03d\n", total_K, total);
+ fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+
+void truncate_statistics (int flag, int proc, char * buf, char * reply_buf)
+{
+#define TRACE_FH_SIZE2 16
+#define BLOCK_SIZE 4096
+ static int no_size_num = 0;
+ static int have_size_num = 0;
+ static int equal_size_num = 0;
+ static int first_size_num = 0;
+ static int truncate_num = 0;
+ static int truncate_size = 0;
+ static int truncate_KB = 0;
+ static int truncate_block_num = 0;
+ static int padding_num = 0;
+ static int padding_KB = 0;
+ static int padding_size = 0;
+ static FILE * fp = NULL;
+ char * p;
+ char * trace_fh;
+ int pre_size, size, count;
+ struct generic_entry * ent;
+
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = proc;
+ int timestamp = (int)buf;
+ if (!fp) {
+ fp = fopen ("truncate.output", "w");
+ RFS_ASSERT (fp);
+ }
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ return;
+ }
+
+ p = strstr (&buf[TRACE_MSGID_POS], "fh");
+ RFS_ASSERT (p);
+ p+=3;
+ trace_fh = p;
+
+ if (proc==SETATTR) {
+ p = strstr (buf, " size ");
+ } else {
+ RFS_ASSERT (proc==WRITE);
+ p = strstr (reply_buf, " ftype 1 ");
+ RFS_ASSERT (p);
+ p = strstr (p, " size ");
+ RFS_ASSERT (p);
+ if (strstr(p, " ftype 1 ")) {
+ fprintf (fp, "#### %s%s\n", buf, reply_buf);
+ fprintf (stderr, "#### %s%s\n", buf, reply_buf);
+ }
+ RFS_ASSERT (!strstr(p, " ftype 1 "));
+ }
+ if (!p) {
+ no_size_num ++;
+ return;
+ }
+ have_size_num ++;
+
+ sscanf (p, " size %x", &size);
+ if (size <0 || size > 2000000000) {
+ fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ }
+
+ RFS_ASSERT (size >=0 && size <2000000000);
+ ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE);
+ if (ent) {
+ if (ent->key3 != size) {
+ if (proc==SETATTR) {
+ //printf ("%s\n", buf);
+ //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ if (ent->key3 > size) {
+ truncate_num ++;
+ truncate_size += ent->key3 - size;
+ truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE;
+ if (size!=0) {
+ //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE;
+ }
+ if (truncate_size > 1000000000) {
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ }
+ } else {
+ padding_num ++;
+ //printf ("%s\n", buf);
+ //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ padding_size += size - ent->key3;
+ if (padding_size > 1000000000) {
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ }
+ }
+ }
+ ent->key3 = size;
+ }else
+ equal_size_num++;
+ } else {
+ generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE);
+ first_size_num ++;
+ }
+};
+
+int get_timestamp (char * buf)
+{
+ char str[128];
+ int ret;
+ strncpy(str, buf, 100);
+ RFS_ASSERT (str[10]=='.');
+ str[10]=0;
+ ret = atoi(str);
+ RFS_ASSERT (ret >1000000000 && ret <2000000000);
+ return ret;
+}
+
+int check_aging (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ switch (proc) {
+ int off, count, size;
+ char * t;
+ case CREATE: printf("%s\n", "create"); break;
+ case MKDIR: printf("%s\n", "mkdir"); break;
+ case REMOVE: printf("%s\n", "remove"); break;
+ case RMDIR: printf("%s\n", "rmdir"); break;
+ case WRITE:
+ t = buf;
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ sscanf (t, "%x", &off);
+ RFS_ASSERT (off>=0 && off<0x7FFFFFFF)
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &count);
+ RFS_ASSERT (count <= 32768);
+ printf("%s off %x count %x\n", "write", off, count);
+ //printf("%s count %x\n", "write", count);
+ break;
+ case SETATTR:
+ t = strstr (buf, " size ");
+ if (t) {
+ sscanf (t, " size %x", &size);
+ printf ("%s size %x\n", "setattr", size);
+ }
+ }
+ if ((disk_index%10000)==0) {
+ fprintf(stderr, "%d disk trace passed\n", disk_index);
+ }
+ };
+
+ fprintf(stderr, "%d disk trace parsed\n", disk_index);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, -1);
+}
+
+
+int check_statistics (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+ static int last_timestamp_sec = -1;
+ int timestamp_sec;
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ timestamp_sec = get_timestamp(buf);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ if (proc!= WRITE && proc!=SETATTR && proc!=READ) {
+ continue;
+ }
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ if (proc==READ || proc==WRITE)
+ read_write_fh_statistics (OPS_FLAG_INC, buf, 0);
+ if (proc == WRITE)
+ write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status);
+ if (proc==WRITE || proc==SETATTR)
+ truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf);
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ //printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if *
+ if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ if (last_timestamp_sec == -1) {
+ last_timestamp_sec = timestamp_sec;
+ } else if (timestamp_sec - last_timestamp_sec >=3600) {
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+ last_timestamp_sec = timestamp_sec;
+ }
+/*
+ if ((memory_trace_size%10000)==0) {
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, -1);
+ truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL);
+ }
+*/
+ };
+
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+}
+
+
+/* This routine output all the requests, together with their replies */
+int pair_trace (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+ FILE * outputfp;
+
+ outputfp = fopen ("pair.output", "w");
+ RFS_ASSERT (outputfp);
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+ fprintf (outputfp, "%s\n", buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ fprintf(outputfp, "%s", reply_buf);
+ trace_status = find_reply_status(reply_buf);
+ if (debug)
+ fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf));
+ };
+ }
+ }
+
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ fprintf (stderr, "%s no reply\n", buf);
+ fprintf(outputfp, "missing_reply\n");
+ missing_reply_num ++;
+ }
+
+ if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) {
+ fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+
+ memory_trace_size ++;
+
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+/* This routine output all the write requests, together with their replies. It is used for
+ * analysis of write requests: appended bytes, overwrite bytes etc
+ */
+int pair_write (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int pair_write_debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ if (!strstr(buf, "write"))
+ continue;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (pair_write_debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ /* store the request to memory */
+ //strcpy (memory_trace[memory_trace_size].line, buf);
+ //memory_trace[memory_trace_size].disk_index = disk_index;
+
+ /* find and store the reply status and reply fhandle to memory */
+ //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS;
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (pair_write_debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ int pre_size, size, count;
+ //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf);
+ if (pair_write_debug)
+ printf("reply found trace_status %d\n", find_reply_status(reply_buf));
+ //break;
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ break;
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if (missing_reply_num > memory_trace_size/10) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ /*
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ */
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+
+int read_trace ()
+{
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int debug = 0;
+ memory_trace_ent_t * ent=NULL;
+
+ start_profile (&read_trace_profile);
+
+ while (!CYCLIC_FULL(memory_trace_index)) {
+ if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS))
+ buf = reply_buf;
+ if ((buf=read_line(++disk_index))==NULL) {
+END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num );
+ fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+ end_profile (&read_trace_profile);
+ return TRACE_FILE_END;
+ }
+
+#ifdef notdef
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ if (disk_index==1) {
+ trace_timestamp1 = get_timestamp (buf);
+ } else
+ trace_timestamp2 = get_timestamp (buf);
+#endif
+ /* store the request to memory */
+ ent = &(memory_trace[memory_trace_index.head]);
+ strcpy (ent->line, buf);
+ ent->disk_index = disk_index;
+
+ if (MAX_COMMAND_REPLY_DISTANCE ==1) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ reply_buf=read_line(++disk_index);
+ RFS_ASSERT (reply_buf);
+ if (!strcmp(reply_buf, "missing_reply\n")) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ ent->trace_status = find_reply_status(reply_buf);
+ }
+ };
+
+ if (ent->trace_status == NFS3ERR_RFS_MISS)
+ missing_reply_num ++;
+
+ if (MAX_COMMAND_REPLY_DISTANCE > 1) {
+ if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index);
+ exit (0);
+ }
+ }
+
+ /* find and store the reply trace fhandle for create-class requests */
+ if (ent->trace_status==NFS3_OK) {
+ if (strstr(buf, "create") || strstr(buf, "mkdir")
+ || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2'))
+ || strstr(buf, "mknod") ) {
+ p = find_reply_trace_fh(reply_buf);
+ memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE);
+ } else
+ memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE);
+ }
+
+ add_to_dep_tab(memory_trace_index.head);
+
+ if (((disk_index+1)%20000)==0) {
+ fprintf(stderr, "%d disk trace parsed \n", disk_index+1);
+ };
+ };
+
+ end_profile (&read_trace_profile);
+ return TRACE_BUF_FULL;
+}
+#else /* not defined REDUCE_MEMORY_TRACE_SIZE */
+int read_trace ()
+{
+ FILE * fp;
+ char buf[1024];
+ // char * t=buf;
+ int disk_index=0;
+
+ fp = fopen(trace_file, "r");
+ RFS_ASSERT (fp!=NULL);
+ while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) {
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("strlen(buf) %d buf %s \n", strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+
+ /* store the request to memory */
+ strcpy (memory_trace[memory_trace_size].line, buf);
+ memory_trace[memory_trace_size].disk_index = disk_index;
+ memory_trace_size ++;
+
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ if ((disk_index%100000)==0)
+ fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size);
+ disk_index ++;
+ }
+
+ fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size );
+}
+#endif
+
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline int disk_index_to_memory_index (int disk_index)
+{
+ static int memory_index = 0;
+
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index);
+ RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index);
+ if (disk_index > memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index < disk_index) {
+ memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size);
+ }
+ };
+ if (disk_index < memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index > disk_index) {
+ memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size);
+ }
+ };
+
+ RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index);
+ return memory_index;
+}
+#else
+#define disk_index_to_memory_index(disk_index) disk_index
+#endif
+
+#define get_line_by_disk_index(disk_index) \
+ memory_trace[disk_index_to_memory_index(disk_index)].line
+
+inline char * find_reply_line (char * command_line, int cur_disk_index)
+{
+ int i;
+ char * line;
+ char * p;
+ int request_memory_index = disk_index_to_memory_index (cur_disk_index);
+ for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&line[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS])))
+ return line;
+ }
+ }
+ return NULL;
+}
+
+inline int find_reply_status (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */
+ if (strstr(p, "OK"))
+ return NFS3_OK;
+ if (strstr(p, "lookup 2"))
+ return 0x2;
+ if (strstr(p, "create d"))
+ return 0xd;
+ if (strstr(p, "setattr 1"))
+ return 0x1;
+ if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */
+ return 0x2712;
+ if (strstr(p, "lookup d"))
+ return 0xd;
+ if (strstr(p, "read d"))
+ return 0xd;
+ if (strstr(p, "write d"))
+ return 0xd;
+ if (strstr(p, "write 46"))
+ return 0x46;
+ if (strstr(p, "getattr 46"))
+ return 0x46;
+ if (strstr(p, "mkdir d"))
+ return 0xd;
+ printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n");
+}
+
+inline int find_reply_status_old (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ if (!strstr(line, "OK")) {
+ p=strstr(line, " 6 read ");
+ if (p) {
+ p+=strlen(" 6 read ");
+ } else {
+ p = strstr (line, "status=XXX");
+ RFS_ASSERT (p);
+ p--;
+ RFS_ASSERT (*p==' ');
+ while (*p==' ')
+ p--;
+ while (*p!=' ') {
+ p--;
+ }
+ p++;
+ }
+ sscanf (p, "%x", &i);
+ if ((i<=0) || (i>10000))
+ printf("line %s\n", line);
+ RFS_ASSERT (i>0 && i<10009);
+ }
+ return i;
+}
+
+inline char * find_reply_trace_fh (char * line)
+{
+ char * p;
+ p = strstr (line, "OK fh");
+ if (!p)
+ printf ("find_reply_trace_fh line %s\n", line);
+ RFS_ASSERT (p);
+ return p+6;
+}
+
+#ifndef NO_DEPENDENCY_TABLE
+inline int disk_index_to_dep_index(int cur_dep_index, int disk_index)
+{
+ int i;
+ for (i=cur_dep_index; i>min_dep_index; i--) {
+ if (dep_tab[i].disk_index == disk_index)
+ return i;
+ }
+ RFS_ASSERT (0);
+}
+#endif
+
+inline int is_play_candidate (int dep_index)
+{
+ int proc = dep_tab[dep_index].proc;
+ int status = dep_tab[dep_index].status;
+ int trace_status = dep_tab[dep_index].trace_status;
+
+#ifndef TAKE_CARE_CREATE_MODE_BY_DAN
+ /* for a failed create in trace, trace_replay just ignore many time the trace create fail
+ * due to access control, but trace_play will success because our access control
+ * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests
+ */
+ if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) {
+ if (dependency_debug)
+ printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+ failed_create_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_OTHER_FAILED_COMMAND
+ if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) ||
+ ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){
+ if (dependency_debug)
+ printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+
+ failed_other_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */
+ skipped_readlink_command_num ++;
+ return FALSE;
+ }
+#endif
+/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */
+#ifndef TAKE_CARE_CUSTOM_COMMAND
+ /* this line has a file handle which should belong to discard but it is not
+ * the file handle directly appears as parent directory in a lookup request
+ * the return value is NOENT, the parent directory should have been initialized
+ * but the initialization code just ignored all lookup request which didn't success
+ * including NOENT even though the parent directory is still valid.
+ */
+/*
+ if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402))
+ && !(strcmp(trace_file, "anon-lair62-011130-1100.txt"))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+*/
+ if (( ((dep_tab[dep_index].disk_index==423727) || (0))
+ && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt")))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+ /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */
+ if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_FSSTAT_COMMAND
+ /* the file handle used in this command is not processed properly by pre-processing */
+ if (proc==FSSTAT) {
+ char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ fh_map_t * fh = lookup_fh (trace_fh);
+ if (!fh) {
+ skipped_fsstat_command_num++;
+ return FALSE;
+ }
+ }
+#endif
+ return TRUE;
+}
+
+inline int is_dir_op (int proc)
+{
+ switch (proc) {
+ case MKDIR:
+ case CREATE:
+ case LINK:
+ case SYMLINK:
+ case MKNOD:
+ case REMOVE:
+ case RMDIR:
+ case RENAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+inline int is_create_op (int proc)
+{
+ if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline int is_delete_op (int proc)
+{
+ if (proc==REMOVE || proc==RMDIR || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+static inline char * find_lead_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh");
+ RFS_ASSERT (p);
+ p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+inline char * find_another_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh2");
+ RFS_ASSERT (p);
+ p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+/* return the index of next request in dep_tab.
+ * Return -1 if there is no suitable request to send
+ */
+inline int get_nextop(void)
+{
+ int i,j, k;
+ int * t;
+ static int dep_index = -2;
+ char * line;
+ char * p;
+#define INIT_MIN_WAIT_VALUE -999
+ static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE;
+ int proc;
+ int flag;
+
+ if (min_wait_fhandle_dep_index == -999)
+ min_wait_fhandle_dep_index = dep_window_index.head;
+
+ for (i=0; i<CYCLIC_NUM(dep_window_index); i++) {
+ dep_index = (dep_window_index.tail+i) % dep_window_index.size;
+
+ proc = dep_tab[dep_index].proc;
+ flag = dep_tab[dep_index].flag;
+
+ if (dependency_debug)
+ printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+#ifdef NO_DEPENDENCY_TABLE
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT) {
+ if (is_play_candidate(dep_index)==TRUE) {
+ /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle
+ * used in the request */
+ if (proc==LINK || proc==RENAME) {
+ dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = 0;
+ } else {
+ dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = (fh_map_t *)1;
+ };
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+#ifdef TIME_PLAY
+ dep_tab[dep_index].skip_sec = skip_sec;
+#endif
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index);
+ } else {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) {
+
+ if (!dep_tab[dep_index].fh)
+ dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh);
+ if (!dep_tab[dep_index].fh_2)
+ dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2);
+
+ /* need to wait for file handle */
+ if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) {
+ if (dependency_debug)
+ printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE;
+ sfs_gettime (&dep_tab[dep_index].start);
+ if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index))
+ min_wait_fhandle_dep_index = dep_index;
+ } else {
+ struct ladtime tmp;
+ if (dep_index==dep_window_index.tail) {
+ if (!profile_debug)
+ printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ fh_path_map_err_num ++;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ sfs_gettime (&tmp);
+ SUBTIME (tmp, dep_tab[dep_index].start);
+#define DEPENDENCY_TIMEOUT 5
+#ifdef TIME_PLAY
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec));
+#else
+ if (tmp.sec >= DEPENDENCY_TIMEOUT) {
+ printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index,
+ dep_tab[dep_index].flag, dep_tab[dep_index].disk_index,
+ dep_tab[dep_index].line);
+ }
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT );
+#endif
+ }
+ continue;
+ }
+
+ /* file handle ready, adjust_min_wait_fhandle_dep_index */
+ if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) {
+ if (dep_index == min_wait_fhandle_dep_index) {
+ min_wait_fhandle_dep_index = dep_window_index.head;
+ for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) {
+ if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) {
+ min_wait_fhandle_dep_index = j;
+ break;
+ }
+ }
+ }
+ }
+ if (dependency_debug)
+ printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY;
+
+ /* the normal file operation can be executed now */
+ if (!is_dir_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ if (dependency_debug)
+ printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index);
+ /* the directory operation need to lock the directory first */
+ if (dep_tab[dep_index].fh->lock) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) {
+ int j = dep_tab[dep_index].fh - fh_map;
+ if (dependency_debug) {
+ printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock);
+ printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) {
+ if (dependency_debug)
+ printf ("continue to wait for directory lock\n");
+ continue;
+ }
+ if (dependency_debug)
+ printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j);
+ dep_tab[dep_index].fh->lock = 1;
+ if (proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 1;
+
+ /* the non-delete directory operation can proceed now */
+ if (!is_delete_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ /* the delete operation can proceed if nobody ahead is waiting for fhandle */
+ /* probably this condition is not strong enough */
+// if ((min_wait_fhandle_dep_index<dep_index) ) {
+ if (dep_index!=dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE;
+ continue;
+ }
+ dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY;
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) {
+// if (min_wait_fhandle_dep_index > dep_index) {
+ if (dep_index==dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ }
+#else /*NO_DEPENDENCY_TABLE undefined */
+ /* this part of code will be invalid after CYCLIC buffer design */
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT){
+ for (j=0, t=&(dep_tab[dep_index].dep_ops[0]);
+ (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0);
+ j++, t++) {
+ if (*t !=-1) {
+ if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) {
+ /* The depended request has been finished */
+ *t = -1;
+ dep_tab[dep_index].cur_dep_num --;
+ }
+ }
+ }
+
+ if (dep_tab[dep_index].cur_dep_num == 0) {
+ return dep_index;
+ }
+ }
+#endif
+ }
+
+ if (dependency_debug)
+ printf ("get_nexop return -1\n");
+ return -1;
+}
+
+int check_timeout(void)
+{
+ static int biod_index = 0;
+ int i;
+ int dep_index; /* index into dep_tab */
+ int proc;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime timeout;
+
+ sfs_gettime (¤t);
+
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (biod_reqp[biod_index].in_use==TRUE) {
+ timeout = biod_reqp[biod_index].timeout;
+ if ((current.sec>timeout.sec) ||
+ ((current.sec==timeout.sec) && (current.usec>timeout.usec))) {
+
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+ op_ptr->results.timeout_calls++;
+ Ops[TOTAL].results.timeout_calls++;
+
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT);
+
+ if (is_create_op(proc)) {
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+ printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ }
+ //RFS_ASSERT (!is_create_op(proc));
+
+ //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec);
+ }
+ }
+ }
+}
+
+/* Allocate a biod_req entry to send and receive request dep_tab[dep_index]
+ * build the cross reference between dep_tab entry and biod_req entry
+ */
+struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (!biod_reqp[biod_index].in_use) {
+ biod_reqp[biod_index].in_use = 1;
+ biod_reqp[biod_index].dep_tab_index = dep_index;
+ dep_tab[dep_index].biod_req_index = biod_index;
+ num_out_reqs++;
+ return &(biod_reqp[biod_index]);
+ }
+ }
+ return NULL;
+}
+
+/* Return index into biod_reqp
+ * return -1 upon failure
+ */
+int lookup_biod_req (int xid)
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ /* give a NULL as timeout pointer may cause indefinitely block */
+ if (biod_reqp[biod_index].xid == xid) {
+ return biod_index;
+ }
+ }
+ return -1;
+}
+
+extern struct ladtime test_start;
+void init_time_offset(void)
+{
+ struct ladtime tmp1;
+ struct ladtime tmp2;
+
+ test_start.sec = 0;
+ test_start.usec = 0;
+ sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */
+#ifdef SPEED_UP
+ DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */
+#endif
+
+ tmp2 = trace_starttime; /* tmp2 = trace_starttime */
+ SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */
+ time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void init_dep_tab_entry (int dep_index)
+{
+ char * line;
+ int version;
+ int nfsproc;
+ int msgid;
+
+ //line = get_line_by_disk_index (dep_tab[dep_index].disk_index);
+ line = dep_tab[dep_index].line;
+ sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec));
+ sscanf (&line[39], "%x %x", &msgid, &nfsproc);
+ if (line[TRACE_VERSION_POS]=='2') {
+ dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc];
+ RFS_ASSERT (nfsproc <18);
+ } else {
+ /* This is for debug purpose */
+ if (line[TRACE_VERSION_POS] !='3') {
+ fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line);
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1);
+ if (!line)
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2);
+ RFS_ASSERT (line);
+ fprintf(stderr, "previousline %s\n", line);
+ }
+ RFS_ASSERT (line[TRACE_VERSION_POS] =='3');
+ if (nfsproc >= NFS3_PROCEDURE_COUNT) {
+ fprintf(stderr, "proc %d line %s\n", nfsproc, line);
+
+ }
+ RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT);
+ dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc];
+ }
+ RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS);
+ dep_tab[dep_index].flag = DEP_FLAG_INIT;
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ dep_tab[dep_index].reply_line = find_reply_line (line, dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].reply_line == NULL) {
+ //printf ("disk[%d] can not find the reply line, assume trace_status OK\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].trace_status = NFS3ERR_RFS_MISS;
+ missing_reply_num ++;
+ } else
+ dep_tab[dep_index].trace_status = find_reply_status (dep_tab[dep_index].reply_line);
+#endif
+}
+
+void adjust_play_window (int flag, int * poll_timeout)
+{
+ struct ladtime max_window_time;
+ static struct ladtime max_poll_time = {0, 2000, 0};
+ struct ladtime t;
+ int i;
+ char * line;
+ cyclic_index_t old_dep_window_index = dep_window_index;
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, begin\n");
+#endif
+
+ while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) {
+#ifdef notdef
+ //CYCLIC_PRINT (memory_trace_index);
+ //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index);
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+ printf("%d is done\n", dep_window_index.tail);
+#endif
+ CYCLIC_MOVE_TAIL(dep_tab_index);
+ CYCLIC_MOVE_TAIL(dep_window_index);
+
+#ifdef notdef
+ CYCLIC_PRINT (dep_tab_index);
+ CYCLIC_PRINT (dep_window_index);
+
+ if (! (dep_tab_index.tail == dep_window_index.tail)) {
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+ };
+ RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail);
+#endif
+
+ if (!CYCLIC_EMPTY(dep_tab_index)) {
+#ifdef notdef
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) {
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n",
+ dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index,
+ dep_tab[dep_tab_index.tail].memory_index);
+ }
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+#endif
+ CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index);
+ //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail);
+ } else {
+ // CYCLIC_MOVE_TAIL (memory_trace_index);
+ }
+ }
+
+ while (CYCLIC_EMPTY(dep_tab_index)) {
+
+ if (disk_io_status == TRACE_FILE_END)
+ return;
+ else {
+ //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n");
+ //print_cyclic_buffers();
+ pthread_yield();
+ //usleep (1000);
+ }
+ }
+
+ /* max_trace_window_time = current *|/ SCALE + trace_starttime */
+ sfs_gettime (¤t);
+
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+ MULTIME (current, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ DIVTIME (current, PLAY_SCALE);
+#endif
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+
+ /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */
+ if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) {
+ //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index);
+ //printf (".");
+ can_not_catch_speed_num ++;
+ }
+ //RFS_ASSERT (dep_window_max < min_dep_index+MAX_PLAY_WINDOW);
+#else
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+ while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) &&
+ (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) {
+ CYCLIC_MOVE_HEAD(dep_window_index);
+ }
+#endif
+
+ if (flag == BUSY)
+ *poll_timeout = 0;
+ else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) {
+ *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */
+ } else {
+#ifdef TIME_PLAY
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec;
+ if (adjust_play_window_debug>=2)
+ printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec);
+
+ SUBTIME (tmp, max_window_time);
+#ifdef SPEED_UP
+ DIVTIME (tmp, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp, PLAY_SCALE);
+#endif
+/*
+ tmp1 = tmp;
+
+ if (tmp.sec > max_poll_time.sec) {
+
+ if (rfs_debug)
+ printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec);
+ printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec);
+ SUBTIME (tmp, max_poll_time);
+ tmp.usec = 0;
+ skip_sec += tmp.sec;
+ SUBTIME (test_start, tmp);
+ tmp = max_poll_time;
+ }
+*/
+
+ //RFS_ASSERT ((tmp.sec < 1000));
+ if (tmp.sec > 1000)
+ tmp.sec = 1000;
+ if ((tmp.sec ==0) && (tmp.usec==0)) {
+ *poll_timeout = 0;
+ } else
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+#else
+ /*
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec;
+ tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec;
+ tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec;
+ SUBTIME (tmp, tmp1);
+ RFS_ASSERT ((tmp.sec < 1000));
+ RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0)));
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+ */
+
+ *poll_timeout = 100000;
+#endif
+ }
+ if (rfs_debug)
+ printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n",
+ flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head,
+ dep_window_index.head, *poll_timeout);
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, end\n\n");
+#endif
+ //CYCLIC_ASSERT(4);
+}
+
+/* poll for usecs and receive, after receive one reply,
+ * return index in biod_reqp of the corresponding request
+ */
+int poll_and_get_reply (int usecs)
+{
+ int biod_index = -1;
+ int xid;
+ int error;
+ struct timeval zero_time = {0, 0}; /* Immediately return */
+
+ do {
+ error = biod_poll_wait (NFS_client, usecs);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (rfs_debug) {
+ (void) fprintf(stderr, "biod_poll_wait error\n");
+ perror ("");
+ (void) fflush(stderr);
+ }
+ break;
+ case 0:
+ break;
+ default:
+#ifdef UDP
+ error = get_areply_udp (NFS_client, &xid, &zero_time);
+ // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */
+ // RFS_ASSERT (error!= RPC_CANTRECV);
+ RFS_ASSERT (error == RPC_SUCCESS);
+
+ biod_index = lookup_biod_req (xid);
+ sfs_gettime (&(biod_reqp[biod_index].stop));
+#else
+ RFS_ASSERT (0);
+#endif
+ }
+ } while (0);
+ return biod_index;
+}
+
+void print_result(void)
+{
+ int i, j;
+ struct ladtime t;
+ int dep_index;
+ int avg_msecs;
+ unsigned long long tmp;
+ int avg_usecs;
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stdout, "trace play result:\n");
+ (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n");
+ for (i=0; i<NOPS+1; i++) {
+ if (Ops[i].results.good_calls==0) {
+ avg_msecs = 0;
+ avg_usecs = 0;
+ } else {
+ tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec;
+ avg_msecs = 0;
+ avg_usecs = tmp/Ops[i].results.good_calls;
+/*
+ avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls;
+ avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls;
+*/
+ }
+ (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n",
+ Ops[i].name,
+ (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls,
+ Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls,
+ Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs);
+ }
+ (void) fflush (stdout);
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ RFS_ASSERT (read_data_owe_GB==0);
+ printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times);
+ printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times);
+ printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num);
+ printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n",
+ failed_create_command_num,
+ failed_other_command_num,
+ skipped_readlink_command_num,
+ skipped_custom_command_num,
+ fh_path_map_err_num,
+ skipped_fsstat_command_num,
+ missing_reply_num,
+ rename_rmdir_noent_reply_num,
+ rmdir_not_empty_reply_num,
+ loose_access_control_reply_num,
+ lookup_err_due_to_rename_num,
+ lookup_err_due_to_parallel_remove_num,
+ lookup_eaccess_enoent_mismatch_num,
+ read_io_err_num,
+ stale_fhandle_err_num,
+ abnormal_EEXIST_num,
+ abnormal_ENOENT_num,
+ proper_reply_num, run_stage_proper_reply_num);
+#endif
+
+ clnt_destroy(NFS_client);
+ biod_term();
+
+// print_dump(Client_num, Child_num);
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ int dummy = 0;
+
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+
+ if (biod_init(dummy, dummy) == -1) {
+ return(-1);
+ }
+
+ return(0);
+} /* init_rpc */
+
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.timeout_calls = 0; // RFS
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "Timeout");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "trace play usage");
+}
+extern void init_file_system (void)
+{
+ return;
+}
+
+void show_fhandle (nfs_fh3 * fhp)
+{
+ struct knfs_fh * kfhp = (struct knfs_fh *)fhp;
+
+ int dev;
+
+ if (quiet_flag)
+ return;
+
+ RFS_ASSERT (kfhp->fh_version == 1);
+ RFS_ASSERT (kfhp->fh_fsid_type == 0);
+ RFS_ASSERT (kfhp->fh_auth_type == 0);
+
+ dev = ntohs(kfhp->fh_dev_major);
+ dev = dev<<8;
+ dev = dev + ntohs(kfhp->fh_dev_minor);
+
+ /* kfhp->fh_dev_ino hold the inode number of export point of the mounted
+ * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted,
+ * then fh_dev_ino hold the inode number of t1, not t2
+ */
+
+ switch (kfhp->fh_fileid_type) {
+ case 0:
+ printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino);
+ break;
+ case 1:
+ printf("fh:type 1 %d %x dev %x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino);
+ break;
+ case 2:
+ printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino);
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+}
+
+nfs_fh3 zero_fhandle;
+int init_fh_map ()
+{
+ memset (fh_map, 0, sizeof (fh_map));
+ memset(fh_htable, 0, sizeof (fh_htable));
+ memset (&zero_fhandle, 0, sizeof(nfs_fh3));
+ printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000);
+ fh_i = 0;
+}
+
+int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh)
+{
+ char * old_trace_fh;
+
+ /* first lookup if the entry for fh is already in the table */
+ struct generic_entry * p;
+
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (p) {
+ RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL);
+ RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE);
+ fh_map[p->key3].flag = map_flag;
+ //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) {
+ int i;
+ printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh);
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (!strcmp(fh_map[p->key3].path, path));
+ /* It's possible that in fh-path-map, many trace_fh are corresponding to one path
+ * some of it may be the result of lookup after symlink, which is not handled
+ * properly as new created objects
+ */
+#ifdef TAKE_CARE_SYMBOLIC_LINK
+ RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3)));
+#endif
+ memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3));
+ if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE))
+ printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path);
+ return 0;
+ }
+
+ fh_map[fh_i].flag = map_flag;
+ fh_map[fh_i].lock = 0;
+ strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE);
+
+ RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE);
+ strcpy (fh_map [fh_i].path, path);
+ if (map_flag==FH_MAP_FLAG_COMPLETE)
+ memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3));
+ else
+ memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3));
+
+ if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) {
+ printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path);
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ //show_fhandle(play_fh);
+ } else
+ printf("null\n");
+ }
+
+/*
+ if (map_flag == FH_MAP_FLAG_DISCARD)
+ printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path);
+*/
+
+ generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE);
+
+ fh_i = (fh_i+1);
+ RFS_ASSERT (fh_i < FH_MAP_SIZE);
+
+ return 0;
+};
+
+inline fh_map_t * lookup_fh (char * trace_fh )
+{
+ struct generic_entry * p;
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (fh_map_debug==1)
+ printf ("lookup trace_fh %s\n", trace_fh);
+
+ if (p) {
+ if (fh_map_debug==1) {
+ printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path);
+ //show_fhandle(&fh_map[p->key3].play_fh);
+ }
+ RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ return (&(fh_map[p->key3]));
+ } else {
+ //printf ("lookup_fh %s not found\n", trace_fh);
+ if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) {
+ printf ("lookup not found trace_fh %s\n", trace_fh);
+ }
+ return NULL;
+ }
+ RFS_ASSERT (0);
+}
+
+int delete_fh (char * trace_fh, int fh_map_index)
+{
+ generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE);
+ return 0;
+};
+
+int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ static int i=0;
+
+ /* set up the arguments */
+ (void) memcpy((char *) &args.what.dir, (char *) parent,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+ sfs_gettime(&stop);
+
+ if (rpc_stat !=RPC_SUCCESS) {
+ printf("rpc_stat %d\n", rpc_stat);
+ perror("");
+ }
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3));
+ return (reply.status);
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+#define MAX_PATH_DEPTH 20
+ nfs_fh3 parents[MAX_PATH_DEPTH];
+ char * lookup_path_ptr[MAX_PATH_DEPTH];
+ char lookup_path [MAX_PLAY_PATH_SIZE];
+ int depth;
+ int new_dir_flag = 0;
+ int lineno = 0;
+
+ depth = 0;
+ memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr));
+ memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ strcpy(lookup_path, "/");
+ lookup_path_ptr[depth]=&lookup_path[0];
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+ lineno ++;
+ if (fh_i % 10000==0)
+ printf("%d fh_map entry read\n", fh_i);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+ if (fh_map_debug) {
+ printf("%d fgets return %s\n", fh_i, buf);
+ printf("depth %d lookup_path %s\n", depth, lookup_path);
+ }
+ //for (i=0; i<=depth; i++)
+ //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]);
+ //printf("\n");
+#ifdef COMPRESS_TRACE_FH
+ for (i=0; i<TRACE_FH_SIZE/8; i++) {
+ strncpy(intbuf, buf+i*8, 8);
+ sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now
+ }
+ trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */
+#else
+ memcpy(trace_fh, buf, TRACE_FH_SIZE);
+ trace_path = buf + TRACE_FH_SIZE +1;
+#endif
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */
+#endif
+
+#ifdef NO_DEPENDENCY_TABLE
+ if (!strncmp (trace_path, "DISCARD", 7) ||
+ !strncmp (trace_path, "LN", 2) ) {
+ map_flag = FH_MAP_FLAG_DISCARD;
+ add_fh (map_flag, buf, trace_path, 0);
+ continue;
+ }
+#endif
+
+ p = trace_path+strlen(trace_path)-2;
+ while (*p!='/')
+ p--;
+ p++;
+ //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1);
+ //RFS_ASSERT (p>trace_path);
+
+ if (strncmp(lookup_path, trace_path, p-trace_path)) {
+ printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path);
+ }
+ RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path));
+ //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */
+ while (strlen(lookup_path)>p-trace_path && depth>0) {
+ //printf("depth--\n");
+ if (depth<=0)
+ printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth);
+ RFS_ASSERT (depth>0);
+ *lookup_path_ptr[depth]=0;
+ lookup_path_ptr[depth]=0;
+ depth--;
+ }
+ RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0));
+
+
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ if (buf[TRACE_FH_SIZE*2+1]=='Y') {
+ map_flag = FH_MAP_FLAG_COMPLETE;
+ } else {
+ map_flag = FH_MAP_FLAG_PARTIAL;
+ RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N');
+ }
+#else
+ map_flag = FH_MAP_FLAG_COMPLETE;
+#endif
+ if ((*(p+strlen(p)-1))=='/') {
+ *(p+strlen(p)-1)=0;
+ new_dir_flag = 1;
+ } else
+ new_dir_flag = 0;
+
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]);
+ if (ret!=NFS3_OK) {
+ printf ("lineno %d %s\n", lineno, buf);
+ }
+ RFS_ASSERT (ret == NFS3_OK);
+ add_fh (map_flag, buf, trace_path, &parents[depth+1]);
+ } else
+ add_fh (map_flag, buf, trace_path, 0);
+
+ if (new_dir_flag) {
+ /* the new fhandle is of a directory */
+ lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path);
+ strcat (lookup_path, p);
+ strcat (lookup_path, "/");
+
+ //printf("depth++\n");
+ depth++;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (fh_map_debug) {
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+
+ fprintf(stderr, "total %d requests \n", i);
+ }
+}
+
+int f()
+{
+ return 1;
+}
+
+inline free_biod_req (int biod_index)
+{
+ RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE);
+ biod_reqp[biod_index].in_use = FALSE;
+ num_out_reqs --;
+}
+
+inline void finish_request (int biod_index, int dep_index, int status)
+{
+ /* the ending operation, same as when a request time out */
+
+ dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */
+ free_biod_req (biod_index);
+
+ dep_tab[dep_index].status = status;
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = -dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ if (is_dir_op(dep_tab[dep_index].proc)) {
+ int j;
+ RFS_ASSERT (dep_tab[dep_index].fh->lock = 1);
+ dep_tab[dep_index].fh->lock = 0;
+ if (dep_tab[dep_index].proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 0;
+ j = dep_tab[dep_index].fh-fh_map;
+ if (dependency_debug) {
+ printf ("fh_map[%d] is UNlocked\n",j);
+ printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ }
+}
+
+/* the request argument may have pointers pointing to buffers, e.g. the name in lookup,
+ * the target of symlink, the write data */
+char arg_res[MAX_ARG_RES_SIZE];
+int poll_timeout = 0; /* timeout in usecs */
+char buf1 [MAX_BUF1_SIZE];
+char buf2 [MAX_BUF2_SIZE];
+
+int execute_next_request ()
+{
+ int dep_index;
+ int proc;
+ char * line;
+ struct biod_req * reqp;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime call_timeout;
+ static int last_print_time = -1;
+
+ if (num_out_reqs == max_biod_reqs) {
+ return -1;
+ }
+
+ start_profile (&valid_get_nextop_profile);
+ start_profile (&invalid_get_nextop_profile);
+ dep_index = get_nextop();
+ if (dep_index == -1) {
+ end_profile (&invalid_get_nextop_profile);
+ return dep_index;
+ };
+ end_profile (&valid_get_nextop_profile);
+
+ start_profile (&prepare_argument_profile);
+ line = dep_tab[dep_index].line;
+
+ end_profile(&total_profile);
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ last_print_time = total_profile.in.tv_sec;
+ //fprintf (stderr, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+ fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+/*
+ CYCLIC_PRINT (dep_tab_index);
+ {
+ int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size);
+ printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index,
+ dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index);
+ }
+*/
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+/*
+ if (can_not_catch_speed_num < 2000) {
+ PLAY_SCALE ++;
+ printf ("set PLAY_SCALE to %d\n", PLAY_SCALE);
+ };
+ if (can_not_catch_speed_num > 50000) {
+ PLAY_SCALE /= 2;
+ } else {
+ if (can_not_catch_speed_num > 5000) {
+ PLAY_SCALE -= 2;
+ if (PLAY_SCALE < 1)
+ PLAY_SCALE = 1;
+ }
+ }
+*/
+#endif
+ if ((total_profile.in.tv_sec > 100)) {
+ can_not_catch_speed_num_total += can_not_catch_speed_num;
+ }
+ can_not_catch_speed_num = 0;
+#endif
+ }
+ if (rfs_debug)
+ printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line);
+
+ proc = dep_tab[dep_index].proc;
+ rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2);
+
+ op_ptr = &Ops[proc];
+ reqp = get_biod_req (dep_index);
+ RFS_ASSERT (reqp);
+
+#ifdef notdef /* place to set request timeout. G. Jason Peng */
+ call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+#else
+ call_timeout.sec = 0;
+ call_timeout.usec = 500000;
+ //call_timeout.usec = 14000;
+ //call_timeout.usec = 13000;
+ //call_timeout.usec = 6000;
+ //call_timeout.usec = 8000;
+ //call_timeout.usec = 10000;
+#endif
+
+ /* make the call */
+ sfs_gettime(&(reqp->start));
+ end_profile (&prepare_argument_profile);
+ start_profile (&biod_clnt_call_profile);
+#define REAL_PLAY
+#ifdef REAL_PLAY
+ reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc,
+ rfs_Ops[proc].xdr_arg, arg_res);
+#else
+
+ reqp->xid = dep_index+1; /* just fake a message id and let it expire */
+#endif
+ RFS_ASSERT (reqp->xid != 0);
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ dep_tab[dep_index].flag = DEP_FLAG_SENT;
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */
+ end_profile (&biod_clnt_call_profile);
+}
+
+void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status)
+{
+ if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) {
+ if (!profile_debug)
+ printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index);
+#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+ /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they
+ * are refered by name through rename, remove
+ */
+ if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) {
+ /* current initialization doesnot take care of rename source, if there is no
+ * create or lookup before that source, the source object will not exist when
+ * rename occurs
+ */
+ rename_rmdir_noent_reply_num++;
+ } else
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) {
+ /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK
+ * the initialization considers only the lookup return OK and created the file in the initialization
+ * so in trace play the first lookup return OK
+ */
+ RFS_ASSERT (1);
+ } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) {
+ /* trace_status could be EAGAIN */
+ if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) {
+ /* due to similar reason as above, the initialization code initializes the symbolic link as a normal
+ * file already
+ */
+ RFS_ASSERT (1);
+ } else
+#endif
+#ifndef TAKE_CARE_NOEMPTY_RMDIR
+ /* the remove packet seems got lost in the trace capture, so replay can not finish */
+ if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) {
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ RMDIR3args * rmdir_argp;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2);
+ rmdir_argp = (RMDIR3args *)arg_res;
+
+ memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3));
+ memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */
+ args.to.name = buf2;
+ sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name);
+
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ Nfs_timers[Init]);
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ if (reply.status!=NFS3_OK)
+ printf ("change rmdir into rename, reply.status %d\n", reply.status);
+ RFS_ASSERT (reply.status==NFS3_OK);
+ rmdir_not_empty_reply_num ++;
+#endif
+#ifndef TAKE_CARE_ACCESS_ERROR
+ } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) {
+ loose_access_control_reply_num ++;
+#endif
+#ifdef NO_DEPENDENCY_TABLE
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) {
+ lookup_err_due_to_rename_num ++;
+ } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) {
+ /* if there is a remove in front of the lookup, but it is
+ * actually executed later than the lookup
+ */
+ lookup_err_due_to_parallel_remove_num ++;
+#endif
+#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH
+ /* if the looked return EACCESS in the trace, means the object still exists
+ * should have initialized, right not don't initialize it, hence play status
+ * could be ENOENT
+ */
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) {
+ lookup_eaccess_enoent_mismatch_num ++;
+#endif
+#ifdef TOLERANT_READ_IO_ERR
+ } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) {
+ read_io_err_num ++;
+#endif
+#ifdef TOLERANT_STALE_FHANDLE_ERR
+ } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) {
+ printf ("!!!!!!! STALE FILE HANDLE \n");
+ //sleep(1);
+ stale_fhandle_err_num ++;
+#endif
+ } else {
+ int i;
+ for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++)
+ printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line);
+ if (status==EEXIST) {
+ abnormal_EEXIST_num ++;
+ } else if (status == ENOENT) {
+ abnormal_ENOENT_num ++;
+ } else
+ RFS_ASSERT (0);
+ }
+ } else {
+ proper_reply_num ++;
+ if (total_profile.in.tv_sec >= WARMUP_TIME)
+ run_stage_proper_reply_num ++;
+ }
+
+}
+
+/* return -1 if there is no reply being received
+ * return the dep_index if the corresponding reply has been received
+ */
+inline int receive_next_reply (int busy_flag)
+{
+ int dep_index;
+ int biod_index;
+ int proc;
+ char * line;
+ char * reply_line;
+ sfs_op_type *op_ptr; /* per operation info */
+ int ret;
+ int status;
+ int trace_status;
+ char * errmsg;
+
+ /* wait for reply */
+ start_profile (&valid_poll_and_get_reply_profile);
+ start_profile (&invalid_poll_and_get_reply_profile);
+
+ if (busy_flag == BUSY) {
+ poll_timeout = 0;
+ poll_timeout_0_num ++;
+ } else {
+ poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */
+ //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */
+ poll_timeout_pos_num ++;
+ }
+
+ biod_index = poll_and_get_reply (poll_timeout);
+ if (biod_index==-1) {
+ end_profile (&invalid_poll_and_get_reply_profile);
+ return -1;
+ };
+ end_profile (&valid_poll_and_get_reply_profile);
+
+ start_profile (&decode_reply_profile);
+ /* check the corresponding request */
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ if (biod_reqp[biod_index].in_use==1) {
+ RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index);
+ } else {
+ printf ("biod_index %d reply received but the request has been time out\n", biod_index);
+ return -1;
+ }
+
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+
+ if (dep_tab[dep_index].flag != DEP_FLAG_SENT) {
+ printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n",
+ dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status,
+ dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec,
+ dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec );
+ printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT);
+
+ /* decode the reply */
+ rfs_Ops[proc].setres (arg_res, buf1);
+ ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res);
+ RFS_ASSERT (ret == RPC_SUCCESS);
+ status = *((int *)arg_res);
+ errmsg = nfs3_strerror (status);
+ end_profile (&decode_reply_profile);
+
+ start_profile (&check_reply_profile);
+ /* compare with the reply in the trace */
+ line = dep_tab[dep_index].line;
+ reply_line = dep_tab[dep_index].reply_line;
+ trace_status = dep_tab[dep_index].trace_status;
+
+ /* print the result, trace play progress indicator
+ if (rfs_debug || ( !profile_debug && ((dep_index %10000)==0) ) )
+ fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec);
+ */
+
+ /* error checking */
+ check_reply (proc, biod_index, dep_index, status, errmsg, trace_status);
+
+ /* free resources */
+ finish_request (biod_index, dep_index, status);
+
+ /* we set 100 seconds warm up time */
+ if ((total_profile.in.tv_sec >= WARMUP_TIME)) {
+ /* get statistics */
+ if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) {
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop));
+ end_profile (&check_reply_profile);
+ }
+
+ //start_profile (&add_create_object_profile);
+
+ if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) {
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_line);
+#endif
+ if (status!=NFS3_OK) {
+ /* commented out for 1022 */
+ printf ("!!!!!! Should have been an ASSERTION FAILURE \n");
+ RFS_ASSERT (proc==SYMLINK);
+ RFS_ASSERT (0);
+ } else {
+ if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2')
+ add_new_file_system_object(proc, dep_index, line, reply_line);
+ }
+ }
+ //end_profile (&add_create_object_profile);
+}
+
+inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line)
+{
+ char * child_trace_fh;
+ fh_map_t * parent_entryp;
+ char component_name[MAX_PLAY_PATH_SIZE];
+ char * parent_trace_fh;
+ char child_path[MAX_PLAY_PATH_SIZE];
+ post_op_fh3 * post_op_fh3_child;
+ char * reply_trace_fh;
+ nfs_fh3 * child_fh3;
+
+ parent_trace_fh = strstr (line, "fh");
+ RFS_ASSERT (parent_trace_fh);
+ parent_trace_fh +=3;
+ parent_entryp = lookup_fh (parent_trace_fh);
+ RFS_ASSERT (parent_entryp);
+ parse_name (parent_trace_fh+65, component_name);
+ strcpy (child_path, parent_entryp->path);
+ strcat (child_path, "/");
+ strcat (child_path, component_name);
+
+ /* find the corresponding create request */
+ //printf ("before find reply trace_fh reply_line %s\n", reply_line);
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ reply_trace_fh = dep_tab[dep_index].reply_trace_fh;
+#else
+ reply_trace_fh = find_reply_trace_fh (reply_line);
+#endif
+ RFS_ASSERT (reply_trace_fh != NULL);
+ switch (proc) {
+ case CREATE:
+ RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKDIR:
+ RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case SYMLINK:
+ RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKNOD:
+ RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case LOOKUP:
+ RFS_ASSERT (proc==LOOKUP);
+ child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' ');
+#endif
+ reply_trace_fh[TRACE_FH_SIZE] = 0;
+ add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ /* just to preserve the original reply line not changed */
+ reply_trace_fh[TRACE_FH_SIZE] = ' ';
+#endif
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void trace_play(void)
+{
+
+ /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY
+ * when either there is request to send or there is reply being
+ * received. Otherwise it is IDLE. The timeout for polling replies
+ * is set to 0 when BUSY, it is set to the waiting time to the first
+ * request outside of current <min_dep_index, dep_window_max> window when IDLE.
+ */
+ int busy_flag = BUSY;
+ //int dep_index; /* index into dependency table: dep_tab */
+ //int biod_index; /* index into outstanding requests: biod_reqp */
+
+ disk_io_status = read_trace();
+ RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index));
+ CYCLIC_MOVE_HEAD(dep_window_index);
+
+ adjust_play_window(busy_flag, &poll_timeout);
+
+ start_profile (&total_profile);
+ while (!CYCLIC_EMPTY(dep_tab_index)) {
+ if ((total_profile.in.tv_sec > 600)) {
+ printf ("the process has run for more than 600 seconds, exit\n");
+ goto END;
+ }
+
+ if (busy_flag == IDLE) {
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+ if (disk_io_status!=TRACE_FILE_END) {
+ disk_io_status = read_trace();
+ };
+ }
+
+ //start_profile (&adjust_play_window_profile);
+ adjust_play_window (busy_flag, &poll_timeout);
+ if (rfs_debug)
+ printf("num_out_reqs %d\n", num_out_reqs);
+ busy_flag = IDLE;
+ //end_profile (&adjust_play_window_profile);
+
+ start_profile (&execute_next_request_profile);
+ while (execute_next_request()!=-1) {
+ busy_flag = BUSY;
+ }
+ end_profile (&execute_next_request_profile);
+
+
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1)
+ busy_flag = BUSY;
+#endif
+ end_profile (&receive_next_reply_profile);
+
+ CYCLIC_ASSERT (0);
+ }
+ end_profile (&total_profile);
+
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+ if (num_out_reqs !=0 ) {
+ printf ("num_out_reqs %d\n", num_out_reqs);
+ CYCLIC_PRINT(dep_tab_index);
+ }
+ RFS_ASSERT(num_out_reqs==0);
+
+END:
+ printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2,
+ trace_timestamp2 - trace_timestamp1);
+ printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d",
+ can_not_catch_speed_num_total, can_not_catch_speed_num);
+ printf ("total_profile.about: %s\n", total_profile.about);
+ print_profile ("total_profile", &total_profile);
+ printf("\n");
+ //print_profile ("check_timeout", &check_timeout_profile);
+ //printf("\n");
+ //print_profile ("adjust_play_window", &adjust_play_window_profile);
+ //printf("\n");
+ print_profile ("execute_next_request_profile", &execute_next_request_profile);
+ print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile);
+ print_profile ("prepare_argument_profile", &prepare_argument_profile);
+ print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ printf("\n");
+ print_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ print_profile ("decode_reply_profile", &decode_reply_profile);
+ print_profile ("check_reply_profile", &check_reply_profile);
+ print_profile ("fgets_profile", &fgets_profile);
+ print_profile ("read_line_profile", &read_line_profile);
+ print_profile ("read_trace_profile", &read_trace_profile);
+ //print_profile ("add_create_object", &add_create_object_profile);
+ printf("\n");
+
+ printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs);
+}
+
+int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest)
+{
+ cyclic_index_t indextmp, indextmp2;
+ int oldnum, num;
+ indextmp = *index;
+ indextmp2 = indextmp;
+ oldnum = CYCLIC_NUM(indextmp);
+
+ if (! ((dest>=0) && (dest<indextmp.size))) {
+ CYCLIC_PRINT(indextmp);
+ printf("dest %d\n", dest);
+ }
+ RFS_ASSERT ((dest>=0) && (dest<indextmp.size));
+ index->tail = dest;
+ indextmp2.tail = dest;
+ num = CYCLIC_NUM(indextmp2);
+
+ if (num > oldnum) {
+ CYCLIC_PRINT(indextmp);
+ CYCLIC_PRINT(indextmp2);
+ printf("dest %d old_num %d num %d\n", dest, oldnum, num);
+ }
+ RFS_ASSERT (num <= oldnum);
+}
+
+int flush_junk()
+{
+ int i;
+ for (i=0; i<500; i++) {
+ printf ("*************************************************************\n");
+ }
+ fflush(stdout);
+}
+
+int CYCLIC_ASSERT (int i)
+{
+ int j;
+ if (!(dep_tab_index.tail == dep_window_index.tail)) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+
+ if (!((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+ for (i=0, j=0; i<max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == 1)
+ j++;
+ }
+ RFS_ASSERT (num_out_reqs==j);
+/*
+ RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ));
+*/
+}
+
+/* sfs_c_chd.c */
--- /dev/null
+/* Try to change thread scheduling and uses three threads */
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+#include "nfsd_nfsfh_cust.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout);
+extern char * parse_name (char * t, char * buf);
+
+/* forward definitions for local functions */
+static int init_rpc(void);
+
+/* RFS: forward definitions for local functions */
+void init_ops(void);
+static void init_signal();
+extern void init_file_system (void);
+extern void init_dep_tab (void);
+static int read_trace(void);
+static void read_fh_map();
+static void init_play (char * mount_point);
+static void trace_play(void);
+void print_result(void);
+static int get_nextop(void);
+static int check_timeout(void);
+static struct biod_req * get_biod_req(int dep_tab_index);
+static int lookup_biod_req (int xid);
+static void init_time_offset(void);
+void adjust_play_window (int flag, int * poll_timeout_arg);
+static int poll_and_get_reply (int usecs);
+static char * nfs3_strerror(int status);
+static void check_clock(void);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void usage(void);
+void init_dep_tab_entry (int dep_index);
+extern inline fh_map_t * lookup_fh (char * trace_fh );
+static void finish_request (int biod_index, int dep_index, int status, int dep_flag);
+static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line);
+static inline char * find_lead_trace_fh(int proc, char * line);
+static inline char * find_reply_trace_fh (char * line);
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+
+static char io_buf[BUFSIZ]; /* io buffer for print out messages */
+
+char * sfs_Myname;
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+int Validate = 0; /* fake variable */
+int Child_num = 0; /* fake: child index */
+int Tcp = 0; /* We implement UDP first */
+int Client_num = 1; /* fake: number of client */
+uid_t Real_uid;
+gid_t Cur_gid;
+uid_t Cur_uid;
+/* as long as the inital value is different, then it's OK */
+int recv_num = 0;
+int timeout_num = 0;
+int send_num = 0;
+int exit_flag = 0;
+int async_rpc_sem;
+int no_progress_flag = 0;
+int num_out_reqs_statistics[MAX_OUTSTANDING_REQ+1];
+int num_out_reqs_statistics_at_timeout[MAX_OUTSTANDING_REQ+1];
+
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14,
+ 13, 21, 10, 15, 11, 12, 16, 23, 17, 20,
+ 22, 19};
+void print_usage(int pos, int argc, char ** argv)
+{
+ int i;
+ printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n");
+ printf("sfs3 -pair_trace trace_file\n");
+ printf("sfs3 -pair_write trace_file\n");
+ printf("sfs3 -help\n");
+ printf ("pos %d argc %d", pos, argc);
+ for (i=0; i<argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf ("\n");
+ exit;
+}
+
+void print_cyclic_buffers ()
+{
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+}
+
+void add_to_dep_tab(int i)
+{
+ char * trace_fh;
+ fh_map_t * fh_map_entry;
+ dep_tab_t * ent;
+
+ trace_fh = strstr (memory_trace[i].line, "fh");
+ if (!trace_fh)
+ printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line);
+ RFS_ASSERT (trace_fh);
+ trace_fh += 3;
+ fh_map_entry = lookup_fh (trace_fh);
+ if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) {
+ req_num_with_discard_fh ++;
+ return;
+ }
+ if (fh_map_entry)
+ req_num_with_init_fh ++;
+ else
+ req_num_with_new_fh ++;
+
+ RFS_ASSERT (!CYCLIC_FULL(dep_tab_index));
+ ent = &(dep_tab[dep_tab_index.head]);
+
+ ent->disk_index = memory_trace[i].disk_index;
+ ent->memory_index = i;
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ ent->trace_status = memory_trace[i].trace_status;
+ ent->reply_trace_fh = memory_trace[i].reply_trace_fh;
+#endif
+ ent->line = memory_trace[i].line;
+ init_dep_tab_entry(dep_tab_index.head);
+
+ if (rfs_debug && (i%100000)==0)
+ printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index);
+ CYCLIC_MOVE_HEAD(memory_trace_index);
+ CYCLIC_MOVE_HEAD(dep_tab_index);
+}
+
+void init_profile_variables ()
+{
+ init_profile ("total_profile", &total_profile);
+ init_profile ("execute_next_request_profile", &execute_next_request_profile);
+ init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile);
+ init_profile ("prepare_argument_profile", &prepare_argument_profile);
+ init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ init_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ init_profile ("decode_reply_profile", &decode_reply_profile);
+ init_profile ("check_reply_profile", &check_reply_profile);
+ init_profile ("add_create_object_profile", &add_create_object_profile);
+ init_profile ("check_timeout_profile", &check_timeout_profile);
+ init_profile ("adjust_play_window_profile",&adjust_play_window_profile);
+ init_profile ("fgets_profile",&fgets_profile);
+ init_profile ("read_line_profile",&read_line_profile);
+ init_profile ("read_trace_profile",&read_trace_profile);
+}
+
+static char trace_file[256]="anon-lair62-011130-1200.txt";
+int print_memory_usage()
+{
+ printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map));
+ printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab));
+ printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace));
+ printf("size of CREATE3args %d\n", sizeof( CREATE3args));
+ printf("size of MKDIR3args %d\n", sizeof( MKDIR3args));
+ printf("size of READ3args %d\n", sizeof( READ3args));
+ printf("size of WRITE3args %d\n", sizeof( WRITE3args));
+ printf("size of RENAME3args %d\n", sizeof( RENAME3args));
+ printf("size of GETATTR3args %d\n", sizeof( GETATTR3args));
+ printf("size of SETATTR3args %d\n", sizeof( SETATTR3args));
+ printf("size of LINK3args %d\n", sizeof( LINK3args));
+ printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args));
+ printf("size of MKNOD3args %d\n", sizeof( MKNOD3args));
+ printf("size of RMDIR3args %d\n", sizeof( RMDIR3args));
+ printf("size of REMOVE3args %d\n", sizeof( REMOVE3args));
+ printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args));
+ printf("size of READDIR3args %d\n", sizeof( READDIR3args));
+ printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args));
+ printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args));
+ printf("size of FSINFO3args %d\n", sizeof( FSINFO3args));
+ printf("size of COMMIT3args %d\n", sizeof( COMMIT3args));
+ printf("size of ACCESS3args %d\n", sizeof( ACCESS3args));
+ printf("size of READLINK3args %d\n", sizeof( READLINK3args));
+
+
+}
+
+void recv_thread()
+{
+ int last_print_time = -1;
+ int busy_flag;
+
+ while (send_num ==0) {
+ usleep(1000);
+ }
+
+ //while (!CYCLIC_EMPTY(dep_tab_index)) {
+ while (!exit_flag) {
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ static int recv_num_before_10_seconds = 0;
+ last_print_time = total_profile.in.tv_sec;
+ fprintf (stdout, "<<<<< recv_thread recv_num %d time %d num_out_reqs %d \n", recv_num, total_profile.in.tv_sec, num_out_reqs);
+ if (recv_num == recv_num_before_10_seconds) {
+ no_progress_flag = 1;
+ RFS_ASSERT (0);
+ } else
+ recv_num_before_10_seconds = recv_num;
+ }
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+
+ pthread_yield();
+ //usleep(1000);
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1) {
+ busy_flag = BUSY;
+ }
+#endif
+ end_profile (&receive_next_reply_profile);
+ }
+ printf ("<<<< recv thread EXIT\n");
+ exit_flag = 2;
+}
+
+int io_thread ()
+{
+/* number of seconds the I/O thread pauses after each time trying to read the requests */
+#define IO_THREAD_PAUSE_TIME 1
+
+ int i;
+ int j = 0;
+
+ disk_io_status = read_trace ();
+ while (disk_io_status == TRACE_BUF_FULL) {
+
+ usleep (10000);
+ if ((j++%1000)==0) {
+ printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/100);
+ }
+
+ disk_io_status = read_trace ();
+ //printf ("io_thread, after read_trace, disk_index %d\n", disk_index);
+
+#ifdef SEQUEN_READ
+ for (i=0; i<SEQUEN_READ_NUM; i++) {
+ add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size));
+ }
+#endif
+ //printf ("***************** IO THREAD SLEEP 1 s\n");
+ //print_cyclic_buffers();
+ //pthread_yield();
+ }
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+ printf("io_thread EXIT\n");
+}
+
+int execute_thread()
+{
+ int i;
+ struct timeval playstart_time, playend_time;
+
+ sleep (10); /* first let io_thread to run for a while */
+ printf ("trace_play\n");
+
+ gettimeofday(&playstart_time, NULL);
+
+ init_time_offset();
+ trace_play ();
+
+ gettimeofday(&playend_time, NULL);
+
+ {
+ int usec, sec;
+ sec = playend_time.tv_sec - playstart_time.tv_sec;
+ usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec);
+ sec = usec / 1000000;
+ usec = usec % 1000000;
+ printf("Total play time: %d sec %d usec\n", sec, usec);
+ if (sec > WARMUP_TIME)
+ print_result();
+ else
+ printf ("the trace play time %d is less than WARMUP_TIME %d, no statistical results\n");
+ }
+#ifdef RECV_THREAD
+ exit_flag = 1;
+ while (exit_flag == 1) {
+ usleep (1000);
+ }
+#endif
+
+ clnt_destroy(NFS_client);
+ biod_term();
+}
+
+int init_thread ()
+{
+ pthread_attr_t attr;
+ int arg;
+ int ret = 0;
+ pthread_t io_thread_var;
+#ifdef RECV_THREAD
+ pthread_t recv_thread_var;
+#endif
+ pthread_t execute_thread_var;
+
+ ret = pthread_attr_init (&attr);
+ if (ret!=0) {
+ perror("pthread_attr_init attr");
+ return ret;
+ }
+#ifdef IO_THREAD
+ ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+#endif
+
+#ifdef RECV_THREAD
+ ret = pthread_create (&(recv_thread_var), &attr, &recv_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("recv_pthread_attr_init");
+ return ret;
+ }
+#endif
+
+/*
+ ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+*/
+}
+
+void init_buffers()
+{
+ CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES);
+ CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE);
+ CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE);
+}
+
+int main(int argc, char ** argv)
+{
+ extern char * optarg;
+ int i;
+ struct timeval in={1000000, 100};
+
+ init_profile_variables();
+ if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) {
+ print_usage(0, argc, argv);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_write")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_write(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_trace")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_trace(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_aging")) {
+ if (argc!=3) {
+ print_usage(3, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ check_aging (trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_statistics")) {
+ if (argc!=3) {
+ print_usage(1, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ memset(fh_htable, 0, sizeof (fh_htable));
+ check_statistics (trace_file);
+ exit(0);
+ }
+
+ if (argc!=6) {
+ print_usage(2, argc, argv);
+ exit(0);
+ }
+
+ PLAY_SCALE = atoi (argv[4]);
+ RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000);
+
+ WARMUP_TIME = atoi (argv[5]);
+ RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000);
+
+ print_memory_usage();
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+ /*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+ //Real_uid = 513;
+ //Cur_gid = 513;
+
+ Nfs_timers = Nfs_udp_timers;
+
+ init_semaphores();
+ init_file_system ();
+ init_signal();
+ init_play (argv[1]);
+ //init_play ("capella:/p5/RFSFS");
+ init_fh_map();
+ read_fh_map (argv[3]);
+ //read_fh_map ("fh-path-map-play");
+ strcpy(trace_file, argv[2]);
+
+/* If ordered by TIMESTAMP,
+ * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <=
+ * dep_tab_index.head <= memory_trace_index.head
+ */
+
+ init_global_variables();
+ init_buffers();
+ init_thread();
+ pthread_yield();
+ execute_thread();
+}
+
+int init_global_variables()
+{
+ memset (num_out_reqs_statistics, 0, sizeof(num_out_reqs_statistics));
+ memset (num_out_reqs_statistics_at_timeout, 0, sizeof(num_out_reqs_statistics_at_timeout));
+}
+
+int init_semaphores()
+{
+ async_rpc_sem = dest_and_init_sem (ASYNC_RPC_SEM_KEY, 1, "async_rpc_sem");
+}
+
+void init_ops (void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/* Set up the signal handlers for all signals */
+void init_signal()
+{
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+#else
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ // RFS (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+#endif
+}
+
+void
+init_play (
+ char * mount_point) /* Mount point for remote FS */
+{
+ char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */
+ CLIENT * mount_client_ptr; /* Mount client handle */
+
+ if (!rfs_debug);
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ sfs_Myname = namebuf;
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, mount_point, mount_client_ptr);
+
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ init_counters();
+}
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+void inline format_line (char * line_before, char * line)
+{
+ char * pv = line_before;
+ char * pl = line;
+ char * p;
+ int i;
+
+ //printf("format_line before %s\n", line_before);
+ p = strstr (pv, "fh");
+ while (p!=NULL) {
+ while (pv<=p)
+ *pl++ = *pv++;
+ *pl++ = *pv++;
+ if (*pv==2) {
+ *pl++ = *pv++;
+ }
+ *pl++ = *pv++;
+ i = 0;
+ while (*pv !=' ') {
+ RFS_ASSERT ((*pv >='0' && *pv <='9') || (*pv >='a' && *pv<='f'));
+ *pl++ = *pv++;
+ i++;
+ }
+ RFS_ASSERT ((i==48) || (i==40) || (i==24));
+ while (i<48) {
+ *pl++ = '0';
+ i++;
+ }
+ p = strstr (pv, "fh");
+ }
+ while ((*pv)!=NULL) {
+ *pl++ = *pv++;
+ }
+ //printf("format_line after %s\n", line);
+}
+
+char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+ static int varfh_flag = 0;
+#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2)
+#define SAFE_BYTES 1000
+#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES)
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ char varfh_line_buf[READ_LINE_LENGTH];
+
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+
+ if (strstr(trace_file, ".varfh")) {
+ varfh_flag = 1;
+ };
+ if (strstr(trace_file, ".fmt1")) {
+ TRACE_COMMAND_REPLY_FLAG_POS += 12;
+ TRACE_VERSION_POS +=12;
+ TRACE_MSGID_POS +=12;
+ TRACE_FH_SIZE =48;
+ }
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (varfh_flag) {
+ if (!fgets(varfh_line_buf, READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ format_line (varfh_line_buf, line_buf[i]);
+ } else {
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ if (disk_index > start_disk_index+READ_LINE_BUF_SIZE) {
+ printf ("disk_index %d start_disk_index %d READ_LINE_BUF_SIZE %d\n",
+ disk_index, start_disk_index, READ_LINE_BUF_SIZE);
+ }
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+/*
+ if (!(strlen(line_buf[i])>80)) {
+ printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT (strlen(line_buf[i])>80);
+ }
+ if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)))
+ printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH));
+*/
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+#define OPS_FLAG_INC 0
+#define OPS_FLAG_PRINT 1
+int read_write_fh_statistics (int flag, char * buf, int timestamp)
+{
+ static FILE * fp = NULL;
+ char * p;
+ char c;
+ if (!fp) {
+ fp = fopen ("read_write_fh.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (buf, "fh");
+ RFS_ASSERT (p);
+ c = p[40+3];
+ p[40+3]=0;
+ fprintf(fp, "%s\n", p+3+24);
+ p[40+3]=c;
+ };
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+int write_statistics(int flag, char * buf, char * reply_buf, int trace_status)
+{
+ static FILE * fp = NULL;
+ int pre_size, size, count;
+ char * p = reply_buf;
+ if (!fp) {
+ fp = fopen ("write.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ }
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ int timestamp = (int) reply_buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+void ops_statistics (int ops_flag, int proc, int timestamp)
+{
+ static FILE * fp = NULL;
+ static struct {
+ int count;
+ int count_K;
+ int update_flag;
+ char name[32];
+ } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"},
+ {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"},
+ {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"},
+ {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"},
+ {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"},
+ {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}};
+
+ if (ops_flag == OPS_FLAG_INC) {
+ RFS_ASSERT (proc>=0 && proc<NOPS);
+ ops[proc].count ++;
+ if (ops[proc].count == 1000) {
+ ops[proc].count_K ++;
+ ops[proc].count = 0;
+ }
+ }
+ if (ops_flag == OPS_FLAG_PRINT) {
+ int i;
+ int update_K=0, update=0, total_K=0, total=0;
+ float f1, f2;
+ int disk_index = proc;
+
+ if (!fp) {
+ fp = fopen ("mix.output", "w");
+ RFS_ASSERT (fp);
+ }
+ for (i=0; i<NOPS; i++) {
+ total_K += ops[i].count_K;
+ total += ops[i].count;
+ if (ops[i].update_flag) {
+ update_K += ops[i].count_K;
+ update += ops[i].count;
+ }
+ }
+ update_K += update/1000;
+ update = update%1000;
+ total_K += total/1000;
+ total = total%1000;
+
+ f1 = total_K;
+ f1 = f1*1000+total;
+ for (i=0; i<NOPS; i++) {
+ f2 = ops[i].count_K;
+ f2 = f2*1000+ops[i].count;
+ fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ }
+ f2 = update_K;
+ f2 = f2*1000+update;
+ fprintf (fp, " total %8d,%03d\n", total_K, total);
+ fprintf (stderr, " total %8d,%03d\n", total_K, total);
+ fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+
+void truncate_statistics (int flag, int proc, char * buf, char * reply_buf)
+{
+#define TRACE_FH_SIZE2 16
+#define BLOCK_SIZE 4096
+ static int no_size_num = 0;
+ static int have_size_num = 0;
+ static int equal_size_num = 0;
+ static int first_size_num = 0;
+ static int truncate_num = 0;
+ static int truncate_size = 0;
+ static int truncate_KB = 0;
+ static int truncate_block_num = 0;
+ static int padding_num = 0;
+ static int padding_KB = 0;
+ static int padding_size = 0;
+ static FILE * fp = NULL;
+ char * p;
+ char * trace_fh;
+ int pre_size, size, count;
+ struct generic_entry * ent;
+
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = proc;
+ int timestamp = (int)buf;
+ if (!fp) {
+ fp = fopen ("truncate.output", "w");
+ RFS_ASSERT (fp);
+ }
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ return;
+ }
+
+ p = strstr (&buf[TRACE_MSGID_POS], "fh");
+ RFS_ASSERT (p);
+ p+=3;
+ trace_fh = p;
+
+ if (proc==SETATTR) {
+ p = strstr (buf, " size ");
+ } else {
+ RFS_ASSERT (proc==WRITE);
+ p = strstr (reply_buf, " ftype 1 ");
+ RFS_ASSERT (p);
+ p = strstr (p, " size ");
+ RFS_ASSERT (p);
+ if (strstr(p, " ftype 1 ")) {
+ fprintf (fp, "#### %s%s\n", buf, reply_buf);
+ fprintf (stderr, "#### %s%s\n", buf, reply_buf);
+ }
+ RFS_ASSERT (!strstr(p, " ftype 1 "));
+ }
+ if (!p) {
+ no_size_num ++;
+ return;
+ }
+ have_size_num ++;
+
+ sscanf (p, " size %x", &size);
+ if (size <0 || size > 2000000000) {
+ fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ }
+
+ RFS_ASSERT (size >=0 && size <2000000000);
+ ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE);
+ if (ent) {
+ if (ent->key3 != size) {
+ if (proc==SETATTR) {
+ //printf ("%s\n", buf);
+ //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ if (ent->key3 > size) {
+ truncate_num ++;
+ truncate_size += ent->key3 - size;
+ truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE;
+ if (size!=0) {
+ //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE;
+ }
+ if (truncate_size > 1000000000) {
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ }
+ } else {
+ padding_num ++;
+ //printf ("%s\n", buf);
+ //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ padding_size += size - ent->key3;
+ if (padding_size > 1000000000) {
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ }
+ }
+ }
+ ent->key3 = size;
+ }else
+ equal_size_num++;
+ } else {
+ generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE);
+ first_size_num ++;
+ }
+};
+
+int get_timestamp_usec (char * buf)
+{
+ char str[128];
+ int ret;
+ strncpy(str, buf, 100);
+ RFS_ASSERT (str[10]=='.');
+ RFS_ASSERT (str[17]==' ');
+ str[17]=0;
+ ret = atoi(&str[11]);
+ RFS_ASSERT (ret >=0 && ret <=999999);
+ return ret;
+}
+
+int get_timestamp_sec (char * buf)
+{
+ char str[128];
+ int ret;
+ strncpy(str, buf, 100);
+ RFS_ASSERT (str[10]=='.');
+ str[10]=0;
+ ret = atoi(str);
+ RFS_ASSERT (ret >1000000000 && ret <2000000000);
+ return ret;
+}
+
+int check_aging (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ switch (proc) {
+ int off, count, size;
+ char * t;
+ case CREATE: printf("%s\n", "create"); break;
+ case MKDIR: printf("%s\n", "mkdir"); break;
+ case REMOVE: printf("%s\n", "remove"); break;
+ case RMDIR: printf("%s\n", "rmdir"); break;
+ case WRITE:
+ t = buf;
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ sscanf (t, "%x", &off);
+ RFS_ASSERT (off>=0 && off<0x7FFFFFFF)
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &count);
+ RFS_ASSERT (count <= 32768);
+ printf("%s off %x count %x\n", "write", off, count);
+ //printf("%s count %x\n", "write", count);
+ break;
+ case SETATTR:
+ t = strstr (buf, " size ");
+ if (t) {
+ sscanf (t, " size %x", &size);
+ printf ("%s size %x\n", "setattr", size);
+ }
+ }
+ if ((disk_index%10000)==0) {
+ fprintf(stderr, "%d disk trace passed\n", disk_index);
+ }
+ };
+
+ fprintf(stderr, "%d disk trace parsed\n", disk_index);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, -1);
+}
+
+
+int check_statistics (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+ static int last_timestamp_sec = -1;
+ int timestamp_sec;
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ timestamp_sec = get_timestamp_sec(buf);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ if (proc!= WRITE && proc!=SETATTR && proc!=READ) {
+ continue;
+ }
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ if (proc==READ || proc==WRITE)
+ read_write_fh_statistics (OPS_FLAG_INC, buf, 0);
+ if (proc == WRITE)
+ write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status);
+ if (proc==WRITE || proc==SETATTR)
+ truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf);
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ //printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if *
+ if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ if (last_timestamp_sec == -1) {
+ last_timestamp_sec = timestamp_sec;
+ } else if (timestamp_sec - last_timestamp_sec >=3600) {
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+ last_timestamp_sec = timestamp_sec;
+ }
+/*
+ if ((memory_trace_size%10000)==0) {
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, -1);
+ truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL);
+ }
+*/
+ };
+
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+}
+
+
+/* This routine output all the requests, together with their replies */
+int pair_trace (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+ FILE * outputfp;
+ char output_file[1024];
+
+ strcpy (output_file, tracefile);
+ strcat (output_file, ".pair");
+ outputfp = fopen (output_file, "w");
+ RFS_ASSERT (outputfp);
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (disk_index == 258)
+ f();
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+ fprintf (outputfp, "%s\n", buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ fprintf(outputfp, "%s", reply_buf);
+ trace_status = find_reply_status(reply_buf);
+ if (debug)
+ fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf));
+ break;
+ };
+ }
+ }
+
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ fprintf (stderr, "%s no reply\n", buf);
+ fprintf(outputfp, "missing_reply\n");
+ missing_reply_num ++;
+ }
+
+ if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) {
+ fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+
+ memory_trace_size ++;
+
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+/* This routine output all the write requests, together with their replies. It is used for
+ * analysis of write requests: appended bytes, overwrite bytes etc
+ */
+int pair_write (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int pair_write_debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ if (!strstr(buf, "write"))
+ continue;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (pair_write_debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ /* store the request to memory */
+ //strcpy (memory_trace[memory_trace_size].line, buf);
+ //memory_trace[memory_trace_size].disk_index = disk_index;
+
+ /* find and store the reply status and reply fhandle to memory */
+ //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS;
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (pair_write_debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ int pre_size, size, count;
+ //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf);
+ if (pair_write_debug)
+ printf("reply found trace_status %d\n", find_reply_status(reply_buf));
+ //break;
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ break;
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if (missing_reply_num > memory_trace_size/10) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ /*
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ */
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+
+#ifdef notdef
+/* This function is not finished writing */
+int calculate_performance()
+{
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int debug = 0;
+
+typedef struct {
+ struct timeval start;
+ struct timeval stop;
+ int trace_status;
+ int op;
+} trace_performance_ent_t;
+
+ struct timeval req_time;
+ struct timeval reply_time;
+
+ trace_performance_ent_t * ent = NULL;
+
+ while (!CYCLIC_FULL(memory_trace_index)) {
+
+ if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS))
+ buf = reply_buf;
+ if ((buf=read_line(++disk_index))==NULL) {
+END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num );
+ fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+ end_profile (&read_trace_profile);
+ return TRACE_FILE_END;
+ }
+
+ get_timestamp (&ent->req_time, buf);
+ if (MAX_COMMAND_REPLY_DISTANCE ==1) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ reply_buf=read_line(++disk_index);
+ RFS_ASSERT (reply_buf);
+ if (!strcmp(reply_buf, "missing_reply\n")) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ get_timestamp (&ent->reply_time, buf);
+ ent->trace_status = find_reply_status(reply_buf);
+ }
+ }
+ }
+}
+#endif
+
+int read_trace ()
+{
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int debug = 0;
+ memory_trace_ent_t * ent=NULL;
+
+ start_profile (&read_trace_profile);
+
+ while (!CYCLIC_FULL(memory_trace_index)) {
+ if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS))
+ buf = reply_buf;
+ if ((buf=read_line(++disk_index))==NULL) {
+END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num );
+ fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+ end_profile (&read_trace_profile);
+ return TRACE_FILE_END;
+ }
+
+ if (rfs_debug)
+ printf ("disk_index %d %s\n", disk_index, buf);
+
+ if (disk_index==0) {
+ trace_timestamp1 = get_timestamp_sec (buf);
+ trace_starttime.sec = get_timestamp_sec (buf);
+ trace_starttime.usec = get_timestamp_usec (buf);
+ trace_starttime.esec = 0;
+ printf ("trace starttime %d %d %d\n", trace_starttime.sec, trace_starttime.usec, trace_starttime.esec);
+ } else
+ trace_timestamp2 = get_timestamp_sec (buf);
+
+ /* store the request to memory */
+ ent = &(memory_trace[memory_trace_index.head]);
+ strcpy (ent->line, buf);
+ ent->disk_index = disk_index;
+
+ if (MAX_COMMAND_REPLY_DISTANCE ==1) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ reply_buf=read_line(++disk_index);
+ RFS_ASSERT (reply_buf);
+ if (!strcmp(reply_buf, "missing_reply\n")) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ ent->trace_status = find_reply_status(reply_buf);
+ }
+ };
+
+ if (ent->trace_status == NFS3ERR_RFS_MISS)
+ missing_reply_num ++;
+
+ if (MAX_COMMAND_REPLY_DISTANCE > 1) {
+ if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index);
+ exit (0);
+ }
+ }
+
+ /* find and store the reply trace fhandle for create-class requests */
+ if (ent->trace_status==NFS3_OK) {
+ if (strstr(buf, "create") || strstr(buf, "mkdir")
+ || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2'))
+ || strstr(buf, "mknod") ) {
+ p = find_reply_trace_fh(reply_buf);
+ if (p==NULL) {
+ printf("skip line disk_index %d %s \n", disk_index, buf);
+ continue;
+ }
+ memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE);
+ } else
+ memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE);
+ }
+
+ add_to_dep_tab(memory_trace_index.head);
+
+ if (((disk_index+1)%20000)==0) {
+ fprintf(stderr, "%d disk trace parsed \n", disk_index+1);
+ };
+ };
+
+ end_profile (&read_trace_profile);
+ return TRACE_BUF_FULL;
+}
+#else /* not defined REDUCE_MEMORY_TRACE_SIZE */
+int read_trace ()
+{
+ FILE * fp;
+ char buf[1024];
+ // char * t=buf;
+ int disk_index=0;
+
+ fp = fopen(trace_file, "r");
+ RFS_ASSERT (fp!=NULL);
+ while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) {
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("strlen(buf) %d buf %s \n", strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+
+ /* store the request to memory */
+ strcpy (memory_trace[memory_trace_size].line, buf);
+ memory_trace[memory_trace_size].disk_index = disk_index;
+ memory_trace_size ++;
+
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ if ((disk_index%100000)==0)
+ fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size);
+ disk_index ++;
+ }
+
+ fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size );
+}
+#endif
+
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline int disk_index_to_memory_index (int disk_index)
+{
+ static int memory_index = 0;
+
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index);
+ RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index);
+ if (disk_index > memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index < disk_index) {
+ memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size);
+ }
+ };
+ if (disk_index < memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index > disk_index) {
+ memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size);
+ }
+ };
+
+ RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index);
+ return memory_index;
+}
+#else
+#define disk_index_to_memory_index(disk_index) disk_index
+#endif
+
+#define get_line_by_disk_index(disk_index) \
+ memory_trace[disk_index_to_memory_index(disk_index)].line
+
+inline char * find_reply_line (char * command_line, int cur_disk_index)
+{
+ int i;
+ char * line;
+ char * p;
+ int request_memory_index = disk_index_to_memory_index (cur_disk_index);
+ for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&line[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS])))
+ return line;
+ }
+ }
+ return NULL;
+}
+
+inline int find_reply_status (char * line)
+{
+ char * p;
+ int i=0;
+
+ if (rfs_debug)
+ printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ if (!(line[TRACE_COMMAND_REPLY_FLAG_POS]=='R')) {
+ printf ("disk_index %d %s\n", disk_index, line);
+ };
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */
+ if (strstr(p, "OK"))
+ return NFS3_OK;
+ if (strstr(p, "lookup 2") || strstr(p, "lookup 2"))
+ return 0x2;
+ if (strstr(p, "create d"))
+ return 0xd;
+ if (strstr(p, "setattr 1"))
+ return 0x1;
+ if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */
+ return 0x2712;
+ if (strstr(p, "lookup d"))
+ return 0xd;
+ if (strstr(p, "read d"))
+ return 0xd;
+ if (strstr(p, "write d"))
+ return 0xd;
+ if (strstr(p, "write 46"))
+ return 0x46;
+ if (strstr(p, "getattr 46"))
+ return 0x46;
+ if (strstr(p, "mkdir d"))
+ return 0xd;
+ printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n");
+}
+
+inline int find_reply_status_old (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ if (!strstr(line, "OK")) {
+ p=strstr(line, " 6 read ");
+ if (p) {
+ p+=strlen(" 6 read ");
+ } else {
+ p = strstr (line, "status=XXX");
+ RFS_ASSERT (p);
+ p--;
+ RFS_ASSERT (*p==' ');
+ while (*p==' ')
+ p--;
+ while (*p!=' ') {
+ p--;
+ }
+ p++;
+ }
+ sscanf (p, "%x", &i);
+ if ((i<=0) || (i>10000))
+ printf("line %s\n", line);
+ RFS_ASSERT (i>0 && i<10009);
+ }
+ return i;
+}
+
+inline char * find_reply_trace_fh (char * line)
+{
+ char * p;
+ p = strstr (line, "OK fh");
+ if (!p) {
+ printf ("disk_index %d find_reply_trace_fh line %s\n", disk_index, line);
+ return NULL;
+ } else
+ return p+6;
+}
+
+#ifndef NO_DEPENDENCY_TABLE
+inline int disk_index_to_dep_index(int cur_dep_index, int disk_index)
+{
+ int i;
+ for (i=cur_dep_index; i>min_dep_index; i--) {
+ if (dep_tab[i].disk_index == disk_index)
+ return i;
+ }
+ RFS_ASSERT (0);
+}
+#endif
+
+inline int is_play_candidate (int dep_index)
+{
+ int proc = dep_tab[dep_index].proc;
+ int status = dep_tab[dep_index].status;
+ int trace_status = dep_tab[dep_index].trace_status;
+
+#ifndef TAKE_CARE_CREATE_MODE_BY_DAN
+ /* for a failed create in trace, trace_replay just ignore many time the trace create fail
+ * due to access control, but trace_play will success because our access control
+ * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests
+ */
+ if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) {
+ if (dependency_debug)
+ printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+ failed_create_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_OTHER_FAILED_COMMAND
+ if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) ||
+ ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){
+ if (dependency_debug)
+ printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+
+ failed_other_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */
+ skipped_readlink_command_num ++;
+ return FALSE;
+ }
+#endif
+/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */
+#ifndef TAKE_CARE_CUSTOM_COMMAND
+ /* this line has a file handle which should belong to discard but it is not
+ * the file handle directly appears as parent directory in a lookup request
+ * the return value is NOENT, the parent directory should have been initialized
+ * but the initialization code just ignored all lookup request which didn't success
+ * including NOENT even though the parent directory is still valid.
+ */
+/*
+ if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402))
+ && !(strcmp(trace_file, "anon-lair62-011130-1100.txt"))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+*/
+ if (( ((dep_tab[dep_index].disk_index==423727) || (0))
+ && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt")))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+ /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */
+ if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_FSSTAT_COMMAND
+ /* the file handle used in this command is not processed properly by pre-processing */
+ if (proc==FSSTAT) {
+ char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ fh_map_t * fh = lookup_fh (trace_fh);
+ if (!fh) {
+ skipped_fsstat_command_num++;
+ return FALSE;
+ }
+ }
+#endif
+ return TRUE;
+}
+
+inline int is_dir_op (int proc)
+{
+ switch (proc) {
+ case MKDIR:
+ case CREATE:
+ case LINK:
+ case SYMLINK:
+ case MKNOD:
+ case REMOVE:
+ case RMDIR:
+ case RENAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+inline int is_create_op (int proc)
+{
+ if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline int is_delete_op (int proc)
+{
+ if (proc==REMOVE || proc==RMDIR || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+static inline char * find_lead_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh");
+ RFS_ASSERT (p);
+ p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+inline char * find_another_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh2");
+ RFS_ASSERT (p);
+ p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+/* return the index of next request in dep_tab.
+ * Return -1 if there is no suitable request to send
+ */
+inline int get_nextop(void)
+{
+ int i,j, k;
+ int * t;
+ static int dep_index = -2;
+ char * line;
+ char * p;
+#define INIT_MIN_WAIT_VALUE -999
+ static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE;
+ int proc;
+ int flag;
+
+ if (min_wait_fhandle_dep_index == -999)
+ min_wait_fhandle_dep_index = dep_window_index.head;
+
+ for (i=0; i<CYCLIC_NUM(dep_window_index); i++) {
+ dep_index = (dep_window_index.tail+i) % dep_window_index.size;
+
+ proc = dep_tab[dep_index].proc;
+ flag = dep_tab[dep_index].flag;
+
+ if (dependency_debug)
+ printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+#ifdef NO_DEPENDENCY_TABLE
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT) {
+ if (is_play_candidate(dep_index)==TRUE) {
+ /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle
+ * used in the request */
+ if (proc==LINK || proc==RENAME) {
+ dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = 0;
+ } else {
+ dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = (fh_map_t *)1;
+ };
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+#ifdef TIME_PLAY
+ dep_tab[dep_index].skip_sec = skip_sec;
+#endif
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index);
+ } else {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) {
+
+ if (!dep_tab[dep_index].fh)
+ dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh);
+ if (!dep_tab[dep_index].fh_2)
+ dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2);
+
+ /* need to wait for file handle */
+ if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) {
+ if (dependency_debug)
+ printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE;
+ sfs_gettime (&dep_tab[dep_index].start);
+ if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index))
+ min_wait_fhandle_dep_index = dep_index;
+ } else {
+ struct ladtime tmp;
+ if (dep_index==dep_window_index.tail) {
+ if (!profile_debug)
+ printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ fh_path_map_err_num ++;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ sfs_gettime (&tmp);
+ SUBTIME (tmp, dep_tab[dep_index].start);
+#define DEPENDENCY_TIMEOUT 50
+#ifdef TIME_PLAY
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec));
+#else
+ if (tmp.sec >= DEPENDENCY_TIMEOUT) {
+ printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index,
+ dep_tab[dep_index].flag, dep_tab[dep_index].disk_index,
+ dep_tab[dep_index].line);
+ }
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT );
+#endif
+ }
+ continue;
+ }
+
+ /* file handle ready, adjust_min_wait_fhandle_dep_index */
+ if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) {
+ if (dep_index == min_wait_fhandle_dep_index) {
+ min_wait_fhandle_dep_index = dep_window_index.head;
+ for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) {
+ if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) {
+ min_wait_fhandle_dep_index = j;
+ break;
+ }
+ }
+ }
+ }
+ if (dependency_debug)
+ printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY;
+
+ /* the normal file operation can be executed now */
+ if (!is_dir_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ if (dependency_debug)
+ printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index);
+ /* the directory operation need to lock the directory first */
+ if (dep_tab[dep_index].fh->lock) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) {
+ int j = dep_tab[dep_index].fh - fh_map;
+ if (dependency_debug) {
+ printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock);
+ printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) {
+ if (dependency_debug)
+ printf ("continue to wait for directory lock\n");
+ continue;
+ }
+ if (dependency_debug)
+ printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j);
+ dep_tab[dep_index].fh->lock = 1;
+ if (proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 1;
+
+ /* the non-delete directory operation can proceed now */
+ if (!is_delete_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ /* the delete operation can proceed if nobody ahead is waiting for fhandle */
+ /* probably this condition is not strong enough */
+// if ((min_wait_fhandle_dep_index<dep_index) ) {
+ if (dep_index!=dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE;
+ continue;
+ }
+ dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY;
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) {
+// if (min_wait_fhandle_dep_index > dep_index) {
+ if (dep_index==dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ }
+#else /*NO_DEPENDENCY_TABLE undefined */
+ /* this part of code will be invalid after CYCLIC buffer design */
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT){
+ for (j=0, t=&(dep_tab[dep_index].dep_ops[0]);
+ (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0);
+ j++, t++) {
+ if (*t !=-1) {
+ if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) {
+ /* The depended request has been finished */
+ *t = -1;
+ dep_tab[dep_index].cur_dep_num --;
+ }
+ }
+ }
+
+ if (dep_tab[dep_index].cur_dep_num == 0) {
+ return dep_index;
+ }
+ }
+#endif
+ }
+
+ if (dependency_debug)
+ printf ("get_nexop return -1\n");
+ return -1;
+}
+
+int check_timeout(void)
+{
+ static int biod_index = 0;
+ int i;
+ int dep_index; /* index into dep_tab */
+ int proc;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime timeout;
+ struct ladtime current_time;
+
+ sfs_gettime (¤t_time);
+
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (biod_reqp[biod_index].in_use==TRUE) {
+ timeout = biod_reqp[biod_index].timeout;
+ if ((current_time.sec>timeout.sec) ||
+ ((current_time.sec==timeout.sec) && (current_time.usec>timeout.usec))) {
+
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+ op_ptr->results.timeout_calls++;
+ Ops[TOTAL].results.timeout_calls++;
+
+
+ if (is_create_op(proc)) {
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+ printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_CANDIDATE);
+ } else {
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_DONE);
+ }
+ timeout_num ++;
+ num_out_reqs_statistics_at_timeout[num_out_reqs]++;
+
+ //RFS_ASSERT (!is_create_op(proc));
+
+ if (per_packet_debug)
+ printf ("timeout request: disk_index %d, dep_index %d biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", dep_tab[dep_index].disk_index, dep_index, biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec);
+ }
+ }
+ }
+}
+
+/* Allocate a biod_req entry to send and receive request dep_tab[dep_index]
+ * build the cross reference between dep_tab entry and biod_req entry
+ */
+struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (!biod_reqp[biod_index].in_use) {
+ biod_reqp[biod_index].in_use = 1;
+ biod_reqp[biod_index].dep_tab_index = dep_index;
+ dep_tab[dep_index].biod_req_index = biod_index;
+ num_out_reqs++;
+ return &(biod_reqp[biod_index]);
+ }
+ }
+ return NULL;
+}
+
+/* Return index into biod_reqp
+ * return -1 upon failure
+ */
+int lookup_biod_req (int xid)
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ /* give a NULL as timeout pointer may cause indefinitely block */
+ if ((biod_reqp[biod_index].in_use == TRUE) &&( biod_reqp[biod_index].xid == xid)) {
+ return biod_index;
+ }
+ }
+ return -1;
+}
+
+extern struct ladtime test_start;
+void init_time_offset(void)
+{
+ struct ladtime tmp1;
+ struct ladtime tmp2;
+
+ test_start.sec = 0;
+ test_start.usec = 0;
+ sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */
+#ifdef SPEED_UP
+ DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */
+#endif
+
+ tmp2 = trace_starttime; /* tmp2 = trace_starttime */
+ SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */
+ time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void init_dep_tab_entry (int dep_index)
+{
+ char * line;
+ int version;
+ int nfsproc;
+ int msgid;
+
+ //line = get_line_by_disk_index (dep_tab[dep_index].disk_index);
+ line = dep_tab[dep_index].line;
+ sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec));
+ sscanf (&line[TRACE_MSGID_POS], "%x %x", &msgid, &nfsproc);
+ //printf ("msgid %x nfsproc %x\n", msgid, nfsproc);
+ if (line[TRACE_VERSION_POS]=='2') {
+ dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc];
+ RFS_ASSERT (nfsproc <18);
+ } else {
+ /* This is for debug purpose */
+ if (line[TRACE_VERSION_POS] !='3') {
+ fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line);
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1);
+ if (!line)
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2);
+ RFS_ASSERT (line);
+ fprintf(stderr, "previousline %s\n", line);
+ }
+ RFS_ASSERT (line[TRACE_VERSION_POS] =='3');
+ if (nfsproc >= NFS3_PROCEDURE_COUNT) {
+ fprintf(stderr, "proc %d line %s\n", nfsproc, line);
+
+ }
+ RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT);
+ dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc];
+ }
+ RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS);
+ dep_tab[dep_index].flag = DEP_FLAG_INIT;
+}
+
+void adjust_play_window (int flag, int * poll_timeout_arg)
+{
+ struct ladtime max_window_time;
+ static struct ladtime max_poll_time = {0, 2000, 0};
+ struct ladtime t;
+ int i;
+ char * line;
+ cyclic_index_t old_dep_window_index = dep_window_index;
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, begin\n");
+#endif
+
+ while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) {
+#ifdef notdef
+ //CYCLIC_PRINT (memory_trace_index);
+ //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index);
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+ printf("%d is done\n", dep_window_index.tail);
+#endif
+ CYCLIC_MOVE_TAIL(dep_tab_index);
+ CYCLIC_MOVE_TAIL(dep_window_index);
+
+#ifdef notdef
+ CYCLIC_PRINT (dep_tab_index);
+ CYCLIC_PRINT (dep_window_index);
+
+ if (! (dep_tab_index.tail == dep_window_index.tail)) {
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+ };
+ RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail);
+#endif
+
+ if (!CYCLIC_EMPTY(dep_tab_index)) {
+#ifdef notdef
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) {
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n",
+ dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index,
+ dep_tab[dep_tab_index.tail].memory_index);
+ }
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+#endif
+ CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index);
+ //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail);
+ } else {
+ // CYCLIC_MOVE_TAIL (memory_trace_index);
+ }
+ }
+
+ while (CYCLIC_EMPTY(dep_tab_index)) {
+
+ if (disk_io_status == TRACE_FILE_END)
+ return;
+ else {
+ //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n");
+ //print_cyclic_buffers();
+ //pthread_yield();
+ //usleep (1000);
+ }
+ }
+
+ /* max_trace_window_time = current *|/ SCALE + trace_starttime */
+ sfs_gettime (¤t);
+
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+ MULTIME (current, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ DIVTIME (current, PLAY_SCALE);
+#endif
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+
+ /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */
+ if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) {
+ //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index);
+ //printf (".");
+ can_not_catch_speed_num ++;
+ }
+ while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) &&
+ (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) {
+ struct ladtime t;
+ int dep_index = (dep_window_index.tail+i) % dep_window_index.size;
+ t.sec = dep_tab[dep_index].timestamp.tv_sec;
+ t.usec = dep_tab[dep_index].timestamp.tv_usec;
+ if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec))
+ break;
+ CYCLIC_MOVE_HEAD(dep_window_index);
+ }
+#else
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+ while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) &&
+ (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) {
+ CYCLIC_MOVE_HEAD(dep_window_index);
+ }
+#endif
+
+ if (flag == BUSY)
+ *poll_timeout_arg = 0;
+ else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) {
+ *poll_timeout_arg = 1000; /* poll_timeout set to 1 second for the last request */
+ } else {
+#ifdef TIME_PLAY
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec;
+ if (adjust_play_window_debug>=2)
+ printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec);
+
+ SUBTIME (tmp, max_window_time);
+#ifdef SPEED_UP
+ DIVTIME (tmp, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp, PLAY_SCALE);
+#endif
+/*
+ tmp1 = tmp;
+
+ if (tmp.sec > max_poll_time.sec) {
+
+ if (rfs_debug)
+ printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec);
+ printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec);
+ SUBTIME (tmp, max_poll_time);
+ tmp.usec = 0;
+ skip_sec += tmp.sec;
+ SUBTIME (test_start, tmp);
+ tmp = max_poll_time;
+ }
+*/
+
+ //RFS_ASSERT ((tmp.sec < 1000));
+ if (tmp.sec > 1000)
+ tmp.sec = 1000;
+ if ((tmp.sec ==0) && (tmp.usec==0)) {
+ *poll_timeout_arg = 0;
+ } else
+ *poll_timeout_arg = tmp.sec*1000000+tmp.usec;
+#else
+ /*
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec;
+ tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec;
+ tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec;
+ SUBTIME (tmp, tmp1);
+ RFS_ASSERT ((tmp.sec < 1000));
+ RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0)));
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+ */
+
+ *poll_timeout_arg = 100000;
+#endif
+ }
+ if (rfs_debug)
+ printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout_arg %d \n",
+ flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head,
+ dep_window_index.head, *poll_timeout_arg);
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, end\n\n");
+#endif
+ //CYCLIC_ASSERT(4);
+}
+
+/* poll for usecs and receive, after receive one reply,
+ * return index in biod_reqp of the corresponding request
+ */
+int poll_and_get_reply (int usecs)
+{
+ int biod_index = -1;
+ int xid;
+ int error;
+ struct timeval zero_time = {0, 0}; /* Immediately return */
+
+#ifdef RECV_THREAD
+ //printf("recv thread waitsem 1\n");
+ waitsem (async_rpc_sem);
+ //printf("recv thread got sem 1\n");
+#endif
+ do {
+ error = biod_poll_wait (NFS_client, usecs);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (rfs_debug) {
+ (void) fprintf(stderr, "biod_poll_wait error\n");
+ perror ("");
+ (void) fflush(stderr);
+ }
+ break;
+ case 0:
+ break;
+ default:
+#ifdef UDP
+ //printf("recv thread waitsem 2\n");
+ //waitsem (async_rpc_sem);
+ //printf("recv thread got sem 2\n");
+ error = get_areply_udp (NFS_client, &xid, &zero_time);
+ //postsem (async_rpc_sem);
+ //printf("recv thread postsem 2\n");
+ // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */
+ // RFS_ASSERT (error!= RPC_CANTRECV);
+ RFS_ASSERT (error == RPC_SUCCESS);
+
+ biod_index = lookup_biod_req (xid);
+ sfs_gettime (&(biod_reqp[biod_index].stop));
+#else
+ RFS_ASSERT (0);
+#endif
+ }
+ } while (0);
+#ifdef RECV_THREAD
+ postsem (async_rpc_sem);
+ //printf("recv thread postsem 1\n");
+#endif
+ return biod_index;
+}
+
+void print_result(void)
+{
+ int i, j;
+ struct ladtime t;
+ int dep_index;
+ int avg_msecs;
+ unsigned long long tmp;
+ int avg_usecs;
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stdout, "trace play result:\n");
+ (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n");
+ for (i=0; i<NOPS+1; i++) {
+ if (Ops[i].results.good_calls==0) {
+ avg_msecs = 0;
+ avg_usecs = 0;
+ } else {
+ tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec;
+ avg_msecs = 0;
+ avg_usecs = tmp/Ops[i].results.good_calls;
+/*
+ avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls;
+ avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls;
+*/
+ }
+ (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n",
+ Ops[i].name,
+ (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls,
+ Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls,
+ Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs);
+ }
+ (void) fflush (stdout);
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ RFS_ASSERT (read_data_owe_GB==0);
+ printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times);
+ printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times);
+ printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num);
+ printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n",
+ failed_create_command_num,
+ failed_other_command_num,
+ skipped_readlink_command_num,
+ skipped_custom_command_num,
+ fh_path_map_err_num,
+ skipped_fsstat_command_num,
+ missing_reply_num,
+ rename_rmdir_noent_reply_num,
+ rmdir_not_empty_reply_num,
+ loose_access_control_reply_num,
+ lookup_err_due_to_rename_num,
+ lookup_err_due_to_parallel_remove_num,
+ lookup_eaccess_enoent_mismatch_num,
+ read_io_err_num,
+ stale_fhandle_err_num,
+ abnormal_EEXIST_num,
+ abnormal_ENOENT_num,
+ proper_reply_num, run_stage_proper_reply_num);
+#endif
+
+// print_dump(Client_num, Child_num);
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ int dummy = 0;
+
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+ if (biod_init(dummy, dummy) == -1) {
+ return(-1);
+ }
+
+ return(0);
+} /* init_rpc */
+
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.timeout_calls = 0; // RFS
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "Timeout");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "trace play usage");
+}
+extern void init_file_system (void)
+{
+ return;
+}
+
+void show_fhandle (nfs_fh3 * fhp)
+{
+ struct knfs_fh * kfhp = (struct knfs_fh *)fhp;
+
+ int dev;
+
+ if (quiet_flag)
+ return;
+
+ RFS_ASSERT (kfhp->fh_version == 1);
+ RFS_ASSERT (kfhp->fh_fsid_type == 0);
+ RFS_ASSERT (kfhp->fh_auth_type == 0);
+
+ dev = ntohs(kfhp->fh_dev_major);
+ dev = dev<<8;
+ dev = dev + ntohs(kfhp->fh_dev_minor);
+
+ /* kfhp->fh_dev_ino hold the inode number of export point of the mounted
+ * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted,
+ * then fh_dev_ino hold the inode number of t1, not t2
+ */
+
+ switch (kfhp->fh_fileid_type) {
+ case 0:
+ printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino);
+ break;
+ case 1:
+ printf("fh:type 1 %d %x dev %x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino);
+ break;
+ case 2:
+ printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino);
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+}
+
+nfs_fh3 zero_fhandle;
+int init_fh_map ()
+{
+ memset (fh_map, 0, sizeof (fh_map));
+ memset(fh_htable, 0, sizeof (fh_htable));
+ memset (&zero_fhandle, 0, sizeof(nfs_fh3));
+ printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000);
+ fh_i = 0;
+}
+
+int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh)
+{
+ char * old_trace_fh;
+
+ /* first lookup if the entry for fh is already in the table */
+ struct generic_entry * p;
+
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (p) {
+ RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL);
+ RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE);
+ fh_map[p->key3].flag = map_flag;
+ //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) {
+ int i;
+ printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh);
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (!strcmp(fh_map[p->key3].path, path));
+ /* It's possible that in fh-path-map, many trace_fh are corresponding to one path
+ * some of it may be the result of lookup after symlink, which is not handled
+ * properly as new created objects
+ */
+#ifdef TAKE_CARE_SYMBOLIC_LINK
+ RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3)));
+#endif
+ memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3));
+ if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE))
+ printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path);
+ return 0;
+ }
+
+ fh_map[fh_i].flag = map_flag;
+ fh_map[fh_i].lock = 0;
+ strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE);
+
+ RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE);
+ strcpy (fh_map [fh_i].path, path);
+ if (map_flag==FH_MAP_FLAG_COMPLETE)
+ memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3));
+ else
+ memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3));
+
+ if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) {
+ printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path);
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ //show_fhandle(play_fh);
+ } else
+ printf("null\n");
+ }
+
+/*
+ if (map_flag == FH_MAP_FLAG_DISCARD)
+ printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path);
+*/
+
+ generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE);
+
+ fh_i = (fh_i+1);
+ RFS_ASSERT (fh_i < FH_MAP_SIZE);
+
+ return 0;
+};
+
+inline fh_map_t * lookup_fh (char * trace_fh )
+{
+ struct generic_entry * p;
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (fh_map_debug==1)
+ printf ("lookup trace_fh %s\n", trace_fh);
+
+ if (p) {
+ if (fh_map_debug==1) {
+ printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path);
+ //show_fhandle(&fh_map[p->key3].play_fh);
+ }
+ RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ return (&(fh_map[p->key3]));
+ } else {
+ //printf ("lookup_fh %s not found\n", trace_fh);
+ if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) {
+ printf ("lookup not found trace_fh %s\n", trace_fh);
+ }
+ return NULL;
+ }
+ RFS_ASSERT (0);
+}
+
+int delete_fh (char * trace_fh, int fh_map_index)
+{
+ generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE);
+ return 0;
+};
+
+int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ static int i=0;
+
+ /* set up the arguments */
+ (void) memcpy((char *) &args.what.dir, (char *) parent,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+ sfs_gettime(&stop);
+
+ if (rpc_stat !=RPC_SUCCESS) {
+ printf("rpc_stat %d\n", rpc_stat);
+ perror("");
+ }
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3));
+ return (reply.status);
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[MAX_TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+#define MAX_PATH_DEPTH 20
+ nfs_fh3 parents[MAX_PATH_DEPTH];
+ char * lookup_path_ptr[MAX_PATH_DEPTH];
+ char lookup_path [MAX_PLAY_PATH_SIZE];
+ int depth;
+ int new_dir_flag = 0;
+ int lineno = 0;
+
+ depth = 0;
+ memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr));
+ memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ strcpy(lookup_path, "/");
+ lookup_path_ptr[depth]=&lookup_path[0];
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+ if (strstr(fh_map_file, "fmt1")) {
+ TRACE_FH_SIZE = 48;
+ }
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+ lineno ++;
+ if (fh_i % 10000==0)
+ printf("%d fh_map entry read\n", fh_i);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+ if (fh_map_debug) {
+ printf("%d fgets return %s\n", fh_i, buf);
+ printf("depth %d lookup_path %s\n", depth, lookup_path);
+ }
+ //for (i=0; i<=depth; i++)
+ //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]);
+ //printf("\n");
+#ifdef COMPRESS_TRACE_FH
+ for (i=0; i<TRACE_FH_SIZE/8; i++) {
+ strncpy(intbuf, buf+i*8, 8);
+ sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now
+ }
+ trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */
+#else
+ memcpy(trace_fh, buf, TRACE_FH_SIZE);
+ trace_path = buf + TRACE_FH_SIZE +1;
+#endif
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */
+#endif
+
+#ifdef NO_DEPENDENCY_TABLE
+ if (!strncmp (trace_path, "DISCARD", 7) ||
+ !strncmp (trace_path, "LN", 2) ) {
+ map_flag = FH_MAP_FLAG_DISCARD;
+ add_fh (map_flag, buf, trace_path, 0);
+ continue;
+ }
+#endif
+
+ p = trace_path+strlen(trace_path)-2;
+ while (*p!='/')
+ p--;
+ p++;
+ //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1);
+ //RFS_ASSERT (p>trace_path);
+
+ if (strncmp(lookup_path, trace_path, p-trace_path)) {
+ printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path);
+ }
+ RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path));
+ //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */
+ while (strlen(lookup_path)>p-trace_path && depth>0) {
+ //printf("depth--\n");
+ if (depth<=0)
+ printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth);
+ RFS_ASSERT (depth>0);
+ *lookup_path_ptr[depth]=0;
+ lookup_path_ptr[depth]=0;
+ depth--;
+ }
+ RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0));
+
+
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ if (buf[TRACE_FH_SIZE*2+1]=='Y') {
+ map_flag = FH_MAP_FLAG_COMPLETE;
+ } else {
+ map_flag = FH_MAP_FLAG_PARTIAL;
+ RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N');
+ }
+#else
+ map_flag = FH_MAP_FLAG_COMPLETE;
+#endif
+ if ((*(p+strlen(p)-1))=='/') {
+ *(p+strlen(p)-1)=0;
+ new_dir_flag = 1;
+ } else
+ new_dir_flag = 0;
+
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]);
+ if (ret!=NFS3_OK) {
+ printf ("lineno %d %s\n", lineno, buf);
+ }
+ RFS_ASSERT (ret == NFS3_OK);
+ add_fh (map_flag, buf, trace_path, &parents[depth+1]);
+ } else
+ add_fh (map_flag, buf, trace_path, 0);
+
+ if (new_dir_flag) {
+ /* the new fhandle is of a directory */
+ lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path);
+ strcat (lookup_path, p);
+ strcat (lookup_path, "/");
+
+ //printf("depth++\n");
+ depth++;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (fh_map_debug) {
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+
+ fprintf(stderr, "total %d requests \n", i);
+ }
+}
+
+int f()
+{
+ return 1;
+}
+
+inline free_biod_req (int biod_index)
+{
+ RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE);
+ biod_reqp[biod_index].in_use = FALSE;
+ num_out_reqs --;
+}
+
+void finish_request (int biod_index, int dep_index, int status, int dep_flag)
+{
+ static int i = 0;
+ /* the ending operation, same as when a request time out */
+
+ dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */
+ free_biod_req (biod_index);
+
+ dep_tab[dep_index].status = status;
+
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = -dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].flag = dep_flag;
+ if (is_dir_op(dep_tab[dep_index].proc)) {
+ int j;
+ RFS_ASSERT (dep_tab[dep_index].fh->lock = 1);
+ dep_tab[dep_index].fh->lock = 0;
+ if (dep_tab[dep_index].proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 0;
+ j = dep_tab[dep_index].fh-fh_map;
+ if (dependency_debug) {
+ printf ("fh_map[%d] is UNlocked\n",j);
+ printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ }
+}
+
+/* the request argument may have pointers pointing to buffers, e.g. the name in lookup,
+ * the target of symlink, the write data */
+char arg_res[MAX_ARG_RES_SIZE];
+char buf1 [MAX_BUF1_SIZE];
+char buf2 [MAX_BUF2_SIZE];
+
+int execute_next_request ()
+{
+ int dep_index;
+ int proc;
+ char * line;
+ struct biod_req * reqp;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime call_timeout;
+ static int last_print_time = -1;
+
+ if (num_out_reqs == max_biod_reqs) {
+ return -1;
+ }
+
+ start_profile (&valid_get_nextop_profile);
+ start_profile (&invalid_get_nextop_profile);
+ dep_index = get_nextop();
+ if (dep_index == -1) {
+ end_profile (&invalid_get_nextop_profile);
+ return dep_index;
+ };
+ end_profile (&valid_get_nextop_profile);
+
+ start_profile (&prepare_argument_profile);
+ line = dep_tab[dep_index].line;
+
+ if (per_packet_debug)
+ fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+
+ end_profile(&total_profile);
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ last_print_time = total_profile.in.tv_sec;
+ //fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+/*
+ CYCLIC_PRINT (dep_tab_index);
+ {
+ int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size);
+ printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index,
+ dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index);
+ }
+*/
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+/*
+ if (can_not_catch_speed_num < 2000) {
+ PLAY_SCALE ++;
+ printf ("set PLAY_SCALE to %d\n", PLAY_SCALE);
+ };
+ if (can_not_catch_speed_num > 50000) {
+ PLAY_SCALE /= 2;
+ } else {
+ if (can_not_catch_speed_num > 5000) {
+ PLAY_SCALE -= 2;
+ if (PLAY_SCALE < 1)
+ PLAY_SCALE = 1;
+ }
+ }
+*/
+#endif
+ if ((total_profile.in.tv_sec > 100)) {
+ can_not_catch_speed_num_total += can_not_catch_speed_num;
+ }
+ can_not_catch_speed_num = 0;
+#endif
+ }
+ if (rfs_debug)
+ printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line);
+
+ proc = dep_tab[dep_index].proc;
+ rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2);
+
+ op_ptr = &Ops[proc];
+ reqp = get_biod_req (dep_index);
+ RFS_ASSERT (reqp);
+
+#ifdef notdef /* place to set request timeout. G. Jason Peng */
+ call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+#else
+ call_timeout.sec = 0;
+ call_timeout.usec = 300000;
+ //call_timeout.usec = 14000;
+ //call_timeout.usec = 13000;
+ //call_timeout.usec = 6000;
+ //call_timeout.usec = 8000;
+ //call_timeout.usec = 10000;
+#endif
+
+ /* make the call */
+ sfs_gettime(&(reqp->start));
+ end_profile (&prepare_argument_profile);
+ start_profile (&biod_clnt_call_profile);
+#define REAL_PLAY
+#ifdef REAL_PLAY
+
+#ifdef RECV_THREAD
+ //printf ("send thread waitsem\n");
+ waitsem(async_rpc_sem);
+ //printf ("send thread got sem\n");
+#endif
+ reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc,
+ rfs_Ops[proc].xdr_arg, arg_res);
+#ifdef RECV_THREAD
+ postsem(async_rpc_sem);
+ //printf ("send thread postsem\n");
+#endif
+
+#else // REAL_PLAY
+ reqp->xid = dep_index+1; /* just fake a message id and let it expire */
+#endif
+ RFS_ASSERT (reqp->xid != 0);
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ dep_tab[dep_index].flag = DEP_FLAG_SENT;
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */
+ end_profile (&biod_clnt_call_profile);
+
+ send_num ++;
+}
+
+void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status)
+{
+ if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) {
+ if (!profile_debug)
+ printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index);
+#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+ /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they
+ * are refered by name through rename, remove
+ */
+ if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) {
+ /* current initialization doesnot take care of rename source, if there is no
+ * create or lookup before that source, the source object will not exist when
+ * rename occurs
+ */
+ rename_rmdir_noent_reply_num++;
+ } else
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) {
+ /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK
+ * the initialization considers only the lookup return OK and created the file in the initialization
+ * so in trace play the first lookup return OK
+ */
+ RFS_ASSERT (1);
+ } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) {
+ /* trace_status could be EAGAIN */
+ if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) {
+ /* due to similar reason as above, the initialization code initializes the symbolic link as a normal
+ * file already
+ */
+ RFS_ASSERT (1);
+ } else
+#endif
+#ifndef TAKE_CARE_NOEMPTY_RMDIR
+ /* the remove packet seems got lost in the trace capture, so replay can not finish */
+ if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) {
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ RMDIR3args * rmdir_argp;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2);
+ rmdir_argp = (RMDIR3args *)arg_res;
+
+ memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3));
+ memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */
+ args.to.name = buf2;
+ sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name);
+
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ Nfs_timers[Init]);
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ if (reply.status!=NFS3_OK)
+ printf ("change rmdir into rename, reply.status %d\n", reply.status);
+ RFS_ASSERT (reply.status==NFS3_OK);
+ rmdir_not_empty_reply_num ++;
+#endif
+#ifndef TAKE_CARE_ACCESS_ERROR
+ } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) {
+ loose_access_control_reply_num ++;
+#endif
+#ifdef NO_DEPENDENCY_TABLE
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) {
+ lookup_err_due_to_rename_num ++;
+ } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) {
+ /* if there is a remove in front of the lookup, but it is
+ * actually executed later than the lookup
+ */
+ lookup_err_due_to_parallel_remove_num ++;
+#endif
+#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH
+ /* if the looked return EACCESS in the trace, means the object still exists
+ * should have initialized, right not don't initialize it, hence play status
+ * could be ENOENT
+ */
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) {
+ lookup_eaccess_enoent_mismatch_num ++;
+#endif
+#ifdef TOLERANT_READ_IO_ERR
+ } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) {
+ read_io_err_num ++;
+#endif
+#ifdef TOLERANT_STALE_FHANDLE_ERR
+ } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) {
+ printf ("!!!!!!! STALE FILE HANDLE \n");
+ //sleep(1);
+ stale_fhandle_err_num ++;
+#endif
+ } else {
+ int i;
+ for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++) {
+ if (dep_tab[i].flag!=1)
+ printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line);
+ }
+
+ if (status==EEXIST) {
+ abnormal_EEXIST_num ++;
+ } else if (status == ENOENT) {
+ abnormal_ENOENT_num ++;
+ } else {
+ printf ("!!!!!!!!!!!!!1 should fail\n");
+ //RFS_ASSERT (0);
+ }
+ }
+ } else {
+ proper_reply_num ++;
+ if (total_profile.in.tv_sec >= WARMUP_TIME)
+ run_stage_proper_reply_num ++;
+ }
+
+}
+
+/* return -1 if there is no reply being received
+ * return the dep_index if the corresponding reply has been received
+ */
+int receive_next_reply (int busy_flag)
+{
+ int dep_index;
+ int biod_index;
+ int proc;
+ char * line;
+ char * reply_line;
+ sfs_op_type *op_ptr; /* per operation info */
+ int ret;
+ int status;
+ int trace_status;
+ char * errmsg;
+ int poll_timeout = 0; /* timeout in usecs */
+
+ /* wait for reply */
+ start_profile (&valid_poll_and_get_reply_profile);
+ start_profile (&invalid_poll_and_get_reply_profile);
+
+ if (busy_flag == BUSY) {
+ poll_timeout = 0;
+ poll_timeout_0_num ++;
+ } else {
+ poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */
+ //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */
+ poll_timeout_pos_num ++;
+ }
+
+ biod_index = poll_and_get_reply (poll_timeout);
+ if (biod_index==-1) {
+ end_profile (&invalid_poll_and_get_reply_profile);
+ return -1;
+ };
+ end_profile (&valid_poll_and_get_reply_profile);
+
+ start_profile (&decode_reply_profile);
+ /* check the corresponding request */
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ if (biod_reqp[biod_index].in_use==1) {
+ RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index);
+ } else {
+ printf ("biod_index %d reply received but the request has been time out\n", biod_index);
+ return -1;
+ }
+
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+
+ if (dep_tab[dep_index].flag != DEP_FLAG_SENT) {
+ printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n",
+ dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status,
+ dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec,
+ dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec );
+ printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT);
+
+ /* decode the reply */
+ rfs_Ops[proc].setres (arg_res, buf1);
+ ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res);
+ RFS_ASSERT (ret == RPC_SUCCESS);
+ status = *((int *)arg_res);
+ errmsg = nfs3_strerror (status);
+ end_profile (&decode_reply_profile);
+
+ start_profile (&check_reply_profile);
+ /* compare with the reply in the trace */
+ line = dep_tab[dep_index].line;
+ reply_line = dep_tab[dep_index].reply_line;
+ trace_status = dep_tab[dep_index].trace_status;
+
+ if (per_packet_debug || rfs_debug )
+ fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec);
+
+ /* error checking */
+ check_reply (proc, biod_index, dep_index, status, errmsg, trace_status);
+
+ /* free resources */
+ finish_request (biod_index, dep_index, status, DEP_FLAG_DONE);
+ recv_num ++;
+
+ /* we set 100 seconds warm up time */
+ if ((total_profile.in.tv_sec >= WARMUP_TIME)) {
+ /* get statistics */
+ if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) {
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop));
+ end_profile (&check_reply_profile);
+ }
+
+ //start_profile (&add_create_object_profile);
+
+ if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) {
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_line);
+#endif
+ if (status!=NFS3_OK) {
+ /* commented out for 1022 */
+ printf ("!!!!!! Should have been an ASSERTION FAILURE \n");
+ RFS_ASSERT (proc==SYMLINK);
+ RFS_ASSERT (0);
+ } else {
+ if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2')
+ add_new_file_system_object(proc, dep_index, line, reply_line);
+ }
+ }
+ //end_profile (&add_create_object_profile);
+}
+
+inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line)
+{
+ char * child_trace_fh;
+ fh_map_t * parent_entryp;
+ char component_name[MAX_PLAY_PATH_SIZE];
+ char * parent_trace_fh;
+ char child_path[MAX_PLAY_PATH_SIZE];
+ post_op_fh3 * post_op_fh3_child;
+ char * reply_trace_fh;
+ nfs_fh3 * child_fh3;
+
+ parent_trace_fh = strstr (line, "fh");
+ RFS_ASSERT (parent_trace_fh);
+ parent_trace_fh +=3;
+ parent_entryp = lookup_fh (parent_trace_fh);
+ RFS_ASSERT (parent_entryp);
+ parse_name (parent_trace_fh+65, component_name);
+ strcpy (child_path, parent_entryp->path);
+ strcat (child_path, "/");
+ strcat (child_path, component_name);
+
+ /* find the corresponding create request */
+ //printf ("before find reply trace_fh reply_line %s\n", reply_line);
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ reply_trace_fh = dep_tab[dep_index].reply_trace_fh;
+#else
+ reply_trace_fh = find_reply_trace_fh (reply_line);
+#endif
+ RFS_ASSERT (reply_trace_fh != NULL);
+ switch (proc) {
+ case CREATE:
+ RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKDIR:
+ RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case SYMLINK:
+ RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKNOD:
+ RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case LOOKUP:
+ RFS_ASSERT (proc==LOOKUP);
+ child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' ');
+#endif
+ reply_trace_fh[TRACE_FH_SIZE] = 0;
+ add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ /* just to preserve the original reply line not changed */
+ reply_trace_fh[TRACE_FH_SIZE] = ' ';
+#endif
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void trace_play(void)
+{
+
+ /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY
+ * when either there is request to send or there is reply being
+ * received. Otherwise it is IDLE. The timeout for polling replies
+ * is set to 0 when BUSY, it is set to the waiting time to the first
+ * request outside of current <min_dep_index, dep_window_max> window when IDLE.
+ */
+ int busy_flag = BUSY;
+ //int dep_index; /* index into dependency table: dep_tab */
+ //int biod_index; /* index into outstanding requests: biod_reqp */
+ static int last_print_time = -1;
+ int poll_timeout = 0;
+
+#ifndef IO_THREAD
+ disk_io_status = read_trace();
+#endif
+
+ RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index));
+ CYCLIC_MOVE_HEAD(dep_window_index);
+
+ adjust_play_window(busy_flag, &poll_timeout);
+
+ start_profile (&total_profile);
+ while (!CYCLIC_EMPTY(dep_tab_index)) {
+ end_profile(&total_profile);
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ int i;
+
+ last_print_time = total_profile.in.tv_sec;
+ fprintf (stdout, ">>>> sendng thread: time %d send_num %d recv_num %d timeout_num %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, send_num, recv_num, timeout_num, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+ for (i=0; i<=MAX_OUTSTANDING_REQ; i++) {
+ if (num_out_reqs_statistics[i]!=0) {
+ printf("num_out_req[%d]=%d,", i, num_out_reqs_statistics[i]);
+ num_out_reqs_statistics[i]=0;
+ }
+ }
+ printf("\n");
+ for (i=0; i<=MAX_OUTSTANDING_REQ; i++) {
+ if (num_out_reqs_statistics_at_timeout[i]!=0) {
+ printf("num_out_req_at_timeout[%d]=%d,", i, num_out_reqs_statistics_at_timeout[i]);
+ num_out_reqs_statistics_at_timeout[i]=0;
+ }
+ }
+ printf("\n");
+ // CYCLIC_PRINT(dep_tab_index);
+ }
+
+ if ((total_profile.in.tv_sec > 6000)) {
+ printf ("the process has run for more than 600 seconds, exit\n");
+ goto END;
+ }
+
+ if (busy_flag == IDLE) {
+#ifndef RECV_THREAD
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+#endif
+#ifndef IO_THREAD
+ if (disk_io_status!=TRACE_FILE_END) {
+ disk_io_status = read_trace();
+ };
+#endif
+ }
+
+ //start_profile (&adjust_play_window_profile);
+ adjust_play_window (busy_flag, &poll_timeout);
+ if (rfs_debug)
+ printf("num_out_reqs %d\n", num_out_reqs);
+ num_out_reqs_statistics[num_out_reqs]++;
+ busy_flag = IDLE;
+ //end_profile (&adjust_play_window_profile);
+
+ start_profile (&execute_next_request_profile);
+ while (execute_next_request()!=-1) {
+ busy_flag = BUSY;
+ }
+ end_profile (&execute_next_request_profile);
+
+#ifndef RECV_THREAD
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1)
+ busy_flag = BUSY;
+#endif
+ end_profile (&receive_next_reply_profile);
+#endif
+ CYCLIC_ASSERT (0);
+ }
+ end_profile (&total_profile);
+
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+ if (num_out_reqs !=0 ) {
+ printf ("num_out_reqs %d\n", num_out_reqs);
+ CYCLIC_PRINT(dep_tab_index);
+ }
+ RFS_ASSERT(num_out_reqs==0);
+
+END:
+ printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2,
+ trace_timestamp2 - trace_timestamp1);
+ printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d",
+ can_not_catch_speed_num_total, can_not_catch_speed_num);
+ printf ("total_profile.about: %s\n", total_profile.about);
+ print_profile ("total_profile", &total_profile);
+ printf("\n");
+ //print_profile ("check_timeout", &check_timeout_profile);
+ //printf("\n");
+ //print_profile ("adjust_play_window", &adjust_play_window_profile);
+ //printf("\n");
+ print_profile ("execute_next_request_profile", &execute_next_request_profile);
+ print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile);
+ print_profile ("prepare_argument_profile", &prepare_argument_profile);
+ print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ printf("\n");
+ print_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ print_profile ("decode_reply_profile", &decode_reply_profile);
+ print_profile ("check_reply_profile", &check_reply_profile);
+ print_profile ("fgets_profile", &fgets_profile);
+ print_profile ("read_line_profile", &read_line_profile);
+ print_profile ("read_trace_profile", &read_trace_profile);
+ //print_profile ("add_create_object", &add_create_object_profile);
+ printf("\n");
+
+ printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs);
+}
+
+
+int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest)
+{
+ cyclic_index_t indextmp, indextmp2;
+ int oldnum, num;
+ indextmp = *index;
+ indextmp2 = indextmp;
+ oldnum = CYCLIC_NUM(indextmp);
+
+ if (! ((dest>=0) && (dest<indextmp.size))) {
+ CYCLIC_PRINT(indextmp);
+ printf("dest %d\n", dest);
+ }
+ RFS_ASSERT ((dest>=0) && (dest<indextmp.size));
+ index->tail = dest;
+ indextmp2.tail = dest;
+ num = CYCLIC_NUM(indextmp2);
+
+ if (num > oldnum) {
+ CYCLIC_PRINT(indextmp);
+ CYCLIC_PRINT(indextmp2);
+ printf("dest %d old_num %d num %d\n", dest, oldnum, num);
+ }
+ RFS_ASSERT (num <= oldnum);
+}
+
+int flush_junk()
+{
+ int i;
+ for (i=0; i<500; i++) {
+ printf ("*************************************************************\n");
+ }
+ fflush(stdout);
+}
+
+int CYCLIC_ASSERT (int i)
+{
+ int j;
+ if (!(dep_tab_index.tail == dep_window_index.tail)) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+
+ if (!((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+ for (i=0, j=0; i<max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == 1)
+ j++;
+ }
+#ifndef RECV_THREAD
+ RFS_ASSERT (num_out_reqs==j);
+#endif
+/*
+ RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ));
+*/
+}
+
+/* sfs_c_chd.c */
--- /dev/null
+sfs_c_chd.3thread.c
\ No newline at end of file
--- /dev/null
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+#include "nfsd_nfsfh_cust.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout);
+extern char * parse_name (char * t, char * buf);
+
+/* forward definitions for local functions */
+static int init_rpc(void);
+
+/* RFS: forward definitions for local functions */
+void init_ops(void);
+static void init_signal();
+extern void init_file_system (void);
+extern void init_dep_tab (void);
+static void read_trace(char * trace_file);
+static void read_fh_map();
+static void init_play (char * mount_point);
+static void trace_play(void);
+static void print_result(void);
+static int get_nextop(void);
+static int check_timeout(void);
+static struct biod_req * get_biod_req(int dep_tab_index);
+static int lookup_biod_req (int xid);
+static void init_time_offset(void);
+static void adjust_play_window (int flag, int * poll_timeout);
+static int poll_and_get_reply (int usecs);
+static char * nfs3_strerror(int status);
+static void check_clock(void);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void usage(void);
+void init_dep_tab_entry (int dep_index);
+extern inline fh_map_t * lookup_fh (char * trace_fh );
+static inline void finish_request (int biod_index, int dep_index, int status);
+static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line);
+static inline char * find_lead_trace_fh(int proc, char * line);
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+
+static char io_buf[BUFSIZ]; /* io buffer for print out messages */
+
+char * sfs_Myname;
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+int Validate = 0; /* fake variable */
+int Child_num = 0; /* fake: child index */
+int Tcp = 0; /* We implement UDP first */
+int Client_num = 1; /* fake: number of client */
+uid_t Real_uid;
+gid_t Cur_gid;
+uid_t Cur_uid;
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+void print_usage()
+{
+ printf("sfs3 hostname:mount_dir trace_file fh_map_file\n");
+ exit;
+}
+
+void read_dep_tab()
+{
+#ifdef NO_DEPENDENCY_TABLE
+ int i;
+ char * line;
+ char * trace_fh;
+ fh_map_t * fh_map_entry;
+ int req_num_with_new_fh = 0;
+ int req_num_with_discard_fh = 0;
+ int req_num_with_init_fh =0;
+
+ for (i=0; i<memory_trace_size; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='C') {
+ trace_fh = strstr (line, "fh");
+ RFS_ASSERT (trace_fh);
+ trace_fh += 3;
+ fh_map_entry = lookup_fh (trace_fh);
+ if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) {
+ req_num_with_discard_fh ++;
+ continue;
+ }
+ if (fh_map_entry)
+ req_num_with_init_fh ++;
+ else
+ req_num_with_new_fh ++;
+
+ dep_tab[dep_tab_size].disk_index = memory_trace[i].disk_index;
+ dep_tab[dep_tab_size].line = memory_trace[i].line;
+ if ((dep_tab_size%100000)==0)
+ printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_size, dep_tab[dep_tab_size].disk_index, i, memory_trace[i].disk_index);
+ dep_tab_size ++;
+ }
+ }
+#else
+ RFS_ASSERT (0);
+#endif
+ printf ("read_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+}
+
+void init_profile()
+{
+ memset (&total_profile, 0, sizeof(total_profile));
+
+ memset (&execute_next_request_profile, 0, sizeof(execute_next_request_profile));
+ memset (&valid_get_nextop_profile, 0, sizeof(valid_get_nextop_profile));
+ memset (&invalid_get_nextop_profile, 0, sizeof(invalid_get_nextop_profile));
+ memset (&prepare_argument_profile, 0, sizeof(prepare_argument_profile));
+ memset (&biod_clnt_call_profile, 0, sizeof(biod_clnt_call_profile));
+
+ memset (&receive_next_reply_profile, 0, sizeof(receive_next_reply_profile));
+ memset (&valid_poll_and_get_reply_profile, 0, sizeof(valid_poll_and_get_reply_profile));
+ memset (&invalid_poll_and_get_reply_profile, 0, sizeof(invalid_poll_and_get_reply_profile));
+ memset (&decode_reply_profile, 0, sizeof(decode_reply_profile));
+ memset (&check_reply_profile, 0, sizeof(check_reply_profile));
+ memset (&add_create_object_profile, 0, sizeof(add_create_object_profile));
+
+ memset (&check_timeout_profile, 0, sizeof(check_timeout_profile));
+ memset (&adjust_play_window_profile, 0, sizeof(adjust_play_window_profile));
+}
+
+static char trace_file[256]="anon-lair62-011130-1200.txt";
+int print_memory_usage()
+{
+ printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map));
+ printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab));
+ printf("size of memory_trace_entry_t %d sizeof memory_trace %d\n", sizeof(memory_trace_entry_t), sizeof(memory_trace));
+ printf("size of CREATE3args %d\n", sizeof( CREATE3args));
+ printf("size of MKDIR3args %d\n", sizeof( MKDIR3args));
+ printf("size of READ3args %d\n", sizeof( READ3args));
+ printf("size of WRITE3args %d\n", sizeof( WRITE3args));
+ printf("size of RENAME3args %d\n", sizeof( RENAME3args));
+ printf("size of GETATTR3args %d\n", sizeof( GETATTR3args));
+ printf("size of SETATTR3args %d\n", sizeof( SETATTR3args));
+ printf("size of LINK3args %d\n", sizeof( LINK3args));
+ printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args));
+ printf("size of MKNOD3args %d\n", sizeof( MKNOD3args));
+ printf("size of RMDIR3args %d\n", sizeof( RMDIR3args));
+ printf("size of REMOVE3args %d\n", sizeof( REMOVE3args));
+ printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args));
+ printf("size of READDIR3args %d\n", sizeof( READDIR3args));
+ printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args));
+ printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args));
+ printf("size of FSINFO3args %d\n", sizeof( FSINFO3args));
+ printf("size of COMMIT3args %d\n", sizeof( COMMIT3args));
+ printf("size of ACCESS3args %d\n", sizeof( ACCESS3args));
+ printf("size of READLINK3args %d\n", sizeof( READLINK3args));
+
+
+}
+int main(int argc, char ** argv)
+{
+ extern char * optarg;
+ int i;
+ int memory_trace_size;
+
+ if (argc==2 && !strcmp(argv[1],"-help")) {
+ print_usage();
+ exit(0);
+ }
+ print_memory_usage();
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+ /*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+ //Real_uid = 513;
+ //Cur_gid = 513;
+
+ Nfs_timers = Nfs_udp_timers;
+
+ init_file_system ();
+
+ init_signal();
+ init_play (argv[1]);
+ //init_play ("capella:/p5/RFSFS");
+ init_profile();
+ init_fh_map();
+ //read_fh_map (argv[3]);
+ read_fh_map ("fh-path-map-play");
+ init_dep_tab(); /* and dep_tab_size */
+ //read_trace (argv[2]);
+ //read_trace ("anon-lair62-011130-1000.txt");
+ strcpy(trace_file, argv[2]);
+ read_trace (trace_file);
+ stage = READ_DEP_TAB_STAGE;
+ read_dep_tab();
+
+ for (i=0; i<dep_tab_size; i++) {
+ RFS_ASSERT (dep_tab[i].flag == DEP_FLAG_FREE)
+ init_dep_tab_entry (i);
+ }
+ stage = TRACE_PLAY_STAGE;
+ init_time_offset();
+ printf ("trace_play\n");
+ trace_play ();
+ print_result();
+ printf("ffff\n");
+}
+
+void init_ops (void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/* Set up the signal handlers for all signals */
+void init_signal()
+{
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+#else
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ // RFS (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+#endif
+}
+
+void
+init_play (
+ char * mount_point) /* Mount point for remote FS */
+{
+ char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */
+ CLIENT * mount_client_ptr; /* Mount client handle */
+
+ if (!rfs_debug);
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ sfs_Myname = namebuf;
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, mount_point, mount_client_ptr);
+
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ init_counters();
+
+
+}
+
+void read_trace (char * tracefile)
+{
+ FILE * fp;
+ char buf[1024];
+ // char * t=buf;
+ int disk_index=0;
+
+ fp = fopen(tracefile, "r");
+ RFS_ASSERT (fp!=NULL);
+ while (fgets(buf, 1024, fp)) {
+ //printf ("buf: %s buf[36] %c\n", buf, buf[36]);
+ //if (buf[36]=='C' || strstr(buf, "create") || strstr(buf, "lookup")) {
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='C' || strstr(buf, "create") ||
+ strstr(buf, "mkdir") || strstr(buf, "symlink") || strstr(buf, "mknod") || strstr(buf, "lookup")) {
+#endif
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("strlen(buf) %d buf %s \n", strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+
+ /* store the request to memory */
+ strcpy (memory_trace[memory_trace_size].line, buf);
+ memory_trace[memory_trace_size].disk_index = disk_index;
+ memory_trace_size ++;
+
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ if ((disk_index%100000)==0)
+ fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size);
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ } else {
+ RFS_ASSERT (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ }
+#endif
+ disk_index ++;
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size );
+
+}
+
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline int disk_index_to_memory_index (int disk_index)
+{
+ static int memory_index = 0;
+ if (disk_index > memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index < disk_index) {
+ memory_index++;
+ RFS_ASSERT (memory_index < MAX_MEMORY_TRACE_LINES);
+ }
+ };
+ if (disk_index < memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index > disk_index) {
+ memory_index--;
+ RFS_ASSERT (memory_index >=0);
+ }
+ };
+
+ RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index);
+ return memory_index;
+}
+#else
+#define disk_index_to_memory_index(disk_index) disk_index
+#endif
+
+#define get_line_by_disk_index(disk_index) \
+ memory_trace[disk_index_to_memory_index(disk_index)].line
+
+inline char * find_reply_line (char * command_line, int cur_disk_index)
+{
+ int i;
+ char * line;
+ char * p;
+ int request_memory_index = disk_index_to_memory_index (cur_disk_index);
+ for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&line[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS])))
+ return line;
+ }
+ }
+ return NULL;
+}
+
+inline int find_reply_status (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */
+ if (strstr(p, "OK"))
+ return NFS3_OK;
+ if (strstr(p, "lookup 2"))
+ return 0x2;
+ if (strstr(p, "create d"))
+ return 0xd;
+ if (strstr(p, "setattr 1"))
+ return 0x1;
+ if (strstr(p, "lookup d"))
+ return 0xd;
+ if (strstr(p, "read d"))
+ return 0xd;
+ if (strstr(p, "write d"))
+ return 0xd;
+ if (strstr(p, "mkdir d"))
+ return 0xd;
+ printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (0);
+}
+
+inline int find_reply_status_old (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ if (!strstr(line, "OK")) {
+ p=strstr(line, " 6 read ");
+ if (p) {
+ p+=strlen(" 6 read ");
+ } else {
+ p = strstr (line, "status=XXX");
+ RFS_ASSERT (p);
+ p--;
+ RFS_ASSERT (*p==' ');
+ while (*p==' ')
+ p--;
+ while (*p!=' ') {
+ p--;
+ }
+ p++;
+ }
+ sscanf (p, "%x", &i);
+ if ((i<=0) || (i>10000))
+ printf("line %s\n", line);
+ RFS_ASSERT (i>0 && i<10009);
+ }
+ return i;
+}
+
+inline char * find_reply_trace_fh (char * line)
+{
+ char * p;
+ p = strstr (line, "OK fh");
+ if (!p)
+ printf ("find_reply_trace_fh line %s\n", line);
+ RFS_ASSERT (p);
+ return p+6;
+}
+
+inline int disk_index_to_dep_index(int cur_dep_index, int disk_index)
+{
+ int i;
+ for (i=cur_dep_index; i>min_dep_index; i--) {
+ if (dep_tab[i].disk_index == disk_index)
+ return i;
+ }
+ RFS_ASSERT (0);
+}
+
+inline int is_play_candidate (int dep_index)
+{
+ int proc = dep_tab[dep_index].proc;
+ int status = dep_tab[dep_index].status;
+ int trace_status = dep_tab[dep_index].trace_status;
+
+#ifndef TAKE_CARE_CREATE_MODE_BY_DAN
+ /* for a failed create in trace, trace_replay just ignore many time the trace create fail
+ * due to access control, but trace_play will success because our access control
+ * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests
+ */
+ if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) {
+ if (dependency_debug)
+ printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+ failed_create_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_OTHER_FAILED_COMMAND
+ if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) ||
+ ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){
+ if (dependency_debug)
+ printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+
+ failed_other_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */
+ skipped_readlink_command_num ++;
+ return FALSE;
+ }
+#endif
+#define TAKE_CARE_CUSTOM_COMMAND
+/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */
+#ifndef TAKE_CARE_CUSTOM_COMMAND
+ /* this line has a file handle which should belong to discard but it is not
+ * the file handle directly appears as parent directory in a lookup request
+ * the return value is NOENT, the parent directory should have been initialized
+ * but the initialization code just ignored all lookup request which didn't success
+ * including NOENT even though the parent directory is still valid.
+ */
+ if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402))
+ && !(strcmp(trace_file, "anon-lair62-011130-1100.txt"))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_FSSTAT_COMMAND
+ /* the file handle used in this command is not processed properly by pre-processing */
+ if (proc==FSSTAT) {
+ char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ fh_map_t * fh = lookup_fh (trace_fh);
+ if (!fh) {
+ skipped_fsstat_command_num++;
+ return FALSE;
+ }
+ }
+#endif
+ return TRUE;
+}
+
+inline int is_dir_op (int proc)
+{
+ switch (proc) {
+ case MKDIR:
+ case CREATE:
+ case LINK:
+ case SYMLINK:
+ case MKNOD:
+ case REMOVE:
+ case RMDIR:
+ case RENAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+inline int is_create_op (int proc)
+{
+ if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline int is_delete_op (int proc)
+{
+ if (proc==REMOVE || proc==RMDIR || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+static inline char * find_lead_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh");
+ RFS_ASSERT (p);
+ p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+inline char * find_another_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh2");
+ RFS_ASSERT (p);
+ p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+/* return the index of next request in dep_tab.
+ * Return -1 if there is no suitable request to send
+ */
+inline int get_nextop(void)
+{
+ int i,j, k;
+ int * t;
+ static int dep_index = -2;
+ char * line;
+ char * p;
+ static int min_wait_fhandle_dep_index = DEP_TAB_SIZE;
+ int proc;
+ int flag;
+
+ //if (dep_index < min_dep_index-1)
+ // dep_index = min_dep_index-1;
+
+ dep_index = min_dep_index-1;
+ for (i=0; i<max_dep_index-min_dep_index; i++) {
+ dep_index ++;
+ if (dep_index == max_dep_index) {
+ dep_index = min_dep_index;
+ }
+
+ proc = dep_tab[dep_index].proc;
+ flag = dep_tab[dep_index].flag;
+
+ if (dependency_debug)
+ printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+#ifdef NO_DEPENDENCY_TABLE
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT) {
+ if (is_play_candidate(dep_index)==TRUE) {
+ /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle
+ * used in the request */
+ if (proc==LINK || proc==RENAME) {
+ dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = 0;
+ } else {
+ dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = (fh_map_t *)1;
+ };
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+#ifdef TIME_PLAY
+ dep_tab[dep_index].skip_sec = skip_sec;
+#endif
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index);
+ } else {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) {
+
+ if (!dep_tab[dep_index].fh)
+ dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh);
+ if (!dep_tab[dep_index].fh_2)
+ dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2);
+
+ /* need to wait for file handle */
+ if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) {
+ if (dependency_debug)
+ printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE;
+ sfs_gettime (&dep_tab[dep_index].start);
+ if (dep_index < min_wait_fhandle_dep_index)
+ min_wait_fhandle_dep_index = dep_index;
+ } else {
+ struct ladtime tmp;
+ if (dep_index==min_dep_index) {
+ if (!profile_debug)
+ printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ fh_path_map_err_num ++;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ sfs_gettime (&tmp);
+ SUBTIME (tmp, dep_tab[dep_index].start);
+#define DEPENDENCY_TIMEOUT 5
+#ifdef TIME_PLAY
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec));
+#else
+ if (tmp.sec >= DEPENDENCY_TIMEOUT) {
+ printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index,
+ dep_tab[dep_index].flag, dep_tab[dep_index].disk_index,
+ dep_tab[dep_index].line);
+ }
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT );
+#endif
+ }
+ continue;
+ }
+
+ /* file handle ready, adjust_min_wait_fhandle_dep_index */
+ if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) {
+ if (dep_index == min_wait_fhandle_dep_index) {
+ min_wait_fhandle_dep_index = dep_tab_size;
+ for (j=dep_index+1; j<max_dep_index; j++) {
+ if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) {
+ min_wait_fhandle_dep_index = j;
+ break;
+ }
+ }
+ }
+ }
+ if (dependency_debug)
+ printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY;
+
+ /* the normal file operation can be executed now */
+ if (!is_dir_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ if (dependency_debug)
+ printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index);
+ /* the directory operation need to lock the directory first */
+ if (dep_tab[dep_index].fh->lock) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) {
+ int j = dep_tab[dep_index].fh - fh_map;
+ if (dependency_debug) {
+ printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock);
+ printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) {
+ if (dependency_debug)
+ printf ("continue to wait for directory lock\n");
+ continue;
+ }
+ if (dependency_debug)
+ printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j);
+ dep_tab[dep_index].fh->lock = 1;
+ if (proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 1;
+
+ /* the non-delete directory operation can proceed now */
+ if (!is_delete_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ /* the delete operation can proceed if nobody ahead is waiting for fhandle */
+ /* probably this condition is not strong enough */
+// if ((min_wait_fhandle_dep_index<dep_index) ) {
+ if (dep_index!=min_dep_index) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE;
+ continue;
+ }
+ dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY;
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) {
+// if (min_wait_fhandle_dep_index > dep_index) {
+ if (dep_index==min_dep_index) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ }
+#else
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT){
+ for (j=0, t=&(dep_tab[dep_index].dep_ops[0]);
+ (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0);
+ j++, t++) {
+ if (*t !=-1) {
+ if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) {
+ /* The depended request has been finished */
+ *t = -1;
+ dep_tab[dep_index].cur_dep_num --;
+ }
+ }
+ }
+
+ if (dep_tab[dep_index].cur_dep_num == 0) {
+ return dep_index;
+ }
+ }
+#endif
+ }
+
+ if (dependency_debug)
+ printf ("get_nexop return -1\n");
+ return -1;
+}
+
+int check_timeout(void)
+{
+ static int biod_index = 0;
+ int i;
+ int dep_index; /* index into dep_tab */
+ int proc;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime timeout;
+
+ sfs_gettime (¤t);
+
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (biod_reqp[biod_index].in_use==TRUE) {
+ timeout = biod_reqp[biod_index].timeout;
+ if ((current.sec>timeout.sec) ||
+ ((current.sec==timeout.sec) && (current.usec>timeout.usec))) {
+
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+ op_ptr->results.timeout_calls++;
+ Ops[TOTAL].results.timeout_calls++;
+
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT);
+
+ if (is_create_op(proc)) {
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+ printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ }
+ //RFS_ASSERT (!is_create_op(proc));
+
+ //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec);
+ }
+ }
+ }
+}
+
+/* Allocate a biod_req entry to send and receive request dep_tab[dep_index]
+ * build the cross reference between dep_tab entry and biod_req entry
+ */
+struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (!biod_reqp[biod_index].in_use) {
+ biod_reqp[biod_index].in_use = 1;
+ biod_reqp[biod_index].dep_tab_index = dep_index;
+ dep_tab[dep_index].biod_req_index = biod_index;
+ return &(biod_reqp[biod_index]);
+ }
+ }
+ return NULL;
+}
+
+/* Return index into biod_reqp
+ * return -1 upon failure
+ */
+int lookup_biod_req (int xid)
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ /* give a NULL as timeout pointer may cause indefinitely block */
+ if (biod_reqp[biod_index].xid == xid) {
+ return biod_index;
+ }
+ }
+ return -1;
+}
+
+extern struct ladtime test_start;
+void init_time_offset(void)
+{
+ struct ladtime tmp1;
+ struct ladtime tmp2;
+
+ test_start.sec = 0;
+ test_start.usec = 0;
+ sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */
+#ifdef SPEED_UP
+ DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */
+#endif
+
+ tmp2 = trace_starttime; /* tmp2 = trace_starttime */
+ SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */
+ time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void init_dep_tab_entry (int dep_index)
+{
+ static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+ static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14,
+ 13, 21, 10, 15, 11, 12, 16, 23, 17, 20,
+ 22, 19};
+ char * line;
+ int version;
+ int nfsproc;
+ int msgid;
+
+ //line = get_line_by_disk_index (dep_tab[dep_index].disk_index);
+ line = dep_tab[dep_index].line;
+ sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec));
+ sscanf (&line[39], "%x %x", &msgid, &nfsproc);
+ if (line[TRACE_VERSION_POS]=='2') {
+ dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc];
+ RFS_ASSERT (nfsproc <18);
+ } else {
+ /* This is for debug purpose */
+ if (line[TRACE_VERSION_POS] !='3') {
+ fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line);
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1);
+ if (!line)
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2);
+ RFS_ASSERT (line);
+ fprintf(stderr, "previousline %s\n", line);
+ }
+ RFS_ASSERT (line[TRACE_VERSION_POS] =='3');
+ if (nfsproc >= NFS3_PROCEDURE_COUNT) {
+ fprintf(stderr, "proc %d line %s\n", nfsproc, line);
+
+ }
+ RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT);
+ dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc];
+ }
+ RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS);
+ dep_tab[dep_index].flag = DEP_FLAG_INIT;
+ dep_tab[dep_index].reply_line = find_reply_line (line, dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].reply_line == NULL) {
+ //printf ("disk[%d] can not find the reply line, assume trace_status OK\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].trace_status = NFS3ERR_RFS_MISS;
+ missing_reply_num ++;
+ } else
+ dep_tab[dep_index].trace_status = find_reply_status (dep_tab[dep_index].reply_line);
+}
+
+static void adjust_play_window (int flag, int * poll_timeout)
+{
+ struct ladtime max_window_time;
+ static struct ladtime max_poll_time = {0, 2000, 0};
+ struct ladtime t;
+ int old_min = min_dep_index;
+ int old_max = max_dep_index;
+ int i;
+ char * line;
+
+ for (; (dep_tab[min_dep_index].flag == DEP_FLAG_DONE) && (min_dep_index<dep_tab_size); min_dep_index++)
+ ;
+ RFS_ASSERT (min_dep_index <= max_dep_index);
+
+ /* max_trace_window_time = current *|/ SCALE + trace_starttime */
+ sfs_gettime (¤t);
+
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+ MULTIME (current, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ DIVTIME (current, PLAY_SCALE);
+#endif
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+
+ while ((max_dep_index<min_dep_index+MAX_PLAY_WINDOW) && (max_dep_index<dep_tab_size))
+ {
+ t.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ t.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+
+ if (adjust_play_window_debug)
+ printf ("max_window_time sec %d usec %d : max_dep_index %d, t.sec %d t.usec %d\n", max_window_time.sec, max_window_time.usec, max_dep_index, t.sec, t.usec );
+
+ if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec))
+ break;
+
+ max_dep_index++;
+ }
+
+ /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */
+ if (max_dep_index == min_dep_index+MAX_PLAY_WINDOW) {
+ //printf ("can not catch up the speed, max_dep_index %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", max_dep_index, min_dep_index);
+ //printf (".");
+ can_not_catch_speed_num ++;
+ }
+ //RFS_ASSERT (max_dep_index < min_dep_index+MAX_PLAY_WINDOW);
+#else
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+ max_dep_index = min_dep_index + MAX_PLAY_WINDOW;
+ if (max_dep_index >dep_tab_size)
+ max_dep_index = dep_tab_size;
+#endif
+
+ if (flag == BUSY)
+ *poll_timeout = 0;
+ else if (max_dep_index == dep_tab_size) {
+ *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */
+ } else {
+#ifdef TIME_PLAY
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+ if (adjust_play_window_debug)
+ printf ("dep_tab[max_dep_index %d].timestamp %d:%d, max_window_time %d:%d\n",
+ max_dep_index, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec);
+
+ SUBTIME (tmp, max_window_time);
+#ifdef SPEED_UP
+ DIVTIME (tmp, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp, PLAY_SCALE);
+#endif
+ tmp1 = tmp;
+
+ if (tmp.sec > max_poll_time.sec) {
+
+ if (rfs_debug)
+ printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n",
+ max_dep_index, dep_tab[max_dep_index].timestamp.tv_sec, dep_tab[max_dep_index].timestamp.tv_usec, max_window_time.sec, max_window_time.usec);
+ printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec);
+ SUBTIME (tmp, max_poll_time);
+ tmp.usec = 0;
+ skip_sec += tmp.sec;
+ SUBTIME (test_start, tmp);
+ tmp = max_poll_time;
+ }
+
+ RFS_ASSERT ((tmp.sec < 1000));
+ if ((tmp.sec ==0) && (tmp.usec==0)) {
+ *poll_timeout = 0;
+ } else
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+#else
+ /*
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+ tmp1.sec = dep_tab[max_dep_index-1].timestamp.tv_sec;
+ tmp1.usec = dep_tab[max_dep_index-1].timestamp.tv_usec;
+ SUBTIME (tmp, tmp1);
+ RFS_ASSERT ((tmp.sec < 1000));
+ RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0)));
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+ */
+
+ *poll_timeout = 100000;
+#endif
+ }
+ if (rfs_debug)
+ printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n", flag, old_min, min_dep_index, old_max, max_dep_index, *poll_timeout);
+}
+
+/* poll for usecs and receive, after receive one reply,
+ * return index in biod_reqp of the corresponding request
+ */
+int poll_and_get_reply (int usecs)
+{
+ int biod_index = -1;
+ int xid;
+ int error;
+ struct timeval zero_time = {0, 0}; /* Immediately return */
+
+ do {
+ error = biod_poll_wait (NFS_client, usecs);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (rfs_debug) {
+ (void) fprintf(stderr, "biod_poll_wait error\n");
+ perror ("");
+ (void) fflush(stderr);
+ }
+ break;
+ case 0:
+ break;
+ default:
+#ifdef UDP
+ error = get_areply_udp (NFS_client, &xid, &zero_time);
+ // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */
+ // RFS_ASSERT (error!= RPC_CANTRECV);
+ RFS_ASSERT (error == RPC_SUCCESS);
+
+ biod_index = lookup_biod_req (xid);
+ sfs_gettime (&(biod_reqp[biod_index].stop));
+#else
+ RFS_ASSERT (0);
+#endif
+ }
+ } while (0);
+ return biod_index;
+}
+
+void print_result(void)
+{
+ int i, j;
+ struct ladtime t;
+ int dep_index;
+ int avg_msecs;
+ unsigned long long tmp;
+ int avg_usecs;
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stdout, "trace play result:\n");
+ (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n");
+ for (i=0; i<NOPS+1; i++) {
+ if (Ops[i].results.good_calls==0) {
+ avg_msecs = 0;
+ avg_usecs = 0;
+ } else {
+ tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec;
+ avg_msecs = 0;
+ avg_usecs = tmp/Ops[i].results.good_calls;
+/*
+ avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls;
+ avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls;
+*/
+ }
+ (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n",
+ Ops[i].name,
+ (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls,
+ Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls,
+ Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs);
+ }
+ (void) fflush (stdout);
+ }
+
+ RFS_ASSERT (read_data_owe_GB==0);
+ printf("read_data_bytes %d owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe_GB*1073741824+read_data_owe)/(read_data_total/100), read_data_adjust_times);
+ printf("write_data_bytes %d owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe_GB*1073741824+write_data_owe)/(write_data_total/100), write_data_adjust_times);
+ printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num);
+ printf("failed_create_command_num %d\nfailed_other_command_num %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nproper_reply_num %d\n",
+ failed_create_command_num,
+ failed_other_command_num,
+ skipped_readlink_command_num,
+ skipped_custom_command_num,
+ fh_path_map_err_num,
+ skipped_fsstat_command_num,
+ missing_reply_num,
+ rename_rmdir_noent_reply_num,
+ rmdir_not_empty_reply_num,
+ loose_access_control_reply_num,
+ lookup_err_due_to_rename_num,
+ lookup_eaccess_enoent_mismatch_num,
+ read_io_err_num,
+ proper_reply_num);
+
+ clnt_destroy(NFS_client);
+ biod_term();
+
+// print_dump(Client_num, Child_num);
+ return;
+ dep_index = 0;
+ printf ("[%4d] %s \tstart %4d:%6d \n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec);
+ for (i=1; i<dep_tab_size*2; i++) {
+ dep_index = event_order[i];
+ if (dep_index >0)
+ printf ("[%4d] %s \tstart %4d:%6d \n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec);
+ else {
+ dep_index = -dep_index;
+ t=dep_tab[dep_index].stop;
+ SUBTIME (t, dep_tab[dep_index].start);
+ printf ("\t\t\t\t\t[%4d] %s stop %4d:%6d\tinterval %4d:%6d %s\n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[dep_index].status));
+ }
+ }
+/*
+
+ for (i=0, j=0; i<dep_tab_size || j<dep_tab_size; ) {
+ if ((i==dep_tab_size) ||
+ (LARGERTIME(dep_tab[i].start, dep_tab[j].stop) && j<dep_tab_size)) {
+ t=dep_tab[j].stop;
+ SUBTIME (t, dep_tab[j].start);
+ printf ("dep_tab[%d].proc %s stop %d:%d interval %d:%d status %s\n",
+ j, Ops[dep_tab[j].proc].name, dep_tab[j].stop.sec, dep_tab[j].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[j].status));
+ j++;
+ } else {
+ printf ("dep_tab[%d].proc %s start %d:%d \n",
+ i, Ops[dep_tab[i].proc].name, dep_tab[i].start.sec, dep_tab[i].start.usec);
+ i++;
+ }
+ }
+*/
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ int dummy = 0;
+
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+
+ if (biod_init(dummy, dummy) == -1) {
+ return(-1);
+ }
+
+ return(0);
+} /* init_rpc */
+
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.timeout_calls = 0; // RFS
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "Timeout");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "trace play usage");
+}
+extern void init_file_system (void)
+{
+ return;
+}
+
+extern void init_dep_tab (void)
+{
+ int i;
+ memset (&dep_tab, 0, sizeof(dep_tab));
+#ifdef notdef
+ dep_tab[0].disk_index = 0;
+ dep_tab[1].disk_index = 2;
+ dep_tab[2].disk_index = 3;
+ dep_tab[3].disk_index = 5;
+ dep_tab[4].disk_index = 7;
+ dep_tab[5].disk_index = 9;
+ dep_tab[6].disk_index = 11;
+ dep_tab[7].disk_index = 12;
+ dep_tab[8].disk_index = 15;
+ dep_tab[9].disk_index = 17;
+ dep_tab[10].disk_index = 18;
+ dep_tab[11].disk_index = 20;
+ dep_tab_size = 12;
+ //dep_tab_size = 2;
+#endif
+}
+
+extern void init_dep_tab_old (void)
+{
+ int i;
+
+ Cur_file_ptr = &Export_dir;
+ Cur_uid = Real_uid;
+
+ for (i=0; i<5; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec+i*10;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+
+ for (i=5; i<10; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+
+ dep_tab[2].init_dep_num = 2;
+ dep_tab[2].cur_dep_num = 2;
+ dep_tab[2].dep_ops[0] = 0;
+ dep_tab[2].dep_ops[1] = 1;
+
+ // printf ("trace_starttime (%d %d)\n", trace_starttime.sec, trace_starttime.usec);
+
+ /*
+ for (i=2; i<4; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i*10;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+ */
+
+ dep_tab_size = 10;
+
+ for (i=0; i<dep_tab_size; i++)
+ {
+ printf("dep_tab[%d].timestamp (%d %d)\n", i, dep_tab[i].timestamp.tv_sec, dep_tab[i].timestamp.tv_usec);
+ }
+}
+
+void show_fhandle (nfs_fh3 * fhp)
+{
+ struct knfs_fh * kfhp = (struct knfs_fh *)fhp;
+
+ int dev;
+
+ if (quiet_flag)
+ return;
+
+ RFS_ASSERT (kfhp->fh_version == 1);
+ RFS_ASSERT (kfhp->fh_fsid_type == 0);
+ RFS_ASSERT (kfhp->fh_auth_type == 0);
+
+ dev = ntohs(kfhp->fh_dev_major);
+ dev = dev<<8;
+ dev = dev + ntohs(kfhp->fh_dev_minor);
+
+ /* kfhp->fh_dev_ino hold the inode number of export point of the mounted
+ * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted,
+ * then fh_dev_ino hold the inode number of t1, not t2
+ */
+
+ switch (kfhp->fh_fileid_type) {
+ case 0:
+ printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino);
+ break;
+ case 1:
+ printf("fh:type 1 %d %x dev %x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino);
+ break;
+ case 2:
+ printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino);
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+}
+
+nfs_fh3 zero_fhandle;
+int init_fh_map ()
+{
+ memset (fh_map, 0, sizeof (fh_map));
+ memset(fh_htable, 0, sizeof (fh_htable));
+ memset (&zero_fhandle, 0, sizeof(nfs_fh3));
+ printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000);
+ fh_i = 0;
+}
+
+int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh)
+{
+ char * old_trace_fh;
+
+ /* first lookup if the entry for fh is already in the table */
+ struct generic_entry * p;
+
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (p) {
+ RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL);
+ RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE);
+ fh_map[p->key3].flag = map_flag;
+ //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) {
+ int i;
+ printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh);
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (!strcmp(fh_map[p->key3].path, path));
+ /* It's possible that in fh-path-map, many trace_fh are corresponding to one path
+ * some of it may be the result of lookup after symlink, which is not handled
+ * properly as new created objects
+ */
+#ifdef TAKE_CARE_SYMBOLIC_LINK
+ RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3)));
+#endif
+ memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3));
+ if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE))
+ printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path);
+ return 0;
+ }
+
+ fh_map[fh_i].flag = map_flag;
+ fh_map[fh_i].lock = 0;
+ strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE);
+
+ RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE);
+ strcpy (fh_map [fh_i].path, path);
+ if (map_flag==FH_MAP_FLAG_COMPLETE)
+ memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3));
+ else
+ memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3));
+
+ if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) {
+ printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path);
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ //show_fhandle(play_fh);
+ } else
+ printf("null\n");
+ }
+
+/*
+ if (map_flag == FH_MAP_FLAG_DISCARD)
+ printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path);
+*/
+
+ generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE);
+
+ fh_i = (fh_i+1);
+ RFS_ASSERT (fh_i < FH_MAP_SIZE);
+
+ return 0;
+};
+
+inline fh_map_t * lookup_fh (char * trace_fh )
+{
+ struct generic_entry * p;
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (fh_map_debug==1)
+ printf ("lookup trace_fh %s\n", trace_fh);
+
+ if (p) {
+ if (fh_map_debug==1) {
+ printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path);
+ //show_fhandle(&fh_map[p->key3].play_fh);
+ }
+ RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ return (&(fh_map[p->key3]));
+ } else {
+ //printf ("lookup_fh %s not found\n", trace_fh);
+ if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) {
+ printf ("lookup not found trace_fh %s\n", trace_fh);
+ }
+ return NULL;
+ }
+ RFS_ASSERT (0);
+}
+
+int delete_fh (char * trace_fh, int fh_map_index)
+{
+ generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE);
+ return 0;
+};
+
+void lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ static int i=0;
+
+ /* set up the arguments */
+ (void) memcpy((char *) &args.what.dir, (char *) parent,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+ sfs_gettime(&stop);
+
+ if (rpc_stat !=RPC_SUCCESS) {
+ printf("rpc_stat %d\n", rpc_stat);
+ perror("");
+ }
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ RFS_ASSERT (reply.status == NFS3_OK);
+ (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3));
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+#define MAX_PATH_DEPTH 20
+ nfs_fh3 parents[MAX_PATH_DEPTH];
+ char * lookup_path_ptr[MAX_PATH_DEPTH];
+ char lookup_path [MAX_PLAY_PATH_SIZE];
+ int depth;
+ int new_dir_flag = 0;
+
+ depth = 0;
+ memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr));
+ memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ strcpy(lookup_path, "/");
+ lookup_path_ptr[depth]=&lookup_path[0];
+
+ fp = fopen(fh_map_file, "r");
+ RFS_ASSERT (fp!=NULL);
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+
+ if (fh_i % 10000==0)
+ printf("%d fh_map entry read\n", fh_i);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+ if (fh_map_debug) {
+ printf("%d fgets return %s\n", fh_i, buf);
+ printf("depth %d lookup_path %s\n", depth, lookup_path);
+ }
+ //for (i=0; i<=depth; i++)
+ //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]);
+ //printf("\n");
+#ifdef COMPRESS_TRACE_FH
+ for (i=0; i<TRACE_FH_SIZE/8; i++) {
+ strncpy(intbuf, buf+i*8, 8);
+ sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now
+ }
+ trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */
+#else
+ memcpy(trace_fh, buf, TRACE_FH_SIZE);
+ trace_path = buf + TRACE_FH_SIZE +1;
+#endif
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */
+#endif
+
+#ifdef NO_DEPENDENCY_TABLE
+ if (!strncmp (trace_path, "DISCARD", 7)) {
+ map_flag = FH_MAP_FLAG_DISCARD;
+ add_fh (map_flag, buf, trace_path, 0);
+ continue;
+ }
+#endif
+
+ p = trace_path+strlen(trace_path)-2;
+ while (*p!='/')
+ p--;
+ p++;
+ //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1);
+ //RFS_ASSERT (p>trace_path);
+
+ if (strncmp(lookup_path, trace_path, p-trace_path)) {
+ printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path);
+ }
+ RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path));
+ //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */
+ while (strlen(lookup_path)>p-trace_path && depth>0) {
+ //printf("depth--\n");
+ if (depth<=0)
+ printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth);
+ RFS_ASSERT (depth>0);
+ *lookup_path_ptr[depth]=0;
+ lookup_path_ptr[depth]=0;
+ depth--;
+ }
+ RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0));
+
+
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ if (buf[TRACE_FH_SIZE*2+1]=='Y') {
+ map_flag = FH_MAP_FLAG_COMPLETE;
+ } else {
+ map_flag = FH_MAP_FLAG_PARTIAL;
+ RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N');
+ }
+#else
+ map_flag = FH_MAP_FLAG_COMPLETE;
+#endif
+ if ((*(p+strlen(p)-1))=='/') {
+ *(p+strlen(p)-1)=0;
+ new_dir_flag = 1;
+ } else
+ new_dir_flag = 0;
+
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ lookup_init_filesystem (&parents[depth], p, &parents[depth+1]);
+ add_fh (map_flag, buf, trace_path, &parents[depth+1]);
+ } else
+ add_fh (map_flag, buf, trace_path, 0);
+
+ if (new_dir_flag) {
+ /* the new fhandle is of a directory */
+ lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path);
+ strcat (lookup_path, p);
+ strcat (lookup_path, "/");
+
+ //printf("depth++\n");
+ depth++;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (fh_map_debug) {
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+
+ fprintf(stderr, "total %d requests \n", i);
+ }
+}
+
+int f()
+{
+ return 1;
+}
+
+inline void finish_request (int biod_index, int dep_index, int status)
+{
+ /* the ending operation, same as when a request time out */
+ dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */
+ dep_tab[dep_index].status = status;
+ event_order[event_order_index++] = -dep_index;
+ biod_reqp[biod_index].in_use = FALSE;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ if (is_dir_op(dep_tab[dep_index].proc)) {
+ int j;
+ RFS_ASSERT (dep_tab[dep_index].fh->lock = 1);
+ dep_tab[dep_index].fh->lock = 0;
+ if (dep_tab[dep_index].proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 0;
+ j = dep_tab[dep_index].fh-fh_map;
+ if (dependency_debug) {
+ printf ("fh_map[%d] is UNlocked\n",j);
+ printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ }
+ num_out_reqs --;
+}
+
+/* the request argument may have pointers pointing to buffers, e.g. the name in lookup,
+ * the target of symlink, the write data */
+char arg_res[MAX_ARG_RES_SIZE];
+int poll_timeout = 0; /* timeout in usecs */
+char buf1 [MAX_BUF1_SIZE];
+char buf2 [MAX_BUF2_SIZE];
+
+int execute_next_request ()
+{
+ int dep_index;
+ int proc;
+ char * line;
+ struct biod_req * reqp;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime call_timeout;
+
+ start_profile (&valid_get_nextop_profile);
+ start_profile (&invalid_get_nextop_profile);
+ dep_index = get_nextop();
+ if (dep_index == -1) {
+ end_profile (&invalid_get_nextop_profile);
+ return dep_index;
+ };
+ end_profile (&valid_get_nextop_profile);
+
+ start_profile (&prepare_argument_profile);
+ line = dep_tab[dep_index].line;
+ if ((dep_index%(10000))==0) {
+#ifndef TIME_PLAY
+ fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs);
+#else
+ fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+#ifdef SPEED_UP
+ if (can_not_catch_speed_num < 2000) {
+ PLAY_SCALE ++;
+ printf ("set PLAY_SCALE to %d\n", PLAY_SCALE);
+ };
+ if (can_not_catch_speed_num > 50000) {
+ PLAY_SCALE /= 2;
+ } else {
+ if (can_not_catch_speed_num > 5000) {
+ PLAY_SCALE -= 2;
+ if (PLAY_SCALE < 1)
+ PLAY_SCALE = 1;
+ }
+ }
+#endif
+ can_not_catch_speed_num = 0;
+#endif
+ }
+ if (rfs_debug)
+ printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line);
+
+ proc = dep_tab[dep_index].proc;
+ rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2);
+
+ op_ptr = &Ops[proc];
+ reqp = get_biod_req (dep_index);
+ RFS_ASSERT (reqp);
+
+ call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+
+ /* make the call */
+ sfs_gettime(&(reqp->start));
+ end_profile (&prepare_argument_profile);
+ start_profile (&biod_clnt_call_profile);
+#define REAL_PLAY
+#ifdef REAL_PLAY
+ reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc,
+ rfs_Ops[proc].xdr_arg, arg_res);
+#else
+
+ reqp->xid = dep_index+1; /* just fake a message id and let it expire */
+#endif
+ if (reqp->xid != 0) {
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ num_out_reqs++;
+ dep_tab[dep_index].flag = DEP_FLAG_SENT;
+ event_order[event_order_index++] = dep_index;
+ } else
+ RFS_ASSERT (0);
+
+ dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */
+ end_profile (&biod_clnt_call_profile);
+}
+
+inline void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status)
+{
+ if (((status!=trace_status)) && (trace_status!=NFS3ERR_RFS_MISS)) {
+ if (!profile_debug)
+ printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index);
+#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+ /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they
+ * are refered by name through rename, remove
+ */
+ if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) {
+ /* current initialization doesnot take care of rename source, if there is no
+ * create or lookup before that source, the source object will not exist when
+ * rename occurs
+ */
+ rename_rmdir_noent_reply_num++;
+ } else
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) {
+ /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK
+ * the initialization considers only the lookup return OK and created the file in the initialization
+ * so in trace play the first lookup return OK
+ */
+ RFS_ASSERT (1);
+ } else if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) {
+ /* due to similar reason as above, the initialization code initializes the symbolic link as a normal
+ * file already
+ */
+ RFS_ASSERT (1);
+ } else
+#endif
+#ifndef TAKE_CARE_NOEMPTY_RMDIR
+ /* the remove packet seems got lost in the trace capture, so replay can not finish */
+ if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) {
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ RMDIR3args * rmdir_argp;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2);
+ rmdir_argp = (RMDIR3args *)arg_res;
+
+ memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3));
+ memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */
+ args.to.name = buf2;
+ sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name);
+
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ Nfs_timers[Init]);
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ if (reply.status!=NFS3_OK)
+ printf ("change rmdir into rename, reply.status %d\n", reply.status);
+ RFS_ASSERT (reply.status==NFS3_OK);
+ rmdir_not_empty_reply_num ++;
+#endif
+#ifndef TAKE_CARE_ACCESS_ERROR
+ } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) {
+ loose_access_control_reply_num ++;
+#endif
+#ifdef NO_DEPENDENCY_TABLE
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) {
+ lookup_err_due_to_rename_num ++;
+#endif
+#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH
+ /* if the looked return EACCESS in the trace, means the object still exists
+ * should have initialized, right not don't initialize it, hence play status
+ * could be ENOENT
+ */
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) {
+ lookup_eaccess_enoent_mismatch_num ++;
+#endif
+#ifdef TOLERANT_READ_IO_ERR
+ } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) {
+ read_io_err_num ++;
+#endif
+ } else {
+ int i;
+ for (i=min_dep_index; i<max_dep_index; i++)
+ printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line);
+ RFS_ASSERT (0);
+ }
+ } else
+ proper_reply_num ++;
+
+}
+
+/* return -1 if there is no reply being received
+ * return the dep_index if the corresponding reply has been received
+ */
+inline int receive_next_reply (int busy_flag)
+{
+ int dep_index;
+ int biod_index;
+ int proc;
+ char * line;
+ char * reply_line;
+ sfs_op_type *op_ptr; /* per operation info */
+ int ret;
+ int status;
+ int trace_status;
+ char * errmsg;
+
+ /* wait for reply */
+ start_profile (&valid_poll_and_get_reply_profile);
+ start_profile (&invalid_poll_and_get_reply_profile);
+
+ if (busy_flag == BUSY) {
+ poll_timeout = 0;
+ poll_timeout_0_num ++;
+ } else {
+ poll_timeout = 10000; /* 10000 or 2000 is a better number in non-debugging state */
+ poll_timeout_pos_num ++;
+ }
+
+ biod_index = poll_and_get_reply (poll_timeout);
+ if (biod_index==-1) {
+ end_profile (&invalid_poll_and_get_reply_profile);
+ return -1;
+ };
+ end_profile (&valid_poll_and_get_reply_profile);
+
+ start_profile (&decode_reply_profile);
+ /* check the corresponding request */
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+
+ if (dep_tab[dep_index].flag != DEP_FLAG_SENT) {
+ printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n",
+ dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status,
+ dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec,
+ dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec );
+ printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT);
+
+ /* decode the reply */
+ rfs_Ops[proc].setres (arg_res, buf1);
+ ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res);
+ RFS_ASSERT (ret == RPC_SUCCESS);
+ status = *((int *)arg_res);
+ errmsg = nfs3_strerror (status);
+ end_profile (&decode_reply_profile);
+
+ start_profile (&check_reply_profile);
+ /* compare with the reply in the trace */
+ line = dep_tab[dep_index].line;
+ reply_line = dep_tab[dep_index].reply_line;
+ trace_status = dep_tab[dep_index].trace_status;
+
+ /* print the result, trace play progress indicator */
+ if (rfs_debug || ( !profile_debug && ((dep_index %10000)==0) ) )
+ fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec);
+
+ /* error checking */
+ check_reply (proc, biod_index, dep_index, status, errmsg, trace_status);
+
+ /* free resources */
+ finish_request (biod_index, dep_index, status);
+
+ /* get statistics */
+ if (status == trace_status) {
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop));
+ end_profile (&check_reply_profile);
+
+ //start_profile (&add_create_object_profile);
+/*
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==MKNOD)) {
+#else
+#endif
+*/
+ if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) {
+ RFS_ASSERT (reply_line);
+ if (status!=NFS3_OK) {
+ RFS_ASSERT (proc==SYMLINK);
+ } else {
+ if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2')
+ add_new_file_system_object(proc, dep_index, line, reply_line);
+ }
+ }
+ //end_profile (&add_create_object_profile);
+}
+
+inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line)
+{
+ char * child_trace_fh;
+ fh_map_t * parent_entryp;
+ char component_name[MAX_PLAY_PATH_SIZE];
+ char * parent_trace_fh;
+ char child_path[MAX_PLAY_PATH_SIZE];
+ post_op_fh3 * post_op_fh3_child;
+ char * reply_trace_fh;
+ nfs_fh3 * child_fh3;
+
+ parent_trace_fh = strstr (line, "fh");
+ RFS_ASSERT (parent_trace_fh);
+ parent_trace_fh +=3;
+ parent_entryp = lookup_fh (parent_trace_fh);
+ RFS_ASSERT (parent_entryp);
+ parse_name (parent_trace_fh+65, component_name);
+ strcpy (child_path, parent_entryp->path);
+ strcat (child_path, "/");
+ strcat (child_path, component_name);
+
+ /* find the corresponding create request */
+ //printf ("before find reply trace_fh reply_line %s\n", reply_line);
+ reply_trace_fh = find_reply_trace_fh (reply_line);
+ RFS_ASSERT (reply_trace_fh != NULL);
+ switch (proc) {
+ case CREATE:
+ RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKDIR:
+ RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case SYMLINK:
+ RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKNOD:
+ RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case LOOKUP:
+ RFS_ASSERT (proc==LOOKUP);
+ child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' ');
+ reply_trace_fh[TRACE_FH_SIZE] = 0;
+ add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */
+ reply_trace_fh[TRACE_FH_SIZE] = ' ';
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void trace_play(void)
+{
+
+ /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY
+ * when either there is request to send or there is reply being
+ * received. Otherwise it is IDLE. The timeout for polling replies
+ * is set to 0 when BUSY, it is set to the waiting time to the first
+ * request outside of current <min_dep_index, max_dep_index> window when IDLE.
+ */
+ int busy_flag = BUSY;
+
+ //int dep_index; /* index into dependency table: dep_tab */
+ //int biod_index; /* index into outstanding requests: biod_reqp */
+
+ int count = 0;
+ min_dep_index = 0;
+ max_dep_index = 0;
+ adjust_play_window(busy_flag, &poll_timeout);
+
+ start_profile (&total_profile);
+ while ((min_dep_index<dep_tab_size) || (num_out_reqs>0)) {
+
+ if (busy_flag == IDLE) {
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+ }
+
+ //start_profile (&adjust_play_window_profile);
+ //adjust_play_window (flag, &poll_timeout);
+ //adjust_play_window (flag+(max_dep_index-min_dep_index), &poll_timeout);
+ adjust_play_window (busy_flag, &poll_timeout);
+ if (rfs_debug)
+ printf("num_out_reqs %d\n", num_out_reqs);
+ busy_flag = IDLE;
+ //end_profile (&adjust_play_window_profile);
+
+ start_profile (&execute_next_request_profile);
+ while (execute_next_request()!=-1)
+ busy_flag = BUSY;
+ end_profile (&execute_next_request_profile);
+
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1)
+ busy_flag = BUSY;
+#endif
+ end_profile (&receive_next_reply_profile);
+ }
+ end_profile (&total_profile);
+
+ print_profile ("total_profile", &total_profile);
+ printf("\n");
+ //print_profile ("check_timeout", &check_timeout_profile);
+ //printf("\n");
+ //print_profile ("adjust_play_window", &adjust_play_window_profile);
+ //printf("\n");
+ print_profile ("execute_next_request_profile", &execute_next_request_profile);
+ print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile);
+ print_profile ("prepare_argument", &prepare_argument_profile);
+ print_profile ("biod_clnt_call", &biod_clnt_call_profile);
+ printf("\n");
+ print_profile ("receive_next_reply", &receive_next_reply_profile);
+ print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ print_profile ("decode_reply", &decode_reply_profile);
+ print_profile ("check_reply", &check_reply_profile);
+ //print_profile ("add_create_object", &add_create_object_profile);
+ printf("\n");
+
+ printf ("min_dep_index %d dep_tab_size %d num_out_reqs %d\n", min_dep_index, dep_tab_size, num_out_reqs);
+}
+/* sfs_c_chd.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+#include "nfsd_nfsfh_cust.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout);
+extern char * parse_name (char * t, char * buf);
+
+/* forward definitions for local functions */
+static int init_rpc(void);
+
+/* RFS: forward definitions for local functions */
+void init_ops(void);
+static void init_signal();
+extern void init_file_system (void);
+extern void init_dep_tab (void);
+static void read_trace(char * trace_file);
+static void read_fh_map();
+static void init_play (char * mount_point);
+static void trace_play(void);
+static void print_result(void);
+static int get_nextop(void);
+static int check_timeout(void);
+static struct biod_req * get_biod_req(int dep_tab_index);
+static int lookup_biod_req (int xid);
+static void init_time_offset(void);
+static void adjust_play_window (int flag, int * poll_timeout);
+static int poll_and_get_reply (int usecs);
+static char * nfs3_strerror(int status);
+static void check_clock(void);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void usage(void);
+void init_dep_tab_entry (int dep_index);
+extern fh_map_t * lookup_fh (char * trace_fh );
+static inline void finish_request (int biod_index, int dep_index, int status);
+static void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line);
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+
+static char io_buf[BUFSIZ]; /* io buffer for print out messages */
+
+char * sfs_Myname;
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+int Validate = 0; /* fake variable */
+int Child_num = 0; /* fake: child index */
+int Tcp = 0; /* We implement UDP first */
+int Client_num = 1; /* fake: number of client */
+uid_t Real_uid;
+gid_t Cur_gid;
+uid_t Cur_uid;
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+void print_usage()
+{
+ printf("sfs3 hostname:mount_dir trace_file fh_map_file\n");
+ exit;
+}
+
+void read_dep_tab()
+{
+#ifdef NO_DEPENDENCY_TABLE
+ int i;
+ char * line;
+ char * trace_fh;
+ fh_map_t * fh_map_entry;
+ int req_num_with_new_fh = 0;
+ int req_num_with_discard_fh = 0;
+ int req_num_with_init_fh =0;
+
+ for (i=0; i<memory_trace_size; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='C') {
+ trace_fh = strstr (line, "fh");
+ RFS_ASSERT (trace_fh);
+ trace_fh += 3;
+ fh_map_entry = lookup_fh (trace_fh);
+ if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) {
+ req_num_with_discard_fh ++;
+ continue;
+ }
+ if (fh_map_entry)
+ req_num_with_init_fh ++;
+ else
+ req_num_with_new_fh ++;
+
+ dep_tab[dep_tab_size].disk_index = memory_trace[i].disk_index;
+ dep_tab[dep_tab_size].line = memory_trace[i].line;
+ if ((dep_tab_size%100000)==0)
+ printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_size, dep_tab[dep_tab_size].disk_index, i, memory_trace[i].disk_index);
+ dep_tab_size ++;
+ }
+ }
+#else
+ RFS_ASSERT (0);
+#endif
+ printf ("read_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+}
+
+void init_profile()
+{
+ memset (&total_profile, 0, sizeof(total_profile));
+
+ memset (&execute_next_request_profile, 0, sizeof(execute_next_request_profile));
+ memset (&valid_get_nextop_profile, 0, sizeof(valid_get_nextop_profile));
+ memset (&invalid_get_nextop_profile, 0, sizeof(invalid_get_nextop_profile));
+ memset (&prepare_argument_profile, 0, sizeof(prepare_argument_profile));
+ memset (&biod_clnt_call_profile, 0, sizeof(biod_clnt_call_profile));
+
+ memset (&receive_next_reply_profile, 0, sizeof(receive_next_reply_profile));
+ memset (&valid_poll_and_get_reply_profile, 0, sizeof(valid_poll_and_get_reply_profile));
+ memset (&invalid_poll_and_get_reply_profile, 0, sizeof(invalid_poll_and_get_reply_profile));
+ memset (&decode_reply_profile, 0, sizeof(decode_reply_profile));
+ memset (&check_reply_profile, 0, sizeof(check_reply_profile));
+ memset (&add_create_object_profile, 0, sizeof(add_create_object_profile));
+
+ memset (&check_timeout_profile, 0, sizeof(check_timeout_profile));
+ memset (&adjust_play_window_profile, 0, sizeof(adjust_play_window_profile));
+}
+
+int main(int argc, char ** argv)
+{
+ extern char * optarg;
+ char tracefile[256];
+ int i;
+ int memory_trace_size;
+
+ if (argc==2 && !strcmp(argv[1],"-help")) {
+ print_usage();
+ exit(0);
+ }
+
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+ /*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+ //Real_uid = 513;
+ //Cur_gid = 513;
+
+ Nfs_timers = Nfs_udp_timers;
+
+ init_file_system ();
+
+ init_signal();
+ //init_play (argv[1]);
+ init_play ("capella:/p5/RFSFS");
+ init_profile();
+ init_fh_map();
+ //read_fh_map (argv[3]);
+ read_fh_map ("fh-path-map");
+ init_dep_tab(); /* and dep_tab_size */
+ //read_trace (argv[2]);
+ read_trace ("anon-lair62-011130-1100.txt");
+ stage = READ_DEP_TAB_STAGE;
+ read_dep_tab();
+
+ for (i=0; i<dep_tab_size; i++) {
+ RFS_ASSERT (dep_tab[i].flag == DEP_FLAG_FREE)
+ init_dep_tab_entry (i);
+ }
+ stage = TRACE_PLAY_STAGE;
+ init_time_offset();
+ trace_play ();
+ print_result();
+ printf("ffff\n");
+}
+
+void init_ops (void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/* Set up the signal handlers for all signals */
+void init_signal()
+{
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+#else
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ // RFS (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+#endif
+}
+
+void
+init_play (
+ char * mount_point) /* Mount point for remote FS */
+{
+ char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */
+ CLIENT * mount_client_ptr; /* Mount client handle */
+
+ if (!rfs_debug);
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ sfs_Myname = namebuf;
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, mount_point, mount_client_ptr);
+
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ init_counters();
+
+
+}
+
+void read_trace (char * tracefile)
+{
+ FILE * fp;
+ char buf[1024];
+ // char * t=buf;
+ int disk_index=0;
+
+ fp = fopen(tracefile, "r");
+ RFS_ASSERT (fp!=NULL);
+ while (fgets(buf, 1024, fp)) {
+ //printf ("buf: %s buf[36] %c\n", buf, buf[36]);
+ //if (buf[36]=='C' || strstr(buf, "create") || strstr(buf, "lookup")) {
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='C' || strstr(buf, "create") ||
+ strstr(buf, "mkdir") || strstr(buf, "symlink") || strstr(buf, "mknod") || strstr(buf, "lookup")) {
+#endif
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("strlen(buf) %d buf %s \n", strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+
+ /* store the request to memory */
+ strcpy (memory_trace[memory_trace_size].line, buf);
+ memory_trace[memory_trace_size].disk_index = disk_index;
+ memory_trace_size ++;
+
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ printf ("memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ if ((disk_index%100000)==0)
+ fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size);
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ } else {
+ RFS_ASSERT (buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ }
+#endif
+ disk_index ++;
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size );
+
+}
+
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline int disk_index_to_memory_index (int disk_index)
+{
+ static int memory_index = 0;
+ if (disk_index > memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index < disk_index) {
+ memory_index++;
+ RFS_ASSERT (memory_index < MAX_MEMORY_TRACE_LINES);
+ }
+ };
+ if (disk_index < memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index > disk_index) {
+ memory_index--;
+ RFS_ASSERT (memory_index >=0);
+ }
+ };
+
+ RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index);
+ return memory_index;
+}
+#else
+#define disk_index_to_memory_index(disk_index) disk_index
+#endif
+
+#define get_line_by_disk_index(disk_index) \
+ memory_trace[disk_index_to_memory_index(disk_index)].line
+
+inline char * find_reply_line (char * command_line, int cur_disk_index)
+{
+ int i;
+ char * line;
+ char * p;
+ int request_memory_index = disk_index_to_memory_index (cur_disk_index);
+ for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&line[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS])))
+ return line;
+ }
+ }
+ return NULL;
+}
+
+inline int find_reply_status (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ if (!strstr(line, "OK")) {
+ p=strstr(line, " 6 read ");
+ if (p) {
+ p+=strlen(" 6 read ");
+ } else {
+ p = strstr (line, "status=XXX");
+ RFS_ASSERT (p);
+ p--;
+ RFS_ASSERT (*p==' ');
+ while (*p==' ')
+ p--;
+ while (*p!=' ') {
+ p--;
+ }
+ p++;
+ }
+ sscanf (p, "%x", &i);
+ if ((i<=0) || (i>10000))
+ printf("line %s\n", line);
+ RFS_ASSERT (i>0 && i<10009);
+ }
+ return i;
+}
+
+inline char * find_reply_trace_fh (char * line)
+{
+ char * p;
+ p = strstr (line, "OK fh");
+ if (!p)
+ printf ("find_reply_trace_fh line %s\n", line);
+ RFS_ASSERT (p);
+ return p+6;
+}
+
+inline int disk_index_to_dep_index(int cur_dep_index, int disk_index)
+{
+ int i;
+ for (i=cur_dep_index; i>min_dep_index; i--) {
+ if (dep_tab[i].disk_index == disk_index)
+ return i;
+ }
+ RFS_ASSERT (0);
+}
+
+inline int is_play_candidate (int dep_index)
+{
+#ifndef TAKE_CARE_CREATE_MODE_BY_DAN
+ int trace_reply_status;
+ char * reply_line;
+ if ((dep_tab[dep_index].proc==CREATE)) {
+ /* for a failed create in trace, trace_replay just ignore */
+ reply_line = find_reply_line (dep_tab[dep_index].line, dep_tab[dep_index].disk_index);
+ if (reply_line == NULL) {
+ if (dependency_debug)
+ printf ("disk[%d] can not find the reply line, assume trace_reply_status OK\n", dep_tab[dep_index].disk_index);
+ trace_reply_status = NFS3_OK;
+ } else
+ trace_reply_status = find_reply_status (reply_line);
+ /* many time the trace create fail due to access control, but trace_play will success because our access control
+ * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests
+ */
+ if ((dep_tab[dep_index].proc == CREATE) && (trace_reply_status!=NFS3_OK)) {
+ if (dependency_debug)
+ printf ("disk[%d] ignore failed create in trace, trace_reply_status %d line %s", dep_tab[dep_index].disk_index, trace_reply_status, dep_tab[dep_index].line);
+ failed_create_command_num ++;
+ return FALSE;
+ }
+ }
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((dep_tab[dep_index].proc==READLINK)) { /* send request */
+ skipped_readlink_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_CUSTOM_COMMAND
+ /* this line has a file handle which should belong to discard but it is not
+ * the file handle directly appears as parent directory in a lookup request
+ * the return value is NOENT, the parent directory should have been initialized
+ * but the initialization code just ignored all lookup request which didn't success
+ * including NOENT even though the parent directory is still valid.
+ */
+ if ((dep_tab[dep_index].disk_index==262213 ||
+ dep_tab[dep_index].disk_index==214402 )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+inline int is_dir_op (int proc)
+{
+ switch (proc) {
+ case MKDIR:
+ case CREATE:
+ case LINK:
+ case SYMLINK:
+ case MKNOD:
+ case REMOVE:
+ case RMDIR:
+ case RENAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+inline int is_create_op (int proc)
+{
+ if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline int is_delete_op (int proc)
+{
+ if (proc==REMOVE || proc==RMDIR || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline char * find_lead_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh");
+ RFS_ASSERT (p);
+ p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+inline char * find_another_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh2");
+ RFS_ASSERT (p);
+ p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+/* return the index of next request in dep_tab.
+ * Return -1 if there is no suitable request to send
+ */
+inline int get_nextop(void)
+{
+ int i,j, k;
+ int * t;
+ static int dep_index = -2;
+ char * line;
+ char * p;
+ static int min_wait_fhandle_dep_index = DEP_TAB_SIZE;
+ int proc;
+ int flag;
+
+ //if (dep_index < min_dep_index-1)
+ // dep_index = min_dep_index-1;
+
+ dep_index = min_dep_index-1;
+ for (i=0; i<max_dep_index-min_dep_index; i++) {
+ dep_index ++;
+ if (dep_index == max_dep_index) {
+ dep_index = min_dep_index;
+ }
+
+ proc = dep_tab[dep_index].proc;
+ flag = dep_tab[dep_index].flag;
+
+ if (dependency_debug)
+ printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+#ifdef NO_DEPENDENCY_TABLE
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT) {
+ if (is_play_candidate(dep_index)==TRUE) {
+ /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle
+ * used in the request */
+ if (proc==LINK || proc==RENAME) {
+ dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = 0;
+ } else {
+ dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = (fh_map_t *)1;
+ };
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+#ifdef TIME_PLAY
+ dep_tab[dep_index].skip_sec = skip_sec;
+#endif
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index);
+ } else {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) {
+
+ if (!dep_tab[dep_index].fh)
+ dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh);
+ if (!dep_tab[dep_index].fh_2)
+ dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2);
+
+ /* need to wait for file handle */
+ if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) {
+ if (dependency_debug)
+ printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE;
+ sfs_gettime (&dep_tab[dep_index].start);
+ if (dep_index < min_wait_fhandle_dep_index)
+ min_wait_fhandle_dep_index = dep_index;
+ } else {
+ struct ladtime tmp;
+ sfs_gettime (&tmp);
+ SUBTIME (tmp, dep_tab[dep_index].start);
+#ifdef TIME_PLAY
+ RFS_ASSERT (tmp.sec < 20 + (skip_sec - dep_tab[dep_index].skip_sec));
+#else
+ RFS_ASSERT (tmp.sec < 20 );
+#endif
+ }
+ continue;
+ }
+
+ /* file handle ready, adjust_min_wait_fhandle_dep_index */
+ if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) {
+ if (dep_index == min_wait_fhandle_dep_index) {
+ min_wait_fhandle_dep_index = dep_tab_size;
+ for (j=dep_index+1; j<max_dep_index; j++) {
+ if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) {
+ min_wait_fhandle_dep_index = j;
+ break;
+ }
+ }
+ }
+ }
+ if (dependency_debug)
+ printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY;
+
+ /* the normal file operation can be executed now */
+ if (!is_dir_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ if (dependency_debug)
+ printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index);
+ /* the directory operation need to lock the directory first */
+ if (dep_tab[dep_index].fh->lock) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) {
+ int j = dep_tab[dep_index].fh - fh_map;
+ if (dependency_debug) {
+ printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock);
+ printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) {
+ if (dependency_debug)
+ printf ("continue to wait for directory lock\n");
+ continue;
+ }
+ if (dependency_debug)
+ printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j);
+ dep_tab[dep_index].fh->lock = 1;
+ if (proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 1;
+
+ /* the non-delete directory operation can proceed now */
+ if (!is_delete_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ /* the delete operation can proceed if nobody ahead is waiting for fhandle */
+ /* probably this condition is not strong enough */
+// if ((min_wait_fhandle_dep_index<dep_index) ) {
+ if (dep_index!=min_dep_index) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE;
+ continue;
+ }
+ dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY;
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) {
+// if (min_wait_fhandle_dep_index > dep_index) {
+ if (dep_index==min_dep_index) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ }
+#else
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT){
+ for (j=0, t=&(dep_tab[dep_index].dep_ops[0]);
+ (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0);
+ j++, t++) {
+ if (*t !=-1) {
+ if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) { /* The depended request has been finished */
+ *t = -1;
+ dep_tab[dep_index].cur_dep_num --;
+ }
+ }
+ }
+
+ if (dep_tab[dep_index].cur_dep_num == 0) {
+ return dep_index;
+ }
+ }
+#endif
+ }
+
+ if (dependency_debug)
+ printf ("get_nexop return -1\n");
+ return -1;
+}
+
+int check_timeout(void)
+{
+ static int biod_index = 0;
+ int i;
+ int dep_index; /* index into dep_tab */
+ int proc;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime timeout;
+
+ sfs_gettime (¤t);
+
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (biod_reqp[biod_index].in_use==TRUE) {
+ timeout = biod_reqp[biod_index].timeout;
+ if ((current.sec>timeout.sec) ||
+ ((current.sec==timeout.sec) && (current.usec>timeout.usec))) {
+
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+ op_ptr->results.timeout_calls++;
+ Ops[TOTAL].results.timeout_calls++;
+
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT);
+
+ if (is_create_op(proc)) {
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+ printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ }
+ //RFS_ASSERT (!is_create_op(proc));
+
+ //printf ("timeout request: biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec);
+ }
+ }
+ }
+}
+
+/* Allocate a biod_req entry to send and receive request dep_tab[dep_index]
+ * build the cross reference between dep_tab entry and biod_req entry
+ */
+struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (!biod_reqp[biod_index].in_use) {
+ biod_reqp[biod_index].in_use = 1;
+ biod_reqp[biod_index].dep_tab_index = dep_index;
+ dep_tab[dep_index].biod_req_index = biod_index;
+ return &(biod_reqp[biod_index]);
+ }
+ }
+ return NULL;
+}
+
+/* Return index into biod_reqp
+ * return -1 upon failure
+ */
+int lookup_biod_req (int xid)
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ /* give a NULL as timeout pointer may cause indefinitely block */
+ if (biod_reqp[biod_index].xid == xid) {
+ return biod_index;
+ }
+ }
+ return -1;
+}
+
+extern struct ladtime test_start;
+void init_time_offset(void)
+{
+ struct ladtime tmp1;
+ struct ladtime tmp2;
+
+ test_start.sec = 0;
+ test_start.usec = 0;
+ sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */
+#ifdef SPEED_UP
+ DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */
+#endif
+
+ tmp2 = trace_starttime; /* tmp2 = trace_starttime */
+ SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */
+ time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void init_dep_tab_entry (int dep_index)
+{
+ static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+ static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14,
+ 13, 21, 10, 15, 11, 12, 16, 23, 17, 20,
+ 22, 19};
+ char * line;
+ int version;
+ int nfsproc;
+ int msgid;
+
+ //line = get_line_by_disk_index (dep_tab[dep_index].disk_index);
+ line = dep_tab[dep_index].line;
+ sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec));
+ sscanf (&line[39], "%x %x", &msgid, &nfsproc);
+ if (line[TRACE_VERSION_POS]=='2') {
+ dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc];
+ RFS_ASSERT (nfsproc <18);
+ } else {
+ /* This is for debug purpose */
+ if (line[TRACE_VERSION_POS] !='3') {
+ fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line);
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1);
+ if (!line)
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2);
+ RFS_ASSERT (line);
+ fprintf(stderr, "previousline %s\n", line);
+ }
+ RFS_ASSERT (line[TRACE_VERSION_POS] =='3');
+ if (nfsproc >= NFS3_PROCEDURE_COUNT) {
+ fprintf(stderr, "proc %d line %s\n", nfsproc, line);
+
+ }
+ RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT);
+ dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc];
+ }
+ RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS);
+ dep_tab[dep_index].flag = DEP_FLAG_INIT;
+}
+
+/* initialize status, reply status, reply lines in one shot is probably better ????????????????
+
+static void adjust_play_window (int flag, int * poll_timeout)
+{
+ struct ladtime max_window_time;
+ static struct ladtime max_poll_time = {0, 2000, 0};
+ struct ladtime t;
+ int old_min = min_dep_index;
+ int old_max = max_dep_index;
+ int i;
+ char * line;
+
+ for (; (dep_tab[min_dep_index].flag == DEP_FLAG_DONE) && (min_dep_index<dep_tab_size); min_dep_index++)
+ ;
+ RFS_ASSERT (min_dep_index <= max_dep_index);
+
+ /* max_trace_window_time = current *|/ SCALE + trace_starttime */
+ sfs_gettime (¤t);
+
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+ MULTIME (current, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ DIVTIME (current, PLAY_SCALE);
+#endif
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+
+ while ((max_dep_index<min_dep_index+MAX_PLAY_WINDOW) && (max_dep_index<dep_tab_size))
+ {
+ t.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ t.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+
+ if (adjust_play_window_debug)
+ printf ("max_window_time sec %d usec %d : max_dep_index %d, t.sec %d t.usec %d\n", max_window_time.sec, max_window_time.usec, max_dep_index, t.sec, t.usec );
+
+ if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec))
+ break;
+
+ max_dep_index++;
+ }
+
+ /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */
+ if (max_dep_index == min_dep_index+MAX_PLAY_WINDOW) {
+ //printf ("can not catch up the speed, max_dep_index %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", max_dep_index, min_dep_index);
+ //printf (".");
+ can_not_catch_speed_num ++;
+ }
+ //RFS_ASSERT (max_dep_index < min_dep_index+MAX_PLAY_WINDOW);
+#else
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+ max_dep_index = min_dep_index + MAX_PLAY_WINDOW;
+ if (max_dep_index >dep_tab_size)
+ max_dep_index = dep_tab_size;
+#endif
+
+ if (flag == BUSY)
+ *poll_timeout = 0;
+ else if (max_dep_index == dep_tab_size) {
+ *poll_timeout = 1000000; /* poll_timeout set to 1 second for the last request */
+ } else {
+#ifdef TIME_PLAY
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+ if (adjust_play_window_debug)
+ printf ("dep_tab[max_dep_index %d].timestamp %d:%d, max_window_time %d:%d\n",
+ max_dep_index, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec);
+
+ SUBTIME (tmp, max_window_time);
+#ifdef SPEED_UP
+ DIVTIME (tmp, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp, PLAY_SCALE);
+#endif
+ tmp1 = tmp;
+
+ if (tmp.sec > max_poll_time.sec) {
+
+ if (rfs_debug)
+ printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n",
+ max_dep_index, dep_tab[max_dep_index].timestamp.tv_sec, dep_tab[max_dep_index].timestamp.tv_usec, max_window_time.sec, max_window_time.usec);
+ printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec);
+ SUBTIME (tmp, max_poll_time);
+ tmp.usec = 0;
+ skip_sec += tmp.sec;
+ SUBTIME (test_start, tmp);
+ tmp = max_poll_time;
+ }
+
+ RFS_ASSERT ((tmp.sec < 1000));
+ if ((tmp.sec ==0) && (tmp.usec==0)) {
+ *poll_timeout = 0;
+ } else
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+#else
+ /*
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[max_dep_index].timestamp.tv_sec;
+ tmp.usec = dep_tab[max_dep_index].timestamp.tv_usec;
+ tmp1.sec = dep_tab[max_dep_index-1].timestamp.tv_sec;
+ tmp1.usec = dep_tab[max_dep_index-1].timestamp.tv_usec;
+ SUBTIME (tmp, tmp1);
+ RFS_ASSERT ((tmp.sec < 1000));
+ RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0)));
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+ */
+
+ *poll_timeout = 100000;
+#endif
+ }
+ if (rfs_debug)
+ printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout %d \n", flag, old_min, min_dep_index, old_max, max_dep_index, *poll_timeout);
+}
+
+/* poll for usecs and receive, after receive one reply,
+ * return index in biod_reqp of the corresponding request
+ */
+int poll_and_get_reply (int usecs)
+{
+ int biod_index = -1;
+ int xid;
+ int error;
+ struct timeval zero_time = {0, 0}; /* Immediately return */
+
+ do {
+ error = biod_poll_wait (NFS_client, usecs);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (rfs_debug) {
+ (void) fprintf(stderr, "biod_poll_wait error\n");
+ perror ("");
+ (void) fflush(stderr);
+ }
+ break;
+ case 0:
+ break;
+ default:
+#ifdef UDP
+ error = get_areply_udp (NFS_client, &xid, &zero_time);
+ // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */
+ // RFS_ASSERT (error!= RPC_CANTRECV);
+ RFS_ASSERT (error == RPC_SUCCESS);
+
+ biod_index = lookup_biod_req (xid);
+ sfs_gettime (&(biod_reqp[biod_index].stop));
+#else
+ RFS_ASSERT (0);
+#endif
+ }
+ } while (0);
+ return biod_index;
+}
+
+void print_result(void)
+{
+ int i, j;
+ struct ladtime t;
+ int dep_index;
+ int avg_msecs;
+ unsigned long long tmp;
+ int avg_usecs;
+
+ printf("read_data_bytes %d owe %d GB and %d bytes, adjusted %d times \n",read_data_total, read_data_owe_GB, read_data_owe, read_data_adjust_times);
+ printf("write_data_bytes %d owe %d GB and %d bytes, adjusted %d times \n",write_data_total, write_data_owe_GB, write_data_owe, write_data_adjust_times);
+ printf("failed_create_command_num %d skipped_readlink_command_num %d skipped_custom_command_num %d\n missing_reply_num %d rename_rmdir_noent_reply_num %d rmdir_not_empty_reply_num %d\n loose_access_control_reply_num %d, proper_reply_num %d, lookup_err_due_to_rename %d\n",
+ failed_create_command_num, skipped_readlink_command_num, skipped_custom_command_num,
+ missing_reply_num, rename_rmdir_noent_reply_num, rmdir_not_empty_reply_num,
+ loose_access_control_reply_num, lookup_err_due_to_rename_num, proper_reply_num );
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stdout, "trace play result:\n");
+ (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n");
+ for (i=0; i<NOPS+1; i++) {
+ if (Ops[i].results.good_calls==0) {
+ avg_msecs = 0;
+ avg_usecs = 0;
+ } else {
+ tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec;
+ avg_msecs = 0;
+ avg_usecs = tmp/Ops[i].results.good_calls;
+/*
+ avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls;
+ avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls;
+*/
+ }
+ (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n",
+ Ops[i].name,
+ (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls,
+ Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls,
+ Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs);
+ }
+ (void) fflush (stdout);
+ }
+
+ clnt_destroy(NFS_client);
+ biod_term();
+
+// print_dump(Client_num, Child_num);
+ return;
+ dep_index = 0;
+ printf ("[%4d] %s \tstart %4d:%6d \n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec);
+ for (i=1; i<dep_tab_size*2; i++) {
+ dep_index = event_order[i];
+ if (dep_index >0)
+ printf ("[%4d] %s \tstart %4d:%6d \n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec);
+ else {
+ dep_index = -dep_index;
+ t=dep_tab[dep_index].stop;
+ SUBTIME (t, dep_tab[dep_index].start);
+ printf ("\t\t\t\t\t[%4d] %s stop %4d:%6d\tinterval %4d:%6d %s\n",
+ dep_index, Ops[dep_tab[dep_index].proc].name, dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[dep_index].status));
+ }
+ }
+/*
+
+ for (i=0, j=0; i<dep_tab_size || j<dep_tab_size; ) {
+ if ((i==dep_tab_size) ||
+ (LARGERTIME(dep_tab[i].start, dep_tab[j].stop) && j<dep_tab_size)) {
+ t=dep_tab[j].stop;
+ SUBTIME (t, dep_tab[j].start);
+ printf ("dep_tab[%d].proc %s stop %d:%d interval %d:%d status %s\n",
+ j, Ops[dep_tab[j].proc].name, dep_tab[j].stop.sec, dep_tab[j].stop.usec, t.sec, t.usec, nfs3_strerror(dep_tab[j].status));
+ j++;
+ } else {
+ printf ("dep_tab[%d].proc %s start %d:%d \n",
+ i, Ops[dep_tab[i].proc].name, dep_tab[i].start.sec, dep_tab[i].start.usec);
+ i++;
+ }
+ }
+*/
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ int dummy = 0;
+
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+
+ if (biod_init(dummy, dummy) == -1) {
+ return(-1);
+ }
+
+ return(0);
+} /* init_rpc */
+
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.timeout_calls = 0; // RFS
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "Timeout");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "trace play usage");
+}
+extern void init_file_system (void)
+{
+ return;
+}
+
+extern void init_dep_tab (void)
+{
+ int i;
+ memset (&dep_tab, 0, sizeof(dep_tab));
+#ifdef notdef
+ dep_tab[0].disk_index = 0;
+ dep_tab[1].disk_index = 2;
+ dep_tab[2].disk_index = 3;
+ dep_tab[3].disk_index = 5;
+ dep_tab[4].disk_index = 7;
+ dep_tab[5].disk_index = 9;
+ dep_tab[6].disk_index = 11;
+ dep_tab[7].disk_index = 12;
+ dep_tab[8].disk_index = 15;
+ dep_tab[9].disk_index = 17;
+ dep_tab[10].disk_index = 18;
+ dep_tab[11].disk_index = 20;
+ dep_tab_size = 12;
+ //dep_tab_size = 2;
+#endif
+}
+
+extern void init_dep_tab_old (void)
+{
+ int i;
+
+ Cur_file_ptr = &Export_dir;
+ Cur_uid = Real_uid;
+
+ for (i=0; i<5; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec+i*10;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+
+ for (i=5; i<10; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+
+ dep_tab[2].init_dep_num = 2;
+ dep_tab[2].cur_dep_num = 2;
+ dep_tab[2].dep_ops[0] = 0;
+ dep_tab[2].dep_ops[1] = 1;
+
+ // printf ("trace_starttime (%d %d)\n", trace_starttime.sec, trace_starttime.usec);
+
+ /*
+ for (i=2; i<4; i++) {
+ dep_tab[i].flag = DEP_FLAG_INIT;
+ dep_tab[i].proc = CREATE;
+ dep_tab[i].timestamp.tv_sec = trace_starttime.sec+i*10;
+ dep_tab[i].timestamp.tv_usec = trace_starttime.usec;
+ dep_tab[i].init_dep_num = 0;
+ dep_tab[i].cur_dep_num = 0;
+ }
+ */
+
+ dep_tab_size = 10;
+
+ for (i=0; i<dep_tab_size; i++)
+ {
+ printf("dep_tab[%d].timestamp (%d %d)\n", i, dep_tab[i].timestamp.tv_sec, dep_tab[i].timestamp.tv_usec);
+ }
+}
+
+void show_fhandle (nfs_fh3 * fhp)
+{
+ struct knfs_fh * kfhp = (struct knfs_fh *)fhp;
+
+ int dev;
+
+ if (quiet_flag)
+ return;
+
+ RFS_ASSERT (kfhp->fh_version == 1);
+ RFS_ASSERT (kfhp->fh_fsid_type == 0);
+ RFS_ASSERT (kfhp->fh_auth_type == 0);
+
+ dev = ntohs(kfhp->fh_dev_major);
+ dev = dev<<8;
+ dev = dev + ntohs(kfhp->fh_dev_minor);
+
+ /* kfhp->fh_dev_ino hold the inode number of export point of the mounted
+ * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted,
+ * then fh_dev_ino hold the inode number of t1, not t2
+ */
+
+ switch (kfhp->fh_fileid_type) {
+ case 0:
+ printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino);
+ break;
+ case 1:
+ printf("fh:type 1 %d %x dev %x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino);
+ break;
+ case 2:
+ printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino);
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+}
+
+nfs_fh3 zero_fhandle;
+int init_fh_map ()
+{
+ memset (fh_map, 0, sizeof (fh_map));
+ memset(fh_htable, 0, sizeof (fh_htable));
+ memset (&zero_fhandle, 0, sizeof(nfs_fh3));
+ printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000);
+ fh_i = 0;
+}
+
+int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh)
+{
+ char * old_trace_fh;
+
+ /* first lookup if the entry for fh is already in the table */
+ struct generic_entry * p;
+
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (p) {
+ RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL);
+ RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE);
+ fh_map[p->key3].flag = map_flag;
+ RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ RFS_ASSERT (!strcmp(fh_map[p->key3].path, path));
+ RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3)));
+ memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3));
+ if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE))
+ printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path);
+ return 0;
+ }
+
+ fh_map[fh_i].flag = map_flag;
+ fh_map[fh_i].lock = 0;
+ memcpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE);
+
+ RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE);
+ strcpy (fh_map [fh_i].path, path);
+ if (map_flag==FH_MAP_FLAG_COMPLETE)
+ memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3));
+ else
+ memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3));
+
+ if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) {
+ printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path);
+ if (map_flag == FH_MAP_FLAG_COMPLETE)
+ show_fhandle(play_fh);
+ else
+ printf("null\n");
+ }
+
+/*
+ if (map_flag == FH_MAP_FLAG_DISCARD)
+ printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path);
+*/
+
+ generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE);
+
+ fh_i = (fh_i+1);
+ RFS_ASSERT (fh_i < FH_MAP_SIZE);
+
+ return 0;
+};
+
+fh_map_t * lookup_fh (char * trace_fh )
+{
+ struct generic_entry * p;
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (fh_map_debug==1)
+ printf ("lookup trace_fh %s\n", trace_fh);
+
+ if (p) {
+ if (fh_map_debug==1) {
+ printf ("found: fh_i %d path %s play_fh:", p->key3, fh_map[p->key3].path);
+ show_fhandle(&fh_map[p->key3].play_fh);
+ }
+ return (&(fh_map[p->key3]));
+ } else {
+ //printf ("lookup_fh %s not found\n", trace_fh);
+ if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) {
+ printf ("lookup not found trace_fh %s\n", trace_fh);
+ }
+ return NULL;
+ }
+ RFS_ASSERT (0);
+}
+
+int delete_fh (char * trace_fh, int fh_map_index)
+{
+ generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE);
+ return 0;
+};
+
+void lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ static int i=0;
+
+ /* set up the arguments */
+ (void) memcpy((char *) &args.what.dir, (char *) parent,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+ sfs_gettime(&stop);
+
+ if (rpc_stat !=RPC_SUCCESS) {
+ printf("rpc_stat %d\n", rpc_stat);
+ perror("");
+ }
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ RFS_ASSERT (reply.status == NFS3_OK);
+ (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3));
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+#define MAX_PATH_DEPTH 20
+ nfs_fh3 parents[MAX_PATH_DEPTH];
+ char * lookup_path_ptr[MAX_PATH_DEPTH];
+ char lookup_path [MAX_PLAY_PATH_SIZE];
+ int depth;
+ int new_dir_flag = 0;
+
+ depth = 0;
+ memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr));
+ memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ strcpy(lookup_path, "/");
+ lookup_path_ptr[depth]=&lookup_path[0];
+
+ fp = fopen(fh_map_file, "r");
+ RFS_ASSERT (fp!=NULL);
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+
+ if (fh_i % 10000==0)
+ printf("%d fh_map entry read\n", fh_i);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+ if (fh_map_debug) {
+ printf("%d fgets return %s\n", fh_i, buf);
+ printf("depth %d lookup_path %s\n", depth, lookup_path);
+ }
+ //for (i=0; i<=depth; i++)
+ //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]);
+ //printf("\n");
+#ifdef COMPRESS_TRACE_FH
+ for (i=0; i<TRACE_FH_SIZE/8; i++) {
+ strncpy(intbuf, buf+i*8, 8);
+ sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now
+ }
+ trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */
+#else
+ memcpy(trace_fh, buf, TRACE_FH_SIZE);
+ trace_path = buf + TRACE_FH_SIZE +1;
+#endif
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */
+#endif
+
+#ifdef NO_DEPENDENCY_TABLE
+ if (!strncmp (trace_path, "DISCARD", 7)) {
+ map_flag = FH_MAP_FLAG_DISCARD;
+ add_fh (map_flag, buf, trace_path, 0);
+ continue;
+ }
+#endif
+
+ p = trace_path+strlen(trace_path)-2;
+ while (*p!='/')
+ p--;
+ p++;
+ //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1);
+ //RFS_ASSERT (p>trace_path);
+
+ if (strncmp(lookup_path, trace_path, p-trace_path)) {
+ printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path);
+ }
+ RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path));
+ //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */
+ while (strlen(lookup_path)>p-trace_path && depth>0) {
+ //printf("depth--\n");
+ if (depth<=0)
+ printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth);
+ RFS_ASSERT (depth>0);
+ *lookup_path_ptr[depth]=0;
+ lookup_path_ptr[depth]=0;
+ depth--;
+ }
+ RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0));
+
+
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ if (buf[TRACE_FH_SIZE*2+1]=='Y') {
+ map_flag = FH_MAP_FLAG_COMPLETE;
+ } else {
+ map_flag = FH_MAP_FLAG_PARTIAL;
+ RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N');
+ }
+#else
+ map_flag = FH_MAP_FLAG_COMPLETE;
+#endif
+ if ((*(p+strlen(p)-1))=='/') {
+ *(p+strlen(p)-1)=0;
+ new_dir_flag = 1;
+ } else
+ new_dir_flag = 0;
+
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ lookup_init_filesystem (&parents[depth], p, &parents[depth+1]);
+ add_fh (map_flag, buf, trace_path, &parents[depth+1]);
+ } else
+ add_fh (map_flag, buf, trace_path, 0);
+
+ if (new_dir_flag) {
+ /* the new fhandle is of a directory */
+ lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path);
+ strcat (lookup_path, p);
+ strcat (lookup_path, "/");
+
+ //printf("depth++\n");
+ depth++;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (fh_map_debug) {
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+
+ fprintf(stderr, "total %d requests \n", i);
+ }
+}
+
+int f()
+{
+ return 1;
+}
+
+inline void finish_request (int biod_index, int dep_index, int status)
+{
+ /* the ending operation, same as when a request time out */
+ dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */
+ dep_tab[dep_index].status = status;
+ event_order[event_order_index++] = -dep_index;
+ biod_reqp[biod_index].in_use = FALSE;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ if (is_dir_op(dep_tab[dep_index].proc)) {
+ int j;
+ RFS_ASSERT (dep_tab[dep_index].fh->lock = 1);
+ dep_tab[dep_index].fh->lock = 0;
+ if (dep_tab[dep_index].proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 0;
+ j = dep_tab[dep_index].fh-fh_map;
+ if (dependency_debug) {
+ printf ("fh_map[%d] is UNlocked\n",j);
+ printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ }
+ num_out_reqs --;
+}
+
+/* the request argument may have pointers pointing to buffers, e.g. the name in lookup,
+ * the target of symlink, the write data */
+char arg_res[MAX_ARG_RES_SIZE];
+int poll_timeout = 0; /* timeout in usecs */
+char buf1 [MAX_BUF1_SIZE];
+char buf2 [MAX_BUF2_SIZE];
+#define NFS3_REPLY_MISS -1
+
+int execute_next_request ()
+{
+ int dep_index;
+ int proc;
+ char * line;
+ struct biod_req * reqp;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime call_timeout;
+
+ start_profile (&valid_get_nextop_profile);
+ start_profile (&invalid_get_nextop_profile);
+ dep_index = get_nextop();
+ if (dep_index == -1) {
+ end_profile (&invalid_get_nextop_profile);
+ return dep_index;
+ };
+ end_profile (&valid_get_nextop_profile);
+
+ start_profile (&prepare_argument_profile);
+ line = dep_tab[dep_index].line;
+ if ((dep_index%(10000))==0) {
+#ifndef TIME_PLAY
+ fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs);
+#else
+ fprintf (stderr, "processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+#ifdef SPEED_UP
+ if (can_not_catch_speed_num < 2000) {
+ PLAY_SCALE ++;
+ printf ("set PLAY_SCALE to %d\n", PLAY_SCALE);
+ };
+ if (can_not_catch_speed_num > 50000) {
+ PLAY_SCALE /= 2;
+ } else {
+ if (can_not_catch_speed_num > 5000) {
+ PLAY_SCALE -= 2;
+ if (PLAY_SCALE < 1)
+ PLAY_SCALE = 1;
+ }
+ }
+#endif
+ can_not_catch_speed_num = 0;
+#endif
+ }
+ if (rfs_debug)
+ printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line);
+
+ proc = dep_tab[dep_index].proc;
+ rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2);
+
+ op_ptr = &Ops[proc];
+ reqp = get_biod_req (dep_index);
+ RFS_ASSERT (reqp);
+
+ call_timeout.sec = 4; //Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+
+ /* make the call */
+ sfs_gettime(&(reqp->start));
+ end_profile (&prepare_argument_profile);
+ start_profile (&biod_clnt_call_profile);
+#define REAL_PLAY
+#ifdef REAL_PLAY
+ reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc,
+ rfs_Ops[proc].xdr_arg, arg_res);
+#else
+
+ reqp->xid = dep_index+1; /* just fake a message id and let it expire */
+#endif
+ if (reqp->xid != 0) {
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ num_out_reqs++;
+ dep_tab[dep_index].flag = DEP_FLAG_SENT;
+ event_order[event_order_index++] = dep_index;
+ } else
+ RFS_ASSERT (0);
+
+ dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */
+ end_profile (&biod_clnt_call_profile);
+}
+
+void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_reply_status)
+{
+ if (((status!=trace_reply_status)) && (trace_reply_status!=NFS3_REPLY_MISS)) {
+ if (rfs_debug)
+ printf ("receive problem reply, xid %x nfs_ret %d %s trace_reply_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_reply_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index);
+#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+ /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they
+ * are refered by name through rename, remove
+ */
+ if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_reply_status ==0)) {
+ /* current initialization doesnot take care of rename source, if there is no
+ * create or lookup before that source, the source object will not exist when
+ * rename occurs
+ */
+ RFS_ASSERT (1);
+ rename_rmdir_noent_reply_num++;
+ } else
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_reply_status==NFS3ERR_NOENT)) {
+ /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK
+ * the initialization considers only the lookup return OK and created the file in the initialization
+ * so in trace play the first lookup return OK
+ */
+ RFS_ASSERT (1);
+ } else if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_reply_status == 0)) {
+ /* due to similar reason as above, the initialization code initializes the symbolic link as a normal
+ * file already
+ */
+ RFS_ASSERT (1);
+ } else
+#endif
+#ifndef TAKE_CARE_NOEMPTY_RMDIR
+ /* the remove packet seems got lost in the trace capture, so replay can not finish */
+ if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) {
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ RMDIR3args * rmdir_argp;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2);
+ rmdir_argp = (RMDIR3args *)arg_res;
+
+ memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3));
+ memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */
+ args.to.name = buf2;
+ sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name);
+
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ Nfs_timers[Init]);
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ if (reply.status!=NFS3_OK)
+ printf ("change rmdir into rename, reply.status %d\n", reply.status);
+ RFS_ASSERT (reply.status==NFS3_OK);
+ rmdir_not_empty_reply_num ++;
+#endif
+#ifndef TAKE_CARE_ACCESS_ERROR
+ } else if ((status==0) && (trace_reply_status==NFS3ERR_ACCES)) {
+ loose_access_control_reply_num ++;
+#endif
+#ifdef NO_DEPENDENCY_TABLE
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_reply_status==NFS3_OK)) {
+ lookup_err_due_to_rename_num ++;
+#endif
+ } else {
+ int i;
+ for (i=min_dep_index; i<max_dep_index; i++)
+ printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line);
+ RFS_ASSERT (0);
+ }
+ } else
+ proper_reply_num ++;
+
+}
+
+/* return -1 if there is no reply being received
+ * return the dep_index if the corresponding reply has been received
+ */
+int receive_next_reply (int busy_flag)
+{
+ int dep_index;
+ int biod_index;
+ int proc;
+ char * line;
+ char * reply_line;
+ sfs_op_type *op_ptr; /* per operation info */
+ int ret;
+ int status;
+ int trace_reply_status;
+ char * errmsg;
+
+ /* wait for reply */
+ start_profile (&valid_poll_and_get_reply_profile);
+ start_profile (&invalid_poll_and_get_reply_profile);
+/*
+ if (busy_flag == IDLE)
+ poll_timeout = 0;
+ else
+ poll_timeout = 10000;
+*/
+ biod_index = poll_and_get_reply (poll_timeout);
+ if (biod_index==-1) {
+ end_profile (&invalid_poll_and_get_reply_profile);
+ return -1;
+ };
+ end_profile (&valid_poll_and_get_reply_profile);
+
+ start_profile (&decode_reply_profile);
+ /* check the corresponding request */
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+
+ if (dep_tab[dep_index].flag != DEP_FLAG_SENT) {
+ printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n",
+ dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status,
+ dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec,
+ dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec );
+ printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT);
+
+ /* decode the reply */
+ rfs_Ops[proc].setres (arg_res, buf1);
+ ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res);
+ RFS_ASSERT (ret == RPC_SUCCESS);
+ status = *((int *)arg_res);
+ errmsg = nfs3_strerror (status);
+ end_profile (&decode_reply_profile);
+
+ start_profile (&check_reply_profile);
+ /* compare with the reply in the trace */
+ line = dep_tab[dep_index].line;
+ reply_line = find_reply_line (line, dep_tab[dep_index].disk_index);
+ if (reply_line == NULL) {
+ //printf ("disk[%d] can not find the reply line, assume trace_reply_status OK\n", dep_tab[dep_index].disk_index);
+ trace_reply_status = NFS3_REPLY_MISS;
+ missing_reply_num ++;
+ } else
+ trace_reply_status = find_reply_status (reply_line);
+
+ /* print the result, trace play progress indicator */
+ if ((dep_index %10000)==0 || rfs_debug)
+ fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_reply_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_reply_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec);
+
+ /* error checking */
+ check_reply (proc, biod_index, dep_index, status, errmsg, trace_reply_status);
+
+ /* free resources */
+ finish_request (biod_index, dep_index, status);
+
+ /* get statistics */
+ if (status == trace_reply_status) {
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop));
+ end_profile (&check_reply_profile);
+
+ //start_profile (&add_create_object_profile);
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if (trace_reply_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==MKNOD)) {
+#else
+ if (trace_reply_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) {
+#endif
+ RFS_ASSERT (status == NFS_OK);
+ RFS_ASSERT (reply_line);
+ add_new_file_system_object(proc, dep_index, line, reply_line);
+ }
+ //end_profile (&add_create_object_profile);
+}
+
+void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line)
+{
+ char * child_trace_fh;
+ fh_map_t * parent_entryp;
+ char component_name[MAX_PLAY_PATH_SIZE];
+ char * parent_trace_fh;
+ char child_path[MAX_PLAY_PATH_SIZE];
+ post_op_fh3 * post_op_fh3_child;
+ char * reply_trace_fh;
+ nfs_fh3 * child_fh3;
+
+ parent_trace_fh = strstr (line, "fh");
+ RFS_ASSERT (parent_trace_fh);
+ parent_trace_fh +=3;
+ parent_entryp = lookup_fh (parent_trace_fh);
+ RFS_ASSERT (parent_entryp);
+ parse_name (parent_trace_fh+65, component_name);
+ strcpy (child_path, parent_entryp->path);
+ strcat (child_path, "/");
+ strcat (child_path, component_name);
+
+ /* find the corresponding create request */
+ //printf ("before find reply trace_fh reply_line %s\n", reply_line);
+ reply_trace_fh = find_reply_trace_fh (reply_line);
+ RFS_ASSERT (reply_trace_fh != NULL);
+ switch (proc) {
+ case CREATE:
+ RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKDIR:
+ RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case SYMLINK:
+ RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKNOD:
+ RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case LOOKUP:
+ RFS_ASSERT (proc==LOOKUP);
+ child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' ');
+ reply_trace_fh[TRACE_FH_SIZE] = 0;
+ add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */
+ reply_trace_fh[TRACE_FH_SIZE] = ' ';
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void trace_play(void)
+{
+
+ /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY
+ * when either there is request to send or there is reply being
+ * received. Otherwise it is IDLE. The timeout for polling replies
+ * is set to 0 when BUSY, it is set to the waiting time to the first
+ * request outside of current <min_dep_index, max_dep_index> window when IDLE.
+ */
+ int busy_flag = BUSY;
+
+ //int dep_index; /* index into dependency table: dep_tab */
+ //int biod_index; /* index into outstanding requests: biod_reqp */
+
+ int count = 0;
+ min_dep_index = 0;
+ max_dep_index = 0;
+ adjust_play_window(busy_flag, &poll_timeout);
+
+ start_profile (&total_profile);
+ while ((min_dep_index<dep_tab_size) || (num_out_reqs>0)) {
+
+ if (busy_flag == IDLE) {
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+ }
+
+ //start_profile (&adjust_play_window_profile);
+ //adjust_play_window (flag, &poll_timeout);
+ //adjust_play_window (flag+(max_dep_index-min_dep_index), &poll_timeout);
+ adjust_play_window (busy_flag, &poll_timeout);
+ if (rfs_debug)
+ printf("num_out_reqs %d\n", num_out_reqs);
+ busy_flag = IDLE;
+ //end_profile (&adjust_play_window_profile);
+
+ start_profile (&execute_next_request_profile);
+ while (execute_next_request()!=-1)
+ busy_flag = BUSY;
+ end_profile (&execute_next_request_profile);
+
+ start_profile (&receive_next_reply_profile);
+ while (receive_next_reply(busy_flag)!=-1)
+ busy_flag = BUSY;
+ end_profile (&receive_next_reply_profile);
+ }
+ end_profile (&total_profile);
+
+ print_profile ("total_profile", &total_profile);
+ printf("\n");
+ print_profile ("check_timeout", &check_timeout_profile);
+ printf("\n");
+ print_profile ("adjust_play_window", &adjust_play_window_profile);
+ printf("\n");
+ print_profile ("execute_next_request_profile", &execute_next_request_profile);
+ print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile);
+ print_profile ("prepare_argument", &prepare_argument_profile);
+ print_profile ("biod_clnt_call", &biod_clnt_call_profile);
+ printf("\n");
+ print_profile ("receive_next_reply", &receive_next_reply_profile);
+ print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ print_profile ("decode_reply", &decode_reply_profile);
+ print_profile ("check_reply", &check_reply_profile);
+ print_profile ("add_create_object", &add_create_object_profile);
+ printf("\n");
+
+ printf ("min_dep_index %d dep_tab_size %d num_out_reqs %d\n", min_dep_index, dep_tab_size, num_out_reqs);
+}
+/* sfs_c_chd.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+
+/* forward definitions for local functions */
+static void check_call_rate(void);
+static void init_targets(void);
+static int init_rpc(void);
+static void init_testdir(void);
+static int do_op(void);
+static int op(int);
+static void init_dirlayout(void);
+
+
+/*
+ * ------------------- File Set Size Control -------------------------
+ */
+static uint_t Setattr_borrowed = 0; /* setattr op used for file truncate */
+static uint_t Create_borrowed = 0; /* create op used for file truncate */
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static float Child_call_load; /* per child call/sec rate */
+static float Child_req_load; /* per child req/sec rate */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+static int Measurement_in_progress = 0;
+
+static sfs_work_set_type Dir_working_set;
+static sfs_work_set_type Io_working_set;
+static sfs_work_set_type Non_io_working_set;
+static sfs_work_set_type Symlink_working_set;
+
+static uint_t Files_created = 0; /* unique integer part of file names */
+static char io_buf[BUFSIZ];
+int generations =
+ (_WORKING_SET_AT_25_OPS_PER_SEC/_GROUP_DIVISOR) * _MIN_GROUPS;
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+
+/*
+ * Child number 'child_num'. Initialize internal data structure and
+ * the test directory, then notify parent (through log file) that we
+ * are ready to start generating 'load' calls per second into the current
+ * working directory, or optionally, into the directories specified by
+ * 'argc' and 'argv'. Wait for the start signal, and then generate load
+ * until we complete all our goal for calls or until the run time expires,
+ * depending on the 'Timed_run' flag. The run time expires when the parent
+ * sends the stop signal.
+ */
+void
+child(
+ int child_num,
+ int children,
+ float load,
+ int argc,
+ char *argv[])
+{
+ char namebuf[NFS_MAXNAMLEN]; /* unique name for this program */
+ char *nameptr;
+ int i; /* general use */
+ int op_count; /* ops completed during each request */
+ uint_t rand_sleep_msec; /* random sleep msec between calls */
+ uint_t current_msec; /* current test time in msecs */
+ double previous_pcnt;
+ CLIENT * mount_client_ptr; /* Mount client handle */
+ char * mount_point; /* Mount point for remote FS */
+ int Saveerrno;
+ int mnt_argc;
+
+ struct ladtime elapsed_time; /* Current_time - Start_time */
+ sfs_results_report_type report; /* final results log */
+
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ /* Change my name for error logging */
+ if ((nameptr = strrchr(sfs_Myname, '/')) != NULL)
+ sfs_Myname = ++nameptr;
+ (void) sprintf(namebuf, "%s%d", sfs_Myname, child_num);
+ sfs_Myname = namebuf;
+ Child_call_load = load;
+ Current_test_phase = Mount_phase;
+
+ /* Seed the random number generator based on my child number */
+
+ /*
+ * Note: If random seeds are allocated by the prime client
+ * then this code must change.
+ */
+ sfs_srandom((int)(load + Child_num + 1));
+
+ /* Setup user and group information */
+ Cur_uid = Real_uid;
+
+ /*
+ * Initialize call and request targets.
+ * Calls are the Over-The-Wire (OTW) operations that occur due to
+ * each request. A request may cause one or more calls.
+ * Initialize the child file info and mount the remote test directory.
+ * Set up the rpc and biod structures.
+ */
+ init_targets();
+ init_fileinfo();
+ init_dirlayout();
+
+ /*
+ * Mount points list:
+ * If the mount point list is equal to the number of procs (P), the
+ * mount point for child M is the M'th entry in the list.
+ * If the mount point list is greater than the number of procs (P), the
+ * mount point for client N child M is ((N - 1) * P) + M
+ */
+ if (argc == children)
+ mnt_argc = Child_num;
+ else
+ mnt_argc = (Client_num - 1) * children + Child_num;
+
+ if (mnt_argc >= argc) {
+ (void) fprintf(stderr,
+"%s: Invalid mount point list: required %d only specified %d mount points\n",
+ sfs_Myname, mnt_argc + 1, argc);
+ (void) generic_kill(0, SIGINT);
+ exit(181);
+ }
+
+ mount_point = argv[mnt_argc];
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(Child_num, mount_point, mount_client_ptr);
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ /*
+ * Tell parent I'm ready to initialize my test directory,
+ * wait for the go ahead signal.
+ */
+ if (write(Log_fd, "x", 1) != 1) {
+ (void) fprintf(stderr, "%s: can't write to synchronization file %s",
+ sfs_Myname, Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(147);
+ }
+ (void) pause();
+
+ if (DEBUG_CHILD_GENERAL) {
+ if (Timed_run) {
+ if (Prime_client) {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
+ Child_num, Child_call_load, Child_req_load,
+ Runtime - MULTICLIENT_OFFSET);
+ }
+ else {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
+ Child_num, Child_call_load, Child_req_load, Runtime);
+ }
+ } else {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n",
+ Child_num, Child_call_load, Child_req_load,
+ Ops[TOTAL].target_calls);
+ }
+ (void) fflush(stderr);
+ }
+
+ /* Initialize the test directory */
+ Current_test_phase = Populate_phase;
+ init_testdir();
+
+ /*
+ * activate the biod behaviour if desired
+ */
+ if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) {
+ biod_turn_on();
+ }
+
+ /*
+ * Tell parent I'm ready to start test, wait for the go ahead signal.
+ */
+ if (write(Log_fd, "x", 1) != 1) {
+ (void) fprintf(stderr, "%s: can't write to synchronization file %s\n",
+ sfs_Myname, Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(148);
+ }
+ (void) pause();
+
+ if (DEBUG_CHILD_GENERAL) {
+ if (Timed_run) {
+ if (Prime_client) {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
+ Child_num, Child_call_load, Child_req_load,
+ Runtime - MULTICLIENT_OFFSET);
+ }
+ else {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
+ Child_num, Child_call_load, Child_req_load, Runtime);
+ }
+ } else {
+ (void) fprintf(stderr,
+ "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n",
+ Child_num, Child_call_load, Child_req_load,
+ Ops[TOTAL].target_calls);
+ }
+ (void) fflush(stderr);
+ }
+
+
+ /* Start the warmup phase; initialize operation counters */
+ Current_test_phase = Warmup_phase;
+ init_counters();
+ Measurement_in_progress = 0;
+
+ /*
+ * Compute the average sleep time per call.
+ * Start off with the assumption that we can sleep half the time.
+ * Note: using msec-per-call to adjust sleeping time
+ * limits benchmark load rates to less than 1000 calls-per-sec-per-child.
+ */
+ Target_sleep_mspc = (int) (((1000.0 / Child_call_load) / 2.0) + .5);
+
+ /*
+ * Occasionally, check to see if ops are being generating at the
+ * correct rate. During the warmup phase, checks are made every 2 seconds.
+ * Hopefully, this will allow the test to reach steady state before the
+ * warmup phase ends. During the timed test run, checks are made every
+ * 10 seconds. The switch is made when we receive the start signal.
+ */
+ Msec_per_period = DEFAULT_WARM_RATE_CHECK * 1000;
+
+ /* Loop generating load */
+ while ((Timed_run && Runtime) ||
+ (!Timed_run &&
+ (Ops[TOTAL].results.good_calls < Ops[TOTAL].target_calls))) {
+
+ if (start_run_phase) {
+ init_counters();
+ Measurement_in_progress = 1;
+ /*
+ * Progress is checked every 10 seconds during the test run.
+ */
+ Msec_per_period = DEFAULT_RUN_RATE_CHECK * 1000;
+
+ start_run_phase = 0;
+ }
+
+ /* Do an NFS operation, unless we need to sleep for the whole period. */
+ if (Target_sleep_mspc < Msec_per_period)
+ op_count = do_op();
+ else
+ op_count = 0;
+
+ /* if the call was successful, add op_count to the period total. */
+ if (op_count > 0) {
+ Calls_this_period += op_count;
+ Reqs_this_period++;
+ }
+
+ /*
+ * If the call was successful,
+ * or we need to sleep for the whole period,
+ * sleep for a while before doing the next op.
+ */
+ if ((op_count > 0) || (Target_sleep_mspc >= Msec_per_period)) {
+ /*
+ * Sleep for the whole period or
+ * for a random (positive) time period in the range
+ * (Target_sleep_mspc +- 1/2(Target_sleep_mspc)).
+ */
+ if (Target_sleep_mspc >= Msec_per_period)
+ rand_sleep_msec = Msec_per_period;
+ else if (Target_sleep_mspc >= 1)
+ rand_sleep_msec = (Target_sleep_mspc >> 1)
+ + (sfs_random() % Target_sleep_mspc);
+ else
+ rand_sleep_msec = 0;
+
+ if (rand_sleep_msec != 0) {
+ if (DEBUG_CHILD_TIMING) {
+ (void) fprintf(stderr, "Child %d sleep for %d msec\n",
+ Child_num, rand_sleep_msec);
+ (void) fflush(stderr);
+ }
+ Sleep_msec_this_period += msec_sleep(rand_sleep_msec);
+ }
+ }
+
+ /*
+ * See if it's time to check our progress.
+ * If an operation was just performed, then Cur_time was updated
+ * in the op routine; otherwise we need to get Cur_time.
+ */
+ if (op_count <= 0) {
+ sfs_gettime(&Cur_time);
+ }
+
+ current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000);
+ if (DEBUG_CHILD_XPOINT) {
+ (void) fprintf(stderr, "cur=%d prev=%d per=%d\n",
+ current_msec, Previous_chkpnt_msec, Msec_per_period);
+ }
+
+ if ((current_msec - Previous_chkpnt_msec) > Msec_per_period) {
+ check_call_rate();
+ }
+
+ } /* end while more calls to make */
+
+ /*
+ * We are done generating our part of the load.
+ * Store total time in last slot of counts array.
+ *
+ * The last slot has the wall clock time of all the load generation.
+ * Individual slots have the wall clock time spent just for the op
+ * gen routine.
+ */
+ sfs_gettime(&Cur_time);
+ Measurement_in_progress = 0;
+ elapsed_time.sec = Cur_time.sec;
+ elapsed_time.usec = Cur_time.usec;
+ SUBTIME(elapsed_time, Starttime);
+
+ Ops[TOTAL].results.time.sec = elapsed_time.sec;
+ Ops[TOTAL].results.time.usec = elapsed_time.usec;
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr,
+ "%s: max fss %d KB min fss %d KB\n",
+ sfs_Myname, Most_fss_bytes, Least_fss_bytes);
+ (void) fflush(stderr);
+ }
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "Child %d Files:\n", Child_num);
+ for (i = 0; i < Num_io_files; i++)
+ (void) fprintf(stderr, "Io[%d] use %d xfer %d\n",
+ i, Io_files[i].use_cnt, Io_files[i].xfer_cnt);
+ for (i = 0; i < Num_non_io_files; i++)
+ (void) fprintf(stderr, "Non_io[%d] use %d xfer %d\n",
+ i, Non_io_files[i].use_cnt,
+ Non_io_files[i].xfer_cnt);
+ for (i = 0; i < Num_dir_files; i++)
+ (void) fprintf(stderr, "Dir[%d] use %d xfer %d\n",
+ i, Dirs[i].use_cnt, Dirs[i].xfer_cnt);
+ for (i = 0; i < Num_symlink_files; i++)
+ (void) fprintf(stderr, "Sym[%d] use %d xfer %d\n",
+ i, Symlinks[i].use_cnt, Symlinks[i].xfer_cnt);
+ (void) fflush(stderr);
+ }
+
+ if (DEBUG_CHILD_SETUP) {
+ int j, group_size, offset, index, tot;
+ for (i = 0; i < Io_working_set.access_group_cnt; i++) {
+ group_size = Io_working_set.access_group_size;
+ if (i < (Num_working_io_files -
+ ((Num_working_io_files / Io_working_set.access_group_cnt)
+ * Io_working_set.access_group_cnt)))
+ group_size += 1;
+ tot = 0;
+ for (j = 0; j < group_size; j++) {
+ offset = i + (j * Io_working_set.access_group_cnt);
+ index = Io_working_set.entries[offset].index;
+ tot += Io_files[index].use_cnt;
+ (void) fprintf(stderr, "Working[%d] use %d xfer %d\n",
+ offset, Io_files[index].use_cnt,
+ Io_files[index].xfer_cnt);
+ }
+ (void) fprintf(stderr, "Group %d total use %d\n", i, tot);
+ }
+ (void) fflush(stderr);
+ }
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "Child %d Ops:\n", Child_num);
+
+ previous_pcnt = 0.0;
+ (void) fprintf(stderr,
+ " calls reqs\n");
+ (void) fprintf(stderr,
+ " trgt actl trgt actl bad no trgt actl trgt actl\n");
+ (void) fprintf(stderr,
+ " name mix mix cnt cnt cnt cnt mix mix cnt cnt\n");
+
+ for (i = 0; i < NOPS + 1; i++) {
+ (void) fprintf(stderr,
+ "%11s %4d %4.1f %5d %5d %4d %3d %4.1f %4.1f %6d %6d\n",
+ Ops[i].name, Ops[i].mix_pcnt,
+ (float) (100 * Ops[i].results.good_calls)
+ / (float) Ops[TOTAL].results.good_calls,
+ Ops[i].target_calls, Ops[i].results.good_calls,
+ Ops[i].results.bad_calls, Ops[i].no_calls,
+ Ops[i].req_pcnt - previous_pcnt,
+ (float) (100 * Ops[i].req_cnt) / (float) Ops[TOTAL].req_cnt,
+ Ops[i].target_reqs, Ops[i].req_cnt);
+ previous_pcnt = Ops[i].req_pcnt;
+ }
+ (void) fflush(stderr);
+ }
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "Child %d made %d of %d calls in %ld sec\n",
+ Child_num, Ops[TOTAL].results.good_calls,
+ Ops[TOTAL].target_calls,
+ Ops[TOTAL].results.time.sec);
+ (void) fflush(stderr);
+ }
+
+ clnt_destroy(NFS_client);
+ biod_term();
+
+ /* write stats to log file (append mode) */
+ report.version = nfs_version;
+ for (i = 0; i < NOPS + 1; i++) {
+ report.results_buf[i] = Ops[i].results;
+ }
+ report.total_fss_bytes = Total_fss_bytes;
+ report.least_fss_bytes = Least_fss_bytes;
+ report.most_fss_bytes = Most_fss_bytes;
+ report.base_fss_bytes = Base_fss_bytes;
+
+ if (write(Log_fd, (char *) &report, sizeof(report)) == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't write to synchronization file %s ",
+ sfs_Myname, Logname);
+ errno = Saveerrno;
+ perror(Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(149);
+ }
+ (void) close(Log_fd);
+
+ print_dump(Client_num, Child_num);
+
+} /* child */
+
+
+/*
+ * -------------------- Call Target Initialization --------------------
+ */
+
+/*
+ * Initialize call and request targets.
+ */
+static void
+init_targets(void)
+{
+ int call_target; /* total number of calls to make */
+ int req_target; /* total number of reqs to make */
+ int32_t equal_mix; /* equal mix of operations */
+ int32_t slack; /* calls leftover after % mix */
+ int randnum; /* a random number */
+ int32_t i; /* general use */
+ double total_req_pcnt;
+ double previous_pcnt;
+ int nops_used = 0;
+
+
+ /*
+ * Compute number of target calls for each operation.
+ * These are used to estimate the number of filehandles
+ * that will be used for each type of operation.
+ */
+ call_target = Ops[TOTAL].target_calls;
+ Ops[TOTAL].target_calls = 0;
+
+ for (i = 0; i < NOPS; i++) {
+ Ops[i].target_calls = (Ops[i].mix_pcnt * call_target) / 100;
+ Ops[TOTAL].target_calls += Ops[i].target_calls;
+ if (Ops[i].mix_pcnt != 0)
+ nops_used++;
+ }
+
+ /* Put left over calls into the heavier mix operations. */
+ slack = call_target - Ops[TOTAL].target_calls;
+ equal_mix = (100 / nops_used) / 2;
+ while (slack > 0) {
+ randnum = sfs_random() % NOPS;
+ if (Ops[randnum].mix_pcnt != 0 && Ops[randnum].mix_pcnt >= equal_mix) {
+ Ops[randnum].target_calls++;
+ Ops[TOTAL].target_calls++;
+ slack--;
+ }
+ }
+
+ /*
+ * compute request targets (based on suggestions from M. Molloy, HP)
+ */
+
+ /* compute total of target requests, based on weighted ops */
+ total_req_pcnt = 0.0;
+ for (i = 0; i < NOPS ; i++) {
+ switch (i) {
+ case READ:
+ total_req_pcnt += ((double) Ops[i].mix_pcnt)
+ / Io_dist_ptr->avg_ops_per_read_req;
+ break;
+ case WRITE:
+ total_req_pcnt += ((double) Ops[i].mix_pcnt)
+ / Io_dist_ptr->avg_ops_per_write_req;
+ break;
+ case COMMIT: /* Commits never generate requests */
+ break;
+ default:
+ total_req_pcnt += (double) Ops[i].mix_pcnt;
+ break;
+ }
+ }
+
+ /*
+ * compute cumulative frequency distribution percentile for each op.
+ * This code assumes that the NULLCALL does not generate multiple
+ * OTW operations per request.
+ */
+ previous_pcnt = 0.0;
+ for (i = 0; i < NOPS; i++) {
+ switch (i) {
+ case READ:
+ Ops[i].req_pcnt = previous_pcnt +
+ (((100.0 * (double) Ops[i].mix_pcnt)
+ / Io_dist_ptr->avg_ops_per_read_req)
+ / total_req_pcnt);
+ break;
+ case WRITE:
+ Ops[i].req_pcnt = previous_pcnt +
+ (((100.0 * (double) Ops[i].mix_pcnt)
+ / Io_dist_ptr->avg_ops_per_write_req)
+ / total_req_pcnt);
+ break;
+ case COMMIT: /* Commits never generate requests */
+ Ops[i].req_pcnt = previous_pcnt;
+ break;
+ default:
+ Ops[i].req_pcnt = previous_pcnt +
+ ((100.0 * (double) Ops[i].mix_pcnt)
+ / total_req_pcnt);
+ break;
+ }
+ previous_pcnt = Ops[i].req_pcnt;
+ }
+ /* force last bucket to 100 */
+ Ops[NOPS-1].req_pcnt = 100;
+
+ /* compute the req load rate */
+ Child_req_load = (total_req_pcnt * Child_call_load) / 100.0;
+
+ /*
+ * Compute number of target reqs for each operation.
+ * These are used for debugging purposes.
+ */
+ req_target = (total_req_pcnt * Ops[TOTAL].target_calls) / 100;
+ Ops[TOTAL].target_reqs = 0;
+
+ previous_pcnt = 0.0;
+ for (i = 0; i < NOPS; i++) {
+ Ops[i].target_reqs = 0;
+ if (Ops[i].mix_pcnt != 0) {
+ Ops[i].target_reqs = ((Ops[i].req_pcnt - previous_pcnt) *
+ req_target) / 100;
+ }
+ Ops[TOTAL].target_reqs += Ops[i].target_reqs;
+ previous_pcnt = Ops[i].req_pcnt;
+ }
+
+ /* Put left over reqs into the heavier mix operations. */
+ slack = req_target - Ops[TOTAL].target_reqs;
+ equal_mix = (100 / nops_used) / 2;
+ while (slack > 0) {
+ randnum = sfs_random() % NOPS;
+ if (Ops[randnum].target_reqs != 0 &&
+ Ops[randnum].req_pcnt >= equal_mix) {
+ Ops[randnum].target_reqs++;
+ Ops[TOTAL].target_reqs++;
+ slack--;
+ }
+ }
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr,
+ " Op\t Op mix\tCalls\t\t Req mix\t Reqs\t\n");
+ previous_pcnt = 0.0;
+ for (i = 0; i < NOPS; i++) {
+ (void) fprintf(stderr, "%8s\t%8d\t%5d\t\t%8.2f\t%5d\n",
+ Ops[i].name,
+ Ops[i].mix_pcnt, Ops[i].target_calls,
+ Ops[i].req_pcnt - previous_pcnt,
+ Ops[i].target_reqs);
+ previous_pcnt = Ops[i].req_pcnt;
+ }
+ }
+} /* init_targets */
+
+
+/*
+ * ----------------------- File Set Initialization -----------------------
+ */
+
+static file_array_initialized = 0;
+static int file_size_array[100];
+
+/*
+ * For a value between 0-99, return a size based on distribution
+ */
+static int
+get_file_size(int i)
+{
+ if (i < 0 || i > 99)
+ return (0);
+
+ if (file_array_initialized == 0) {
+ int j, k;
+
+ for (j = 0, k = 0; j < 100; j++) {
+ if (j >= Default_file_size_dist[k].pcnt &&
+ Default_file_size_dist[k + 1].size != 0)
+ k++;
+ file_size_array[j] = Default_file_size_dist[k].size * 1024;
+ }
+ file_array_initialized++;
+ }
+ return (file_size_array[i]);
+}
+
+/*
+ * allocate and initialize the various file information structures.
+ */
+void
+init_fileinfo(void)
+{
+ int i, index;
+ int j;
+ int group_size, group_cnt;
+ int range, previous_range;
+ int next_value;
+ double lambda;
+ double e_to_the_lambda;
+ double cumulative_ratio;
+ int num_non_io_to_init;
+ int io_file_num = 0;
+ int files_per_generation;
+ sfs_fh_data *fh_datap;
+
+
+ /*
+ * Zero number of files created used to create unique names
+ */
+ Files_created = 0;
+
+ /*
+ * Dirs - Initialize the files info structure.
+ * Directories must come first, in initializing test dirs we
+ * need to make sure that any files deleted are no full directories
+ */
+ Num_dir_files =
+ Num_dirs + /* exist: readdir, rmdir */
+ Ops[MKDIR].target_calls + /* non-exist: mkdir */
+ Ops[RMDIR].target_calls; /* empty dir to be removed */
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: allocate %d directories\n",
+ sfs_Myname, Num_dir_files);
+ (void) fflush(stderr);
+ }
+ Dirs = (sfs_fh_type *) calloc(Num_dir_files, sizeof(sfs_fh_type));
+
+ if (Dirs == (sfs_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo dir calloc %d bytes failed",
+ sfs_Myname, Num_dir_files * sizeof(sfs_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(150);
+ }
+ for (i = 0; i < Num_dir_files; i++) {
+ Dirs[i].working_set = 0;
+ Dirs[i].state = Nonexistent;
+ if (i <= (Num_dirs + Ops[RMDIR].target_calls)) {
+ Dirs[i].initialize = 1;
+ Dirs[i].fh_data = (sfs_fh_data *)0;
+ }
+ Dirs[i].unique_num = i;
+ }
+
+ /* Working Set Directory Files - Initialize the working files array. */
+ Num_working_dirs = Num_dir_files;
+ Dir_working_set.entries = (sfs_work_fh_type *)
+ calloc(Num_working_dirs,
+ sizeof(sfs_work_fh_type));
+ if (Dir_working_set.entries == (sfs_work_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo wdir calloc %d bytes failed",
+ sfs_Myname, Num_working_dirs * sizeof(sfs_work_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(151);
+ }
+
+ /*
+ * Dirs are accessed uniformly. See Non_io_files for a description.
+ */
+ if (init_rand_range(Num_dir_files)) {
+ (void) fprintf(stderr, "%s: init_fileinfo dir init_rand_range failed",
+ sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(183);
+ }
+
+ for (i = 0; i < Num_working_dirs; i++) {
+ if (Num_working_dirs != Num_dir_files) {
+ /* generate a random subset */
+ index = rand_range(i);
+ } else {
+ /* match the working set one-to-one with the files */
+ index = i;
+ }
+
+ Dirs[index].working_set = 1;
+ Dir_working_set.entries[i].index = index;
+ Dir_working_set.entries[i].range = i + 1;
+ }
+ Dir_working_set.access_group_size = Num_working_dirs;
+ Dir_working_set.access_group_cnt = 1;
+
+ Dir_working_set.max_range = Num_working_dirs;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "\nDir size=%d cnt=%d max=%d\n",
+ Dir_working_set.access_group_size,
+ Dir_working_set.access_group_cnt,
+ Dir_working_set.max_range);
+ (void) fflush(stderr);
+ }
+
+
+ /*
+ * I/o Files - Initialize the files info structure to Num_io_files.
+ */
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: allocate %d i/o files, %d working\n",
+ sfs_Myname, Num_io_files, Num_working_io_files);
+ (void) fflush(stderr);
+ }
+
+ Io_files = (sfs_fh_type *) calloc(Num_io_files, sizeof(sfs_fh_type));
+ if (Io_files == (sfs_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo %d io files calloc %d bytes failed",
+ sfs_Myname, Num_io_files,
+ Num_io_files * sizeof(sfs_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(152);
+ }
+ io_file_num = 0;
+ for (i = 0; i < Num_io_files; i++) {
+ Io_files[i].working_set = 0;
+ Io_files[i].state = Nonexistent;
+ Io_files[i].initialize = 1;
+ Io_files[i].size = get_file_size(io_file_num % 100);
+ Io_files[i].unique_num = Files_created++;
+ /* Memory allocation for the fh_data will be done later. */
+ Io_files[i].fh_data = (sfs_fh_data *)0;
+ io_file_num++;
+ }
+
+ /*
+ * Working Set I/o Files - Initialize the working files array.
+ * Only Access_percent of the Io_files are put into the working set.
+ */
+ Io_working_set.entries = (sfs_work_fh_type *)
+ calloc(Num_working_io_files,
+ sizeof(sfs_work_fh_type));
+ if (Io_working_set.entries == (sfs_work_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo wio calloc %d bytes failed",
+ sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(153);
+ }
+
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "working_set: ");
+ (void) fflush(stderr);
+ }
+
+ /*
+ * For now, the access distribution is poisson. See below.
+ */
+/* #define UNIFORM_ACCESS */
+#define POISSON_ACCESS
+
+#ifdef UNIFORM_ACCESS
+ /*
+ * With a uniform access distribution, there is no need for access
+ * groups.
+ * Hopefully SPEC-SFS will agree on a non-uniform access function.
+ * (see below for an example using a poisson distribution).
+ */
+ if (init_rand_range(Num_io_files)) {
+ (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed",
+ sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(184);
+ }
+
+ for (i = 0; i < Num_working_io_files; i++) {
+ if (Num_working_io_files != Num_io_files) {
+ /* generate a random subset */
+ index = rand_range(i);
+ } else {
+ /* match the working set one-to-one with the files */
+ index = i;
+ }
+ Io_files[index].working_set = 1;
+ Io_working_set.entries[i].index = index;
+ Io_working_set.entries[i].range = i + 1;
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "%d,", index);
+ (void) fflush(stderr);
+ }
+ }
+ Io_working_set.access_group_size = Num_working_io_files;
+ Io_working_set.access_group_cnt = 1;
+ Io_working_set.max_range = Num_working_io_files;
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n",
+ Io_working_set.access_group_size,
+ Io_working_set.access_group_cnt,
+ Io_working_set.max_range);
+ (void) fflush(stderr);
+ }
+
+#endif /* ! UNIFORM_ACCESS */
+#ifdef POISSON_ACCESS
+
+ /*
+ * The working set is partitioned into access groups of Access_group_size
+ * files. Each group is assigned a probability of being accessed.
+ * This is implemented as a cumulative distribution table, with
+ * variable probabilities for each group. The distribution function
+ * is used to generate a sequence of values, one for each group.
+ * Each group is assigned a 'range' value that is the sum of all
+ * previous range values, plus the next value in the distribution
+ * sequence. Thus, the probability of choosing any particular group
+ * is equal to the relative height of the distribution curve at the
+ * point represented by that group.
+ * The choice is made by generating a random number in the range
+ * 0 up to (the sum of all values in the distribution sequence - 1),
+ * and finding the group with the greatest range value less than
+ * the random number.
+ * Once a group is chosen, a random number in the range
+ * 1 - Access_group_size is used to pick an entry from within the group.
+ * The entry chosen points to a file in the Io_files array.
+ * If the file at Io_files[index] is eligible for the operation,
+ * then it is accessed, otherwise, the access group is searched
+ * sequentially (mod Access_group_size with wrap-around) until an
+ * eligible file is found.
+ * Access_group_size is derived so that there are enough files
+ * in each group to give a good chance of finding an eligible file
+ * for each operation, but so that there are enough groups (each
+ * representing a point on the distribution curve) to generate a
+ * fairly smooth access distribution curve.
+ */
+
+ /*
+ * group_cnt = 8 + ((Num_working_io_files/500) * 4);
+ *
+ * The function is chosen to guarentee that each group contains
+ * at least 1 file, and, beginning with a base of 8 groups, the
+ * number of groups increases by 4 for each 500 files in the working
+ * set. It was arrived at heuristically. The goal is to put enough
+ * files into each group to ensure that a file with the right
+ * attributes can be found once the group is selected (which can be
+ * difficult for small working sets), while at the same time creating
+ * enough groups to provide enough points on the distribution curve
+ * to yield an interesting access distribution.
+ *
+ * Since this function is being computed per child, the interesting range
+ * of working set sizes is computed based on a range of per child load
+ * values from 1 op/sec to 100 op/sec. Note that this assumes an
+ * average server response time of at least 10 msec, which seems to be
+ * a good minimum value for a wide range of servers given the default
+ * mix of NFS operations.
+ * Based on these load values, the total file set, based on the default
+ * values of 10 MB/op and 38 files/MB, works out to 380 - 38000 files.
+ * The default working set of 10% of these files yields a working
+ * set size of 38 - 3800 files.
+ */
+
+ files_per_generation = (_GROUP_DIVISOR * generations) / _FILES_PER_GROUP;
+ Io_working_set.access_group_cnt = generations +
+ ((Num_working_io_files/files_per_generation) * generations);
+ /*
+ * if the number of files in the working set is not a multiple of
+ * the group size, then some groups will contain (group_size+1) files.
+ * Thus, this is the base group size.
+ */
+ Io_working_set.access_group_size = Num_working_io_files /
+ Io_working_set.access_group_cnt;
+
+ if (init_rand_range(Num_io_files)) {
+ (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed",
+ sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(185);
+ }
+
+ /* randomly set up working set of indices into Io_files */
+ for (i = 0; i < Num_working_io_files; i++) {
+ if (Num_working_io_files != Num_io_files) {
+ /* generate a random subset */
+ index = rand_range(i);
+ } else {
+ /* match the working set one-to-one with the files */
+ index = i;
+ }
+ Io_files[index].working_set = 1;
+ Io_working_set.entries[i].index = index;
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "%d,", index);
+ (void) fflush(stderr);
+ }
+ }
+
+ /* initialization for distribution function */
+ range = 0;
+ lambda = (double) (generations / 2);
+ if (lambda <= 0) lambda = 1;
+ e_to_the_lambda = exp(lambda);
+ cumulative_ratio = 1.0;
+
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr,
+ "\ngrp_cnt %d lambda %6.0f e_to_the_lambda %6.2f\n",
+ Io_working_set.access_group_cnt, lambda,
+ e_to_the_lambda);
+ (void) fflush(stderr);
+ }
+
+ /* assign a range to each group */
+ for (i = 0; i < Io_working_set.access_group_cnt; i++) {
+ /*
+ * get next value in poisson distribution sequence, using
+ * lambda^x / (e^(lambda) * x!) , for x=1,2,3,...,group_cnt
+ */
+ double probability;
+
+ if( i % generations == 0)
+ {
+ lambda = (double) (generations / 2);
+ if (lambda <= 0) lambda = 1;
+ e_to_the_lambda = exp(lambda);
+ cumulative_ratio = 1.0;
+ }
+ probability = cumulative_ratio/e_to_the_lambda;
+ if (probability <= 0.0 || probability > 1.0) {
+ (void) fprintf(stderr, "%s: access probability = %g while setting up Io_working_set, i=%d of %d\n",
+ sfs_Myname, probability,
+ i, Io_working_set.access_group_cnt);
+ (void) generic_kill(0, SIGINT);
+ exit(154);
+ }
+
+ /* convert probability to scaled integer */
+ next_value = (int) (PROB_SCALE * probability);
+
+ /* check for negative numbers */
+ if (next_value <= 0) {
+ (void) fprintf(stderr, "%s: next_value = %d while setting up Io_working_set, i=%d of %d\n",
+ sfs_Myname, next_value,
+ i, Io_working_set.access_group_cnt);
+ (void) generic_kill(0, SIGINT);
+ exit(154);
+ }
+
+ previous_range = range;
+ range = previous_range + next_value;
+ if (range <= previous_range || range < 0) {
+ (void) fprintf(stderr, "%s: range = %d previous_range = %d while setting up Io_working_set, i=%d of %d\n",
+ sfs_Myname, range, previous_range,
+ i, Io_working_set.access_group_cnt);
+ (void) generic_kill(0, SIGINT);
+ exit(154);
+ }
+
+ /* assign range value to each file in this group */
+ group_size = Io_working_set.access_group_size;
+ group_cnt = Io_working_set.access_group_cnt;
+ if (i < (Num_working_io_files -
+ ((Num_working_io_files / group_cnt) * group_cnt)))
+ group_size += 1;
+ for (j = 0; j < group_size; j++) {
+ index = i + (j * Io_working_set.access_group_cnt);
+ Io_working_set.entries[index].range = range;
+ }
+
+ cumulative_ratio *= lambda / (double) ((i%generations)+1);
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "group %d next %d range %d\n",
+ i, next_value, range);
+ (void) fflush(stderr);
+ }
+ }
+ Io_working_set.max_range = range;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n",
+ Io_working_set.access_group_size,
+ Io_working_set.access_group_cnt,
+ Io_working_set.max_range);
+ (void) fflush(stderr);
+ }
+#endif /* POISSON_ACCESS */
+
+
+ /* figure out how many files to allocate and initialize */
+
+ /* initialize half the non-I/O files */
+ /* NOTE: initializing half the non-i/o files works ok with the
+ default op mix. If the mix is changed affecting the
+ ratio of creations to removes, there may not be enough
+ empty slots for file creation (or there may not be
+ enough created during initialization to handle a lot of
+ removes that occur early in the test run), and this would
+ cause do_op() to fail to find a file appropriate for the
+ chosen op. This will result in the global variable
+ Ops[op].no_calls being incremented (turn on child level
+ debugging to check this count), and the do_op() local
+ variable aborted_ops to be incremented and checked during
+ runtime for too many failures.
+ */
+ num_non_io_to_init = Num_non_io_files * RATIO_NON_IO_INIT;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: allocate %d non-i/o files\n",
+ sfs_Myname, Num_non_io_files);
+ (void) fflush(stderr);
+ }
+ Non_io_files = (sfs_fh_type *)
+ calloc(Num_non_io_files, sizeof(sfs_fh_type));
+ if (Non_io_files == (sfs_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo nio calloc %d bytes failed",
+ sfs_Myname, Num_non_io_files * sizeof(sfs_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(154);
+ }
+ for (i = 0; i < Num_non_io_files; i++) {
+ Non_io_files[i].working_set = 0;
+ Non_io_files[i].state = Nonexistent;
+ if (i <= num_non_io_to_init)
+ Non_io_files[i].initialize = 1;
+ Non_io_files[i].size = get_file_size(io_file_num % 100);
+ Non_io_files[i].unique_num = Files_created++;
+ /* Allocation of fh_data will happen in init_testdir */
+ Non_io_files[i].fh_data = (sfs_fh_data *)0;
+ io_file_num++;
+ }
+
+ /* Working Set Non i/o Files - Initialize the working files array. */
+ Num_working_non_io_files = Num_non_io_files;
+ Non_io_working_set.entries = (sfs_work_fh_type *)
+ calloc(Num_working_non_io_files,
+ sizeof(sfs_work_fh_type));
+ if (Non_io_working_set.entries == (sfs_work_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo nwio calloc %d bytes failed",
+ sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(155);
+ }
+
+ /*
+ * Non_io_files are accessed uniformly. Each entry has a
+ * 1/Num_working_non_io_files change of being accessed.
+ * The choice is made by generating a random number in the range
+ * 0 through (Num_working_non_io_files - 1) and finding the entry
+ * with the greatest range value less than the random number.
+ * If the file at Non_io_files[index] is eligible for the operation,
+ * it is accessed, otherwise, the access group that the entry belongs
+ * to is searched sequentially until an eligible file is found.
+ * For non i/o files, all of the working set files are in the same
+ * access group (since they access is uniform, this is ok, and
+ * maximizes the chances of finding an eligible file).
+ */
+ if (init_rand_range(Num_non_io_files)) {
+ (void) fprintf(stderr, "%s: init_fileinfo non_io init_rand_range failed",
+ sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(186);
+ }
+
+ for (i = 0; i < Num_working_non_io_files; i++) {
+ if (Num_working_non_io_files != Num_non_io_files) {
+ /* generate a random subset */
+ index = rand_range(i);
+ } else {
+ /* match the working set one-to-one with the files */
+ index = i;
+ }
+ Non_io_files[index].working_set = 1;
+ Non_io_working_set.entries[i].index = index;
+ Non_io_working_set.entries[i].range = i + 1;
+ }
+ Non_io_working_set.access_group_size = Num_working_non_io_files;
+ Non_io_working_set.access_group_cnt = 1;
+ Non_io_working_set.max_range = Num_working_non_io_files;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "\nNon_io size=%d cnt=%d max=%d\n",
+ Non_io_working_set.access_group_size,
+ Non_io_working_set.access_group_cnt,
+ Non_io_working_set.max_range);
+ (void) fflush(stderr);
+ }
+
+
+ /* Symlinks - Initialize the files info structure. */
+ Num_symlink_files =
+ Num_symlinks + /* exist: readlink */
+ Ops[SYMLINK].target_calls; /* non-exist: symlink */
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: allocate %d symlinks\n",
+ sfs_Myname, Num_symlink_files);
+ (void) fflush(stderr);
+ }
+ Symlinks = (sfs_fh_type *)
+ calloc(Num_symlink_files, sizeof(sfs_fh_type));
+ if (Symlinks == (sfs_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo sym calloc %d bytes failed",
+ sfs_Myname, (Num_symlink_files * sizeof(sfs_fh_type)));
+ (void) generic_kill(0, SIGINT);
+ exit(156);
+ }
+ for (i = 0; i < Num_symlink_files; i++) {
+ Symlinks[i].working_set = 0;
+ Symlinks[i].state = Nonexistent;
+ if (i <= Num_symlinks)
+ Symlinks[i].initialize = 1;
+ Symlinks[i].fh_data = (sfs_fh_data *)0;
+ Symlinks[i].unique_num = i;
+ }
+
+ /* Working Set Symlinks - Initialize the working files array. */
+ /* This appears to cause the following loop to be mostly dead */
+ /* code. It is unclear why this line is here. One */
+ /* possibility is that Num_symlink_files should be */
+ /* Num_symlinks. XXX */
+ Num_working_symlinks = Num_symlink_files;
+ Symlink_working_set.entries = (sfs_work_fh_type *)
+ calloc(Num_working_symlinks,
+ sizeof(sfs_work_fh_type));
+ if (Symlink_working_set.entries == (sfs_work_fh_type *) 0) {
+ (void) fprintf(stderr,"%s: init_fileinfo wsym calloc %d bytes failed",
+ sfs_Myname, Num_working_symlinks * sizeof(sfs_work_fh_type));
+ (void) generic_kill(0, SIGINT);
+ exit(157);
+ }
+
+ /*
+ * Symlinks are accessed uniformly. See Non_io_files for a description.
+ */
+ if (init_rand_range(Num_symlink_files)) {
+ (void) fprintf(stderr, "%s: init_fileinfo sym init_rand_range failed",
+ sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(187);
+ }
+
+ for (i = 0; i < Num_working_symlinks; i++) {
+ if (Num_working_symlinks != Num_symlink_files) {
+ /* generate a random subset */
+ index = rand_range(i);
+ } else {
+ /* match the working set one-to-one with the files */
+ index = i;
+ }
+
+ Symlinks[index].working_set = 1;
+ Symlink_working_set.entries[i].index = index;
+ Symlink_working_set.entries[i].range = i + 1;
+ }
+ Symlink_working_set.access_group_size = Num_working_symlinks;
+ Symlink_working_set.access_group_cnt = 1;
+ Symlink_working_set.max_range = Num_working_symlinks;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "\nSymlink size=%d cnt=%d max=%d\n",
+ Symlink_working_set.access_group_size,
+ Symlink_working_set.access_group_cnt,
+ Symlink_working_set.max_range);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * Free up random number range
+ */
+ (void)init_rand_range(0);
+
+
+} /* init_fileinfo */
+
+/*
+ * allocate and initialize the directory layout of the files
+ *
+ * We can only place files in directories that can't be removed
+ */
+static void
+init_dirlayout(void)
+{
+ int i,j;
+
+ /*
+ * Initially create directories only one level deep so all directories
+ * must be in the parent directory.
+ */
+ for (i = 0; i < Num_dir_files; i++) {
+ Dirs[i].dir = &Export_dir;
+ }
+
+ /*
+ * Files must only be placed in the first Num_dirs entries leaving
+ * a set for directory create and remove.
+ */
+ j = 0;
+ for (i = 0; i < Num_io_files; i++) {
+ if (i != 0 && (i % Files_per_dir) == 0)
+ j++;
+ Io_files[i].dir = &Dirs[j];
+ }
+
+ /*
+ * All non-io and symlink files are placed in the parent directory
+ */
+ for (i = 0; i < Num_non_io_files; i++) {
+ Non_io_files[i].dir = &Export_dir;
+ }
+
+ for (i = 0; i < Num_symlink_files; i++) {
+ Symlinks[i].dir = &Export_dir;
+ }
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+ /* Initialize biod simulation mechanism if desired. */
+ if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) {
+ if (biod_init(Biod_max_outstanding_writes,
+ Biod_max_outstanding_reads) == -1) {
+ return(-1);
+ }
+ }
+
+ return(0);
+} /* init_rpc */
+
+/*
+ * Initialize the test directory 'parentdir'/testdir'dirnum'.
+ *
+ * If the directory already exists, check to see that all of the
+ * files exist and can be written. If the directory doesn't exist
+ * create it and fill it with the proper files. The caller is
+ * left with his cwd being the test directory.
+ *
+ * Each child pseudo-mount's his own test directory to get its filehandle.
+ *
+ * Files, directories, and symlinks all have the same name structure
+ * but they are strictly ordered, files first, directories next, then symlinks.
+ * While initializing after a previous run we may have to delete existing
+ * files of the wrong type and then create them later.
+ *
+ * XXX In the future it is probably wiser to have seperate namespaces for
+ * each type of file.
+ */
+static void
+init_testdir(void)
+{
+ int filenum;
+ int max_filenum;
+ int init_size;
+ int append_size;
+ int ret;
+ int non = 0;
+ int dealloc;
+ int alloc_count, dealloc_count;
+ /*
+ * Create directories first so operations that
+ * require them will have a file to work with.
+ */
+ alloc_count=dealloc_count=0;
+ for (filenum = 0; filenum < Num_dir_files; filenum++) {
+ sfs_gettime(&Cur_time);
+
+ Cur_file_ptr = &Dirs[filenum];
+ dealloc=0;
+ if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
+ {
+ alloc_count++;
+ Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
+ Cur_file_ptr->attributes2.type = NFNON;
+ Cur_file_ptr->attributes3.type = NF3NON;
+ if(Cur_file_ptr->working_set == 1)
+ dealloc=0;
+ else
+ dealloc=1;
+ }
+
+ (void) sprintf(Cur_filename, Dirspec, Cur_file_ptr->unique_num);
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: initialize %s (DIR)\n",
+ sfs_Myname, Cur_filename);
+ (void) fflush(stderr);
+ }
+
+ if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(159);
+ }
+
+ if (ret == 0) {
+ /* file exists */
+ if (fh_isdir(Cur_file_ptr) && Cur_file_ptr->initialize)
+ {
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(160);
+ }
+ }
+
+ if (!Cur_file_ptr->initialize) {
+ /* dir shouldn't exist */
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ /* make the directory */
+ if (lad_mkdir(Cur_file_ptr, Cur_filename) == -1) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(161);
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ } /* end for each directory */
+
+ /*
+ * Setup for file i/o operations.
+ * Verify that we can read and write all the files.
+ * Make sure we have the attributes && fh for all regular files.
+ * Create any missing files.
+ */
+ max_filenum = Num_io_files + Num_non_io_files;
+ alloc_count=dealloc_count=0;
+ for (filenum = 0; filenum < max_filenum; filenum++) {
+ sfs_gettime(&Cur_time);
+
+ if (filenum < Num_io_files) {
+ Cur_file_ptr = &Io_files[filenum];
+ } else {
+ Cur_file_ptr = &Non_io_files[filenum - Num_io_files];
+ non = 1;
+ }
+ (void) sprintf(Cur_filename, Filespec, Cur_file_ptr->unique_num);
+ dealloc=0;
+ if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
+ {
+ alloc_count++;
+ Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
+ Cur_file_ptr->attributes2.type = NFNON;
+ Cur_file_ptr->attributes3.type = NF3NON;
+ if(Cur_file_ptr->working_set == 1)
+ dealloc=0;
+ else
+ dealloc=1;
+ }
+
+ /*
+ * Get the size this file should be initialized to, then reset
+ * so we don't get confused.
+ */
+ init_size = Cur_file_ptr->size;
+ Cur_file_ptr->size = 0;
+
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: initialize %s (REG for %sIO)\n",
+ sfs_Myname, Cur_filename,
+ (non ? "non-": ""));
+ (void) fflush(stderr);
+ }
+
+ if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(162);
+ }
+
+ if (ret == 0) {
+ /*
+ * If file exists and it shouldn't, remove it
+ */
+ if (!Cur_file_ptr->initialize) {
+ if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(163);
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ /*
+ * file exists: make sure it is
+ * - a regular file
+ * - accessible (permissions ok)
+ * if not, remove it (if necessary) and recreate it
+ * or extend or truncate it to the standard length.
+ */
+ if (fh_isfile(Cur_file_ptr) &&
+ check_fh_access(Cur_file_ptr) == 0) {
+ goto adjust_size;
+ }
+ if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(164);
+ }
+
+ } /* end if the file exists */
+
+ /* the file doesn't exist */
+ if (!Cur_file_ptr->initialize) {
+ /* file doesn't exist and it shouldn't */
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ /* if the file doesn't exist (or was removed), create it */
+ if (lad_create(Cur_file_ptr, Cur_filename) == -1) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(165);
+ }
+
+adjust_size:
+ /* the non-i/o regular files can be left empty */
+ if (filenum >= Num_io_files) {
+ /* Truncate if it has grown */
+ if (fh_size(Cur_file_ptr) != 0) {
+ if (lad_truncate(Cur_file_ptr, 0)) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(166);
+ }
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ /* the i/o file must be prefilled, check if file too big */
+ if (fh_size(Cur_file_ptr) > init_size) {
+ /* Truncate if it has grown */
+ if (fh_size(Cur_file_ptr) != 0) {
+ if (lad_truncate(Cur_file_ptr, init_size)) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(167);
+ }
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ continue;
+ }
+
+ /* the i/o file must be prefilled, set up the write arguments. */
+ if (fh_size(Cur_file_ptr) < init_size) {
+ append_size = init_size - fh_size(Cur_file_ptr);
+
+ if (lad_write(Cur_file_ptr, fh_size(Cur_file_ptr), append_size)) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(168);
+ }
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ } /* end for each regular file */
+
+ /*
+ * Create symlinks so operations that
+ * require them will have a file to work with.
+ */
+ alloc_count=dealloc_count=0;
+ for (filenum = 0; filenum < Num_symlink_files; filenum++) {
+ char symlink_target[SFS_MAXNAMLEN];
+
+ sfs_gettime(&Cur_time);
+
+ Cur_file_ptr = &Symlinks[filenum];
+ (void) sprintf(Cur_filename, Symspec, Cur_file_ptr->unique_num);
+
+ dealloc=0;
+ if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
+ {
+ alloc_count++;
+ Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
+ Cur_file_ptr->attributes2.type = NFNON;
+ Cur_file_ptr->attributes3.type = NF3NON;
+ if(Cur_file_ptr->working_set == 1)
+ dealloc=0;
+ else
+ dealloc=1;
+ }
+ if (DEBUG_CHILD_SETUP) {
+ (void) fprintf(stderr, "%s: initialize %s (SYMLINK)\n",
+ sfs_Myname, Cur_filename);
+ (void) fflush(stderr);
+ }
+
+ if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(169);
+ }
+
+ if (ret == 0) {
+ /* file exists */
+ if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(170);
+ }
+ }
+
+ /* File doesn't exist */
+ if (Cur_file_ptr->initialize) {
+ /* make the symlink */
+ (void) sprintf(symlink_target, Filespec, filenum);
+ if (lad_symlink(Cur_file_ptr, symlink_target, Cur_filename) != 0) {
+ /* some error that I don't know what to do with, quit. */
+ (void) generic_kill(0, SIGINT);
+ exit(171);
+ }
+ }
+ if(dealloc == 1)
+ {
+ dealloc_count++;
+ free(Cur_file_ptr->fh_data);
+ Cur_file_ptr->fh_data=(sfs_fh_data *)0;
+ }
+ } /* end for each symlink */
+} /* init_testdir */
+
+/*
+ * Initialize the test results counters.
+ */
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize use count for each file */
+ for (i = 0; i < Num_io_files; i++) {
+ Io_files[i].use_cnt = 0;
+ Io_files[i].xfer_cnt = 0;
+ }
+ for (i = 0; i < Num_non_io_files; i++)
+ Non_io_files[i].use_cnt = 0;
+ for (i = 0; i < Num_dir_files; i++)
+ Dirs[i].use_cnt = 0;
+ for (i = 0; i < Num_symlink_files; i++)
+ Symlinks[i].use_cnt = 0;
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+
+
+/*
+ * ------------------------- Load Generation -------------------------
+ */
+
+/*
+ * The routines below attempt to do over-the-wire operations.
+ * Each op tries to cause one or more of a particular
+ * NFS operation to go over the wire. Each individual op routine
+ * returns how many OTW calls were made.
+ *
+ * An array of file information is kept for files existing in
+ * the test directory. File handles, attributes, names, etc
+ * are stored in this array.
+ *
+ */
+
+
+#define OP_ABORTED (-1)
+#define OP_BORROWED (-2)
+#define OP_SKIPPED (-3)
+/*
+ * Randomly perform an operation according to the req mix weightings.
+ */
+static int
+do_op(void)
+{
+ double ratio;
+ int op_count;
+ int opnum;
+ int start_opnum;
+ static int failed_ops = 0;
+ static int aborted_ops = 0;
+ static int borrowed_ops = 0;
+
+ if (Testop != -1) {
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "testop start op=%s\n", Ops[Testop].name);
+ }
+ op_count = op(Testop);
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "end op=%s\n", Ops[Testop].name);
+ }
+ return(op_count);
+ }
+
+ /* get a random number and search the Ops tables for the proper entry */
+ ratio = sfs_random() % 10000;
+ for (opnum = 0; Ops[opnum].req_pcnt <= ratio / 100.0 ; opnum++) {
+ ;
+ }
+
+ /*
+ * If test targeted a a specific number of ops,
+ * and the call would put us over the call target for this op,
+ * search Ops table sequentially for an op that hasn't
+ * reached its target yet
+ */
+ if (!Timed_run) {
+ start_opnum = opnum;
+ for (; Ops[opnum].results.good_calls >= Ops[opnum].target_calls;) {
+ opnum = (opnum + 1) % NOPS;
+ if (opnum == start_opnum)
+ break;
+ }
+ }
+
+ if (DEBUG_CHILD_RPC) {
+ (void) fprintf(stderr, "(%d,%d,%d) ",
+ Child_num, Ops[TOTAL].results.good_calls, opnum);
+ (void) fflush(stderr);
+ }
+
+ /* attempt the op */
+ op_count = op(opnum);
+
+ /* count the operations as completed or check for too many errors */
+ if (op_count > 0) {
+ Ops[opnum].req_cnt++;
+ Ops[TOTAL].req_cnt++;
+ } else if (op_count == 0) {
+ failed_ops++;
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "Child %d - %d failed %d op\n",
+ Child_num, failed_ops, opnum);
+ (void) fflush(stderr);
+ }
+ if ((failed_ops % 50) == 0) {
+ (void) fprintf(stderr, "Child %d - %d failed ops\n",
+ Child_num, failed_ops);
+ (void) fflush(stderr);
+ }
+ } else if (op_count == OP_ABORTED) {
+ aborted_ops++;
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "Child %d - %d aborted %d op\n",
+ Child_num, aborted_ops, opnum);
+ (void) fflush(stderr);
+ }
+ if ((aborted_ops % 50) == 0) {
+ (void) fprintf(stderr, "Child %d - %d aborted ops\n",
+ Child_num, aborted_ops);
+ (void) fflush(stderr);
+ }
+ } else if (op_count == OP_BORROWED) {
+ borrowed_ops++;
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "Child %d - %d borrowed %d op\n",
+ Child_num, borrowed_ops, opnum);
+ (void) fflush(stderr);
+ }
+ } else if (op_count == OP_SKIPPED) {
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "Child %d - skipped %d op\n",
+ Child_num, opnum);
+ (void) fflush(stderr);
+ }
+ }
+
+ return(op_count);
+
+} /* do_op */
+
+
+/*
+ * Because file sizes are variable in length, it is possible that
+ * a group chosen for a large transfer size may not contain a file
+ * that large. Loop calling randfh to try and find another group
+ * with a large enough file, but only up to IO_LOOP_MAX times.
+ */
+#define IO_LOOP_MAX 5
+
+/*
+ * Call the RPC operation generator for op 'opnum'.
+ * The return values of the op generator routines is the count
+ * of operations performed. This routine also returns that count.
+ * A return of 0 means no operation was attempted,
+ * OP_ABORTED (-1) means that the operation failed.
+ * OP_BORROWED (-2) means that the operation was borrowed.
+ * OP_SKIPPED (-3) means that the operation was not done on purpose.
+ */
+static int
+op(
+ int opnum)
+{
+ int op_count;
+ int trunc_count;
+ sfs_io_op_dist_type *dist; /* io size distribution */
+ int i;
+ int ratio;
+ int buf_size;
+ int frag_size;
+ int xfer_size;
+ int file_size;
+ int trunc_op;
+ uint_t append_flag = 0;
+ uint_t randfh_flags = 0;
+ char *spec;
+ int io_loop = 0;
+
+ spec = Filespec;
+
+ /* pick a file that make sense for the operation */
+ switch (opnum) {
+
+ case NULLCALL:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case GETATTR:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case SETATTR:
+ if (Setattr_borrowed != 0) {
+ Setattr_borrowed--;
+ return(OP_BORROWED);
+ }
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case ROOT:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
+ break;
+
+ case LOOKUP:
+ ratio = (int) (sfs_random() % 100);
+ if (ratio < Num_failed_lookup)
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
+ else
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case READLINK:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_symlink);
+ spec = Symspec;
+ break;
+
+ case READ:
+ /* special handling for i/o operations */
+ dist = Io_dist_ptr->read;
+
+ /* determine number of full buffers and their total size */
+ ratio = (sfs_random() % 100);
+ for (i = 0; dist[i].pcnt <= ratio; i++)
+ ;
+ buf_size = dist[i].bufs * Bytes_per_block;
+
+ /* determine size of fragment */
+ /* 1KB - (Kb_per_block - 1) KB fragment */
+ ratio = sfs_random();
+ if (Kb_per_block > 1)
+ ratio = ratio % (Kb_per_block-1);
+ else
+ ratio = 0;
+ ratio = (ratio + 1) * 1024;
+ frag_size = dist[i].frags * ratio;
+
+ xfer_size = buf_size + frag_size;
+
+ do {
+ Cur_file_ptr = randfh(opnum, xfer_size, 0, Exists,
+ Sfs_io_file);
+ } while (Cur_file_ptr == (sfs_fh_type *) -1 &&
+ io_loop++ < IO_LOOP_MAX);
+ break;
+
+ case WRCACHE:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
+ break;
+
+ case WRITE:
+ /* special handling for i/o operations */
+ dist = Io_dist_ptr->write;
+
+ /* determine number of full buffers and their total size */
+ ratio = (sfs_random() % 100);
+ for (i = 0; dist[i].pcnt <= ratio; i++)
+ ;
+ buf_size = dist[i].bufs * Bytes_per_block;
+
+ /* determine size of fragment */
+ /* 1KB - (Kb_per_block - 1) KB fragment */
+ ratio = sfs_random();
+ if (Kb_per_block > 1)
+ ratio = ratio % (Kb_per_block-1);
+ else
+ ratio = 0;
+ ratio = (ratio + 1) * 1024;
+ frag_size = dist[i].frags * ratio;
+
+ xfer_size = buf_size + frag_size;
+
+ /* decide if it should append or overwrite. */
+ ratio = (sfs_random() % 100);
+ if (ratio < Append_percent) {
+ append_flag = 1;
+ randfh_flags &= RANDFH_APPEND;
+ }
+
+ /* decide if a truncation will be needed */
+ if (append_flag &&
+ ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) {
+ randfh_flags &= RANDFH_TRUNC;
+ }
+
+ do {
+ Cur_file_ptr = randfh(opnum, xfer_size,
+ randfh_flags,
+ Exists, Sfs_io_file);
+ } while (Cur_file_ptr == (sfs_fh_type *) -1 &&
+ io_loop++ < IO_LOOP_MAX);
+ break;
+
+ case CREATE:
+ if (Create_borrowed != 0) {
+ Create_borrowed--;
+ return(OP_BORROWED);
+ }
+ if ((Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent,
+ Sfs_non_io_file)) != (sfs_fh_type *) NULL)
+ break;
+
+ /* if there are no Nonexistent files, use one that exists */
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists,
+ Sfs_non_io_file);
+ /* flag create of existing file for data dump interface */
+ dump_create_existing_file = TRUE;
+ break;
+
+ case REMOVE:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
+ break;
+
+ case RENAME:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
+ break;
+
+ case LINK:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent,
+ Sfs_non_io_file);
+ break;
+
+ case SYMLINK:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_symlink);
+ spec = Symspec;
+ break;
+
+ case MKDIR:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_dir);
+ spec = Dirspec;
+ break;
+
+ case RMDIR:
+ Cur_file_ptr = randfh(opnum, 0, 0, Empty_dir, Sfs_dir);
+ spec = Dirspec;
+ break;
+
+ case READDIR:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir);
+ spec = Dirspec;
+ break;
+
+ case FSSTAT:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case ACCESS:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
+ break;
+
+ case COMMIT:
+ return(OP_SKIPPED);
+
+ case FSINFO:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
+ break;
+
+ case MKNOD:
+ Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
+ break;
+
+ case PATHCONF:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
+ break;
+
+ case READDIRPLUS:
+ Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir);
+ spec = Dirspec;
+ break;
+
+ default:
+ (void) fprintf(stderr, "%s: invalid operation %d\n", sfs_Myname, opnum);
+ (void) generic_kill(0, SIGINT);
+ exit(172);
+ } /* switch on opnum */
+
+ if (Cur_file_ptr == (sfs_fh_type *) NULL ||
+ Cur_file_ptr == (sfs_fh_type *) -1) {
+ Ops[opnum].no_calls++;
+ return(OP_ABORTED);
+ }
+
+ (void) sprintf(Cur_filename, spec, Cur_file_ptr->unique_num);
+
+ /* Call the op routine. For io operations, maintain file set size info. */
+ switch (opnum) {
+
+ case SETATTR:
+ op_count = (*Ops[opnum].funct)(-1);
+ break;
+
+ case READ:
+ op_count = (*Ops[opnum].funct)(xfer_size);
+ if (op_count > 0)
+ Cur_file_ptr->xfer_cnt += (xfer_size + 1023) / 1024;
+ else if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: READ failed\n", sfs_Myname);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case WRITE:
+ trunc_count = 0;
+
+ /* if appending, we may need to truncate the file first */
+ if (append_flag &&
+ ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) {
+
+ /* use either SETATTR or CREATE for truncation */
+ file_size = fh_size(Cur_file_ptr);
+ trunc_op = -1; /* assume there are no ops to borrow */
+
+ if (Ops[SETATTR].mix_pcnt == 0 && Ops[CREATE].mix_pcnt == 0)
+ trunc_op = -1; /* no ops to borrow */
+
+ else if (Ops[SETATTR].mix_pcnt > 0 && Ops[CREATE].mix_pcnt > 0){
+ /* only borrow if the target hasn't been met yet */
+ if (Ops[SETATTR].results.good_calls
+ >= Ops[SETATTR].target_calls) {
+ if (Ops[CREATE].results.good_calls
+ < Ops[CREATE].target_calls) {
+ trunc_op = CREATE; /* borrow a CREATE */
+ }
+ } else if (Ops[CREATE].results.good_calls
+ >= Ops[CREATE].target_calls) {
+ trunc_op = SETATTR; /* borrow a SETATTR */
+ } else {
+ /* borrow weighted by mix percentage */
+ if ((Ops[SETATTR].mix_pcnt * Create_borrowed) >
+ (Ops[CREATE].mix_pcnt * Setattr_borrowed))
+ trunc_op = SETATTR;
+ else
+ trunc_op = CREATE;
+ }
+
+ } else if (Ops[SETATTR].results.good_calls <
+ Ops[SETATTR].target_calls) {
+ /* only borrow if the target hasn't been met yet */
+ trunc_op = SETATTR; /* borrow a SETATTR */
+
+ } else if (Ops[CREATE].results.good_calls <
+ Ops[CREATE].target_calls) {
+ /* only borrow if the target hasn't been met yet */
+ trunc_op = CREATE; /* borrow a CREATE */
+ }
+
+ /* perform the truncation and update the file set size */
+ if (trunc_op != -1) {
+ dump_truncate_op = TRUE;
+ if (trunc_op == SETATTR) {
+ trunc_count = (*Ops[SETATTR].funct)(0);
+ if (trunc_count > 0) {
+ Setattr_borrowed++;
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "%s: SETATTR TRUNCATE\n",
+ sfs_Myname);
+ (void) fflush(stderr);
+ }
+ }
+ } else if (trunc_op == CREATE) {
+ trunc_count = (*Ops[CREATE].funct)();
+ if (trunc_count > 0) {
+ Create_borrowed++;
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stderr, "%s: CREATE TRUNCATE\n",
+ sfs_Myname);
+ (void) fflush(stderr);
+ }
+ }
+ }
+
+ Cur_fss_bytes -= (file_size / 1024);
+ if (Cur_fss_bytes < Least_fss_bytes)
+ Least_fss_bytes = Cur_fss_bytes;
+ }
+ } /* end of if an append is needed */
+
+ /*
+ * do the write request
+ * specify the stable flag to always be off, it is not used
+ * with V2 servers.
+ */
+ op_count = (*Ops[opnum].funct)(xfer_size, append_flag, 0);
+ if (op_count > 0)
+ Cur_file_ptr->xfer_cnt += (xfer_size + 1023) / 1024;
+ else if (DEBUG_CHILD_ERROR) {
+ (void) fprintf(stderr, "%s: WRITE failed\n", sfs_Myname);
+ (void) fflush(stderr);
+ }
+ if (append_flag) {
+ Cur_fss_bytes += (xfer_size / 1024);
+ if (Cur_fss_bytes > Most_fss_bytes)
+ Most_fss_bytes = Cur_fss_bytes;
+ }
+ op_count += trunc_count;
+ break;
+
+ default:
+ op_count = (*Ops[opnum].funct)();
+ break;
+
+ } /* send switch on opnum */
+
+ if ((DEBUG_CHILD_ERROR) && (op_count <= 0)) {
+ (void) fprintf(stderr, "%s: OP %d failed\n", sfs_Myname, opnum);
+ (void) fflush(stderr);
+ }
+
+ return(op_count);
+
+} /* op */
+
+
+/*
+ * Return an entry into the fh array for a file of type 'file_type'
+ * with existence state 'file_state'. When 'opnum' specifies an I/O
+ * operation, the file must be atleast 'xfer_size' bytes long
+ * (except when 'append_flag' is true). If 'trunc_flag', spare the
+ * first file found that is longer than the base file size (to support
+ * the READ operation). If only one file is longer than the base file
+ * size, return the the next longest file.
+ */
+sfs_fh_type *
+randfh(
+ int opnum,
+ int xfer_size,
+ uint_t flags,
+ sfs_state_type file_state,
+ sfs_file_type file_type)
+{
+ sfs_fh_type * files; /* file array */
+ int fh; /* index into file array */
+ int found_fh = -1; /* index into file array */
+ uint_t append_flag = flags & RANDFH_APPEND;
+ uint_t trunc_flag = flags & RANDFH_TRUNC;
+
+ sfs_work_set_type * work_set; /* work_set array */
+ int start_file; /* index into work_set array */
+ int file; /* index into work_set array */
+
+ int nworkfiles; /* # files in work_set */
+ int group_cnt; /* # file groups in work_set */
+ int group_size; /* size of each group in work_set */
+ int group; /* group index with work_set */
+ int offset; /* file index within group */
+
+ int value; /* distribution function value */
+ int previous; /* binary search for value */
+ int low; /* binary search for value */
+ int high; /* binary search for value */
+
+ int found_file = 0; /* count */
+ int best_delta = 0;
+ static int op_num = 0;
+ long rand_int;
+ int max_range;
+
+ op_num++;
+
+ /*
+ * if the more than one type of file will do, choose one.
+ * Note: this code assumes specific values and order for
+ * the entries in sfs_file_enum_type.
+ */
+ switch (file_type) {
+
+ case Sfs_regular:
+ file_type = (int) (sfs_random() % 2);
+ break;
+
+ case Sfs_non_dir:
+ file_type = (int) (sfs_random() % 3);
+ break;
+
+ case Sfs_any_file:
+ file_type = (int) (sfs_random() % 4);
+ break;
+
+ default:
+ break;
+
+ } /* end switch on file type */
+
+ /* get the file type arrays */
+ switch (file_type) {
+
+ case Sfs_io_file:
+ files = Io_files;
+ work_set = &Io_working_set;
+ nworkfiles = Num_working_io_files;
+ break;
+
+ case Sfs_non_io_file:
+ files = Non_io_files;
+ work_set = &Non_io_working_set;
+ nworkfiles = Num_working_non_io_files;
+ break;
+
+ case Sfs_symlink:
+ files = Symlinks;
+ work_set = &Symlink_working_set;
+ nworkfiles = Num_working_symlinks;
+ break;
+
+ case Sfs_dir:
+ files = Dirs;
+ work_set = &Dir_working_set;
+ nworkfiles = Num_working_dirs;
+ break;
+
+ default:
+ (void) fprintf(stderr, "%s: invalid file type\n", sfs_Myname);
+ (void) kill(0, SIGINT);
+ exit(174);
+ } /* end switch on file type */
+
+ /*
+ * Pick the access group.
+ *
+ * Each access group consists of those files in the working set
+ * (numbered according to the file's index in the array) that
+ * have the same value modulo the number of groups. For example,
+ * a working set of 13 files with 3 groups is organized as
+ * group files
+ * ----- -----------------
+ * 0 0, 3, 6, 9, 12 ie, == 0 mod 3
+ * 1 1, 4, 7, 10 ie, == 1 mod 3
+ * 2 2, 5, 8, 11 ie, == 2 mod 3
+ *
+ * Generate a random number mod the maximum range value of the working set.
+ * and then binary search the first group_cnt entries in the working set
+ * to find the group whose range contains the random number.
+ * (this implements the file access distribution function)
+ */
+ max_range = work_set->max_range;
+ rand_int = (long) sfs_random();
+
+ while ((rand_int / max_range) >= (_M_MODULUS / max_range)) {
+ /*
+ * for large values of max_range, modulo doesn't provide a uniform
+ * distribution unless we exclude these values ...
+ */
+ rand_int = (long) sfs_random();
+ }
+ value = rand_int % max_range;
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "randfh: size=%d cnt=%d max=%d val=%3d\n",
+ work_set->access_group_size,
+ work_set->access_group_cnt,
+ work_set->max_range, value);
+ (void) fflush(stderr);
+ }
+
+ previous = -1;
+ for (low = 0, high = work_set->access_group_cnt-1, group = (low + high)/2;;
+ previous = group, group = (low + high)/2) {
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr,
+ "PICK GROUP low=%d hi=%d grp=%d range=%d val=%d\n",
+ low, high, group, work_set->entries[group].range,
+ value);
+ (void) fflush(stderr);
+ }
+
+ if (previous == group)
+ break;
+ if (work_set->entries[group].range == value)
+ break;
+ if (work_set->entries[group].range > value) {
+ if (group == 0)
+ break;
+ if (work_set->entries[group-1].range < value)
+ break;
+ else
+ high = group - 1;
+ } else if (work_set->entries[group].range < value) {
+ if (work_set->entries[group+1].range > value) {
+ group++;
+ break;
+ } else
+ low = group + 1;
+ }
+ }
+
+ /*
+ * Pick a file within the group to operate on.
+ * Since (working_set_size / group_size) may have a remainder,
+ * groups may have either group_size or (group_size+1) files.
+ */
+ group_cnt = work_set->access_group_cnt;
+ group_size = work_set->access_group_size;
+ if (group < (nworkfiles - ((nworkfiles / group_cnt) * group_cnt)))
+ group_size += 1;
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "Selected group = %d\n", group);
+ (void) fflush(stderr);
+ }
+ /*
+ * Beginning with a random starting point in the group,
+ * search for a file that is eligible for this operation.
+ * index is an index into the files in the group.
+ * file and start_file are indices into the working set array.
+ */
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "group_size = %d\n", group_size);
+ (void) fflush(stderr);
+ }
+
+ offset = (int) (sfs_random() % group_size);
+ start_file = group + (offset * group_cnt);
+ file = start_file;
+ do {
+ int f_size;
+ int delta;
+
+ fh = work_set->entries[file].index;
+
+ if (DEBUG_CHILD_OPS) {
+ (void) fprintf(stderr, "PICK FILE op= %d file=%d fh=%d\n",
+ opnum, file, fh);
+ (void) fprintf(stderr, "fh_state = %d file_state= %d\n",
+ files[fh].state, file_state);
+ (void) fflush(stderr);
+ }
+
+ /* look for a file that has the right state attribute */
+ if (files[fh].state == file_state) {
+ f_size = fh_size(&files[fh]);
+
+ /*
+ * for read and write ops and setattr truncates,
+ * the file must be large enough to do the xfer or truncate.
+ */
+ if ((opnum == READ) || (opnum == WRITE && !append_flag) ||
+ trunc_flag) {
+
+ /*
+ * If the request is a read and the transfer size is
+ * less than or equal to be block size, grab the first
+ * file that is less than or equal in size. Should never
+ * see a transfer size less than block size as it will
+ * be rounded up for the request. This allows small files
+ * to be read.
+ */
+ if (opnum == READ && xfer_size <= Bytes_per_block) {
+ if (f_size <= Bytes_per_block) {
+ found_fh = fh;
+ break;
+ }
+ }
+/* #define FIRST_FIT */
+#define BEST_FIT
+#ifdef FIRST_FIT
+ if (f_size >= xfer_size) {
+ found_fh = fh;
+ break;
+ }
+#endif
+#ifdef BEST_FIT
+ if (DEBUG_CHILD_FIT) {
+ (void) fprintf(stderr,
+"%s: %8d: xfer_size %d f_size %d best_delta %d found %d\n",
+sfs_Myname, op_num, xfer_size, f_size, best_delta, found_file);
+ (void) fflush(stderr);
+ }
+
+ /*
+ * If we find an good enough match we should use it.
+ * Define good enough to be xfer_size <= X < xfer_size + 8K
+ * If not we continue to search for the best fit within
+ * a fixed distance 8.
+ */
+ if (f_size >= xfer_size) {
+ if (f_size < (xfer_size + 8 * 1024)) {
+ found_fh = fh;
+ break;
+ }
+
+ found_file++;
+ delta = f_size - xfer_size;
+
+ if (found_fh == -1) {
+ best_delta = delta;
+ found_fh = fh;
+ /* break; Removed as per Robinson */
+ }
+
+ if (delta < best_delta) {
+ found_fh = fh;
+ best_delta = delta;
+ }
+
+ if (found_file >= 8) {
+ break;
+ }
+ }
+#endif
+ } else {
+ /* for non-i/o ops, only requirement is proper file state */
+ found_fh = fh;
+ break;
+ }
+ }
+ offset = (offset + 1) % group_size;
+ file = group + (offset * group_cnt);
+ } while (file != start_file);
+
+ if (found_fh == -1) {
+ /* didn't find a file for this operation */
+ if (DEBUG_CHILD_FIT) {
+ if (opnum == READ || (opnum == WRITE && !append_flag) ||
+ opnum == SETATTR) {
+ (void) fprintf(stderr, "%s: no file for %d byte %d op\n",
+ sfs_Myname, xfer_size, opnum);
+ } else {
+ (void) fprintf(stderr, "%s: no file for %d op\n",
+ sfs_Myname, opnum);
+ }
+ (void) fflush(stderr);
+ return((sfs_fh_type *) -1);
+ }
+ return((sfs_fh_type *) NULL);
+ }
+ /* found it */
+ files[found_fh].use_cnt++;
+ return(&files[found_fh]);
+} /* randfh */
+
+/*
+ * ------------------------ Miscellaneous Subroutines -----------------------
+ */
+
+/*
+ * check to make sure that we have both read and write permissions
+ * for this file or directory given in 'statb'.
+ * return: 0 == ok, -1 == bad
+ */
+int
+check_access(
+ struct stat *statb)
+{
+ /* check user */
+ if (statb->st_uid == Real_uid) {
+ if ((statb->st_mode & 0400) && (statb->st_mode & 0200)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+ }
+
+ /* check group */
+ if (statb->st_gid == Cur_gid) {
+ if ((statb->st_mode & 040) && (statb->st_mode & 020)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+ }
+
+ /* check other */
+ if ((statb->st_mode & 04) && (statb->st_mode & 02)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+
+} /* check_access */
+
+/*
+ * check to make sure that we have both read and write permissions
+ * for this file or directory given in the file attributes.
+ * return: 0 == ok, -1 == bad
+ */
+int
+check_fh_access(sfs_fh_type *file_ptr)
+{
+ /* check user */
+ if (fh_uid(file_ptr) == Real_uid) {
+ if ((fh_mode(file_ptr) & 0400) && (fh_mode(file_ptr) & 0200)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+ }
+
+ /* check group */
+ if (fh_gid(file_ptr) == Cur_gid) {
+ if ((fh_mode(file_ptr) & 040) && (fh_mode(file_ptr) & 020)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+ }
+
+ /* check other */
+ if ((fh_mode(file_ptr) & 04) && (fh_mode(file_ptr) & 02)) {
+ return(0);
+ } else {
+ return(-1);
+ }
+}
+
+static int last_bad_calls = 0;
+
+/*
+ * Adjust the sleep time per call based on a number of global variables,
+ */
+static void
+check_call_rate()
+{
+ int call_target_per_period; /* target calls for each period */
+ int req_target_per_period; /* target reqs for each period */
+ int call_target_this_test; /* target calls for test so far */
+ int req_target_this_test; /* target reqs for test so far */
+ int msec_this_period; /* actual length of this period */
+ int msec_this_test; /* actual length of test so far */
+ uint_t current_msec; /* current time in msecs */
+ int old_target_sleep_mspc;
+ struct ladtime elapsed_time; /* Current_time - Start_time */
+
+ int reqs_needed_next_period;/* req target for the next period */
+ float mspc; /* target msec per call, with/sleep */
+ float work_mspc; /* actual msec worked / call */
+
+
+ if (Child_num == -1)
+ /* I'm the parent, ignore the signal */
+ return;
+
+ /* update the test so far totals */
+ Calls_this_test += Calls_this_period;
+ Reqs_this_test += Reqs_this_period;
+ Sleep_msec_this_test += Sleep_msec_this_period;
+
+ /* compute per period targets */
+ call_target_per_period = (int) (Child_call_load *
+ ((float) Msec_per_period / (float) 1000));
+ req_target_per_period = (int) (Child_req_load *
+ ((float) Msec_per_period / (float) 1000));
+
+ /*
+ * The child() routine retrieved the Cur_time when deciding to call us.
+ * Use Cur_time to compute the elapsed time since the last checkpoint
+ * and the current checkpoint time (ie, elapsed time since test began)
+ */
+ /* sfs_gettime(&Cur_time); */
+ elapsed_time.sec = Cur_time.sec;
+ elapsed_time.usec = Cur_time.usec;
+ SUBTIME(elapsed_time, Starttime);
+
+ msec_this_test = (elapsed_time.sec * 1000) + (elapsed_time.usec / 1000);
+ current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000);
+ msec_this_period = current_msec - Previous_chkpnt_msec;
+
+ if (msec_this_test < Sleep_msec_this_test) {
+ if (DEBUG_CHILD_XPOINT) {
+ (void) fprintf(stderr,
+ "Accum. sleep time %d is msecs ahead of wall clock\n",
+ Sleep_msec_this_test - msec_this_test);
+ (void) fflush(stderr);
+ }
+ Sleep_msec_this_test = msec_this_test;
+ }
+
+ /* compute targets for test so far */
+ call_target_this_test = (int) ((Child_call_load * (float) msec_this_test)
+ / (float) 1000);
+ req_target_this_test = (int) ((Child_req_load * (float) msec_this_test)
+ / (float) 1000);
+
+ /* save the old sleep rate */
+ old_target_sleep_mspc = Target_sleep_mspc;
+
+ /* Compute how long each request has taken on average. */
+ if (Reqs_this_test != 0)
+ work_mspc = ((float) (msec_this_test - Sleep_msec_this_test))
+ / (float) Reqs_this_test;
+ else
+ work_mspc = (1000.0 / (float) Child_req_load) / 2.0;
+
+ /*
+ * Compute the number of reqs needed in the next period
+ * in order to just meet the reqstarget for the test when that period ends.
+ * (Try to make up the entire shortage or overage in the next period.)
+ * Beware that we might not need to make any reqs next period.
+ */
+ reqs_needed_next_period = (req_target_this_test - Reqs_this_test)
+ + req_target_per_period;
+
+ if (reqs_needed_next_period <= 0) {
+ /* if no reqs are needed, set the sleep time to the whole period */
+ mspc = 0.0;
+ Target_sleep_mspc = Msec_per_period;
+ } else {
+ /* decide how much time is available for each request */
+ mspc = (float) (Msec_per_period) / (float) (reqs_needed_next_period);
+ Target_sleep_mspc = (int) (mspc - work_mspc);
+ }
+
+ /* Don't increase the target_sleep_mspc by much more than a factor of two,
+ because doing so can lead to violent oscillations. */
+ if (Target_sleep_mspc > 2*(old_target_sleep_mspc + 5)) {
+ Target_sleep_mspc = 2*(old_target_sleep_mspc + 5);
+ }
+
+ if (Target_sleep_mspc >= Msec_per_period) {
+ Target_sleep_mspc = Msec_per_period;
+ if (DEBUG_CHILD_XPOINT) {
+ (void) fprintf(stderr,
+ "Child %d: 0 call, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n",
+ Child_num, reqs_needed_next_period, mspc, work_mspc,
+ msec_this_test, Sleep_msec_this_test, Reqs_this_test);
+ (void) fflush(stderr);
+ }
+ if (Measurement_in_progress) {
+ (void) fprintf(stderr,
+ "Child %d: 0 calls during measurement interval\n",Child_num);
+ (void) fprintf(stderr,
+ "Child %d: probably unstable, try more processes.\n",Child_num);
+ (void) generic_kill(0, SIGINT);
+ (void) fflush(stderr);
+ exit(188);
+ }
+ }
+ if (Target_sleep_mspc <= 0) {
+ Target_sleep_mspc = 0;
+ if (DEBUG_CHILD_XPOINT) {
+ (void) fprintf(stderr,
+ "Child %d: 0 slp, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n",
+ Child_num, reqs_needed_next_period, mspc, work_mspc,
+ msec_this_test, Sleep_msec_this_test, Reqs_this_test);
+ (void) fflush(stderr);
+ }
+ }
+
+ if (DEBUG_CHILD_XPOINT) {
+ (void) fprintf(stderr, "Child %d\n%s", Child_num,
+ " msec_prd calls_prd reqs_prd calls_tot req_tot mspc_req\n");
+ (void) fprintf(stderr, "target: %8d %9d %8d %9d %8d %6.2f\n",
+ Msec_per_period,
+ call_target_per_period, req_target_per_period,
+ call_target_this_test, req_target_this_test,
+ 1000.0 / (float) req_target_per_period);
+ (void) fprintf(stderr, "actual: %8d %9d %8d %9d %8d ->%6.2f\n",
+ msec_this_period,
+ Calls_this_period, Reqs_this_period,
+ Calls_this_test, Reqs_this_test,
+ mspc);
+ (void) fprintf(stderr,
+ " old_sleep_mspc %5d new_sleep_mspc %5d\n\n",
+ old_target_sleep_mspc, Target_sleep_mspc);
+ }
+
+ /*
+ * check for too many failed RPC calls
+ * and print a warning if there are too many.
+ */
+ if (((Ops[TOTAL].results.bad_calls - last_bad_calls) > 100) ||
+ ((Ops[TOTAL].results.good_calls > 300) &&
+ ((Ops[TOTAL].results.bad_calls - last_bad_calls) >
+ Ops[TOTAL].results.good_calls/50))) {
+ (void) fprintf(stderr,
+ "%s: too many failed RPC calls - %d good %d bad\n",
+ sfs_Myname, Ops[TOTAL].results.good_calls,
+ Ops[TOTAL].results.bad_calls);
+ last_bad_calls = Ops[TOTAL].results.bad_calls;
+ }
+
+ /* reset the period counters */
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Previous_chkpnt_msec = current_msec;
+
+} /* check_call_rate */
+
+/* sfs_c_chd.c */
--- /dev/null
+/* Try to change thread scheduling and uses three threads */
+#ifndef lint
+static char sfs_c_chdSid[] = "@(#)sfs_c_chd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * -------------------------- sfs_c_chd.c -------------------------
+ *
+ * The sfs child. Routines to initialize child parameters,
+ * initialize test directories, and generate load.
+ *
+ *.Exported_Routines
+ * void child(int, float, int, char *);
+ * void init_fileinfo(void);
+ * void init_counters(void);
+ * sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
+ * sfs_file_type);
+ * int check_access(struct *stat)
+ * int check_fh_access();
+ *
+ *.Local_Routines
+ * void check_call_rate(void);
+ * void init_targets(void);
+ * void init_dirlayout(void);
+ * void init_rpc(void);
+ * void init_testdir(void);
+ * int do_op(void);
+ * int op(int);
+ *
+ *.Revision_History
+ * 21-Aug-92 Wittle randfh() uses working set files array.
+ * init_fileinfo() sets up working set.
+ * 02-Jul-92 Teelucksingh Target file size now based on peak load
+ * instead of BTDT.
+ * 04-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+#include "rfs_c_def.h"
+#include "generic_hash.h"
+#include "nfsd_nfsfh_cust.h"
+
+extern struct hostent *Server_hostent;
+
+#define PROB_SCALE 1000L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+
+#define _GROUP_DIVISOR 500
+#define _FILES_PER_GROUP 4
+#define _MIN_GROUPS 12
+#define _WORKING_SET_AT_25_OPS_PER_SEC 975
+
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
+extern enum clnt_stat proc_header(CLIENT *cl, xdrproc_t xdr_results, void *results_ptr);
+extern int biod_poll_wait(CLIENT *, uint32_t);
+extern enum clnt_stat get_areply_udp (CLIENT * cl, uint32_t *xid, struct timeval *timeout);
+extern char * parse_name (char * t, char * buf);
+
+/* forward definitions for local functions */
+static int init_rpc(void);
+
+/* RFS: forward definitions for local functions */
+void init_ops(void);
+static void init_signal();
+extern void init_file_system (void);
+extern void init_dep_tab (void);
+static int read_trace(void);
+static void read_fh_map();
+static void init_play (char * mount_point);
+static void trace_play(void);
+void print_result(void);
+static int get_nextop(void);
+static int check_timeout(void);
+static struct biod_req * get_biod_req(int dep_tab_index);
+static int lookup_biod_req (int xid);
+static void init_time_offset(void);
+void adjust_play_window (int flag, int * poll_timeout_arg);
+static int poll_and_get_reply (int usecs);
+static char * nfs3_strerror(int status);
+static void check_clock(void);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void usage(void);
+void init_dep_tab_entry (int dep_index);
+extern inline fh_map_t * lookup_fh (char * trace_fh );
+static void finish_request (int biod_index, int dep_index, int status, int dep_flag);
+static inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line);
+static inline char * find_lead_trace_fh(int proc, char * line);
+static inline char * find_reply_trace_fh (char * line);
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+static uint_t Calls_this_period; /* calls made during the current run period */
+static uint_t Calls_this_test; /* calls made during the test so far */
+static uint_t Reqs_this_period; /* reqs made during the current run period */
+static uint_t Reqs_this_test; /* reqs made during the test so far */
+static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
+static uint_t Sleep_msec_this_period;
+static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
+static int Target_sleep_mspc; /* targeted sleep time per call */
+
+static char io_buf[BUFSIZ]; /* io buffer for print out messages */
+
+char * sfs_Myname;
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+int Validate = 0; /* fake variable */
+int Child_num = 0; /* fake: child index */
+int Tcp = 0; /* We implement UDP first */
+int Client_num = 1; /* fake: number of client */
+uid_t Real_uid;
+gid_t Cur_gid;
+uid_t Cur_uid;
+/* as long as the inital value is different, then it's OK */
+int recv_num = 0;
+int timeout_num = 0;
+int send_num = 0;
+int exit_flag = 0;
+int async_rpc_sem;
+int no_progress_flag = 0;
+int num_out_reqs_statistics[MAX_OUTSTANDING_REQ+1];
+int num_out_reqs_statistics_at_timeout[MAX_OUTSTANDING_REQ+1];
+
+/*
+ * ------------------------- SFS Child -------------------------
+ */
+
+static int nfs2proc_to_rfsproc[18] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17};
+static int nfs3proc_to_rfsproc[NFS3_PROCEDURE_COUNT] = {0, 1, 2, 4, 18, 5, 6, 8, 9, 14,
+ 13, 21, 10, 15, 11, 12, 16, 23, 17, 20,
+ 22, 19};
+void print_usage(int pos, int argc, char ** argv)
+{
+ int i;
+ printf("sfs3 hostname:mount_dir trace_file|stdin fh_map_file play_scale warmup_time(in seconds) \n");
+ printf("sfs3 -pair_trace trace_file\n");
+ printf("sfs3 -pair_write trace_file\n");
+ printf("sfs3 -help\n");
+ printf ("pos %d argc %d", pos, argc);
+ for (i=0; i<argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf ("\n");
+ exit;
+}
+
+void print_cyclic_buffers ()
+{
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+}
+
+void add_to_dep_tab(int i)
+{
+ char * trace_fh;
+ fh_map_t * fh_map_entry;
+ dep_tab_t * ent;
+
+ trace_fh = strstr (memory_trace[i].line, "fh");
+ if (!trace_fh)
+ printf ("memory_trace[%d].line %s\n", i, memory_trace[i].line);
+ RFS_ASSERT (trace_fh);
+ trace_fh += 3;
+ fh_map_entry = lookup_fh (trace_fh);
+ if (fh_map_entry && (fh_map_entry->flag==FH_MAP_FLAG_DISCARD) ) {
+ req_num_with_discard_fh ++;
+ return;
+ }
+ if (fh_map_entry)
+ req_num_with_init_fh ++;
+ else
+ req_num_with_new_fh ++;
+
+ RFS_ASSERT (!CYCLIC_FULL(dep_tab_index));
+ ent = &(dep_tab[dep_tab_index.head]);
+
+ ent->disk_index = memory_trace[i].disk_index;
+ ent->memory_index = i;
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ ent->trace_status = memory_trace[i].trace_status;
+ ent->reply_trace_fh = memory_trace[i].reply_trace_fh;
+#endif
+ ent->line = memory_trace[i].line;
+ init_dep_tab_entry(dep_tab_index.head);
+
+ if (rfs_debug && (i%100000)==0)
+ printf ("dep_tab[%d].disk_index %d = memory_trace[%d].disk_index %d\n", dep_tab_index.head, ent->disk_index, i, memory_trace[i].disk_index);
+ CYCLIC_MOVE_HEAD(memory_trace_index);
+ CYCLIC_MOVE_HEAD(dep_tab_index);
+}
+
+void init_profile_variables ()
+{
+ init_profile ("total_profile", &total_profile);
+ init_profile ("execute_next_request_profile", &execute_next_request_profile);
+ init_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ init_profile ("invalid_get_nextop_profile",&invalid_get_nextop_profile);
+ init_profile ("prepare_argument_profile", &prepare_argument_profile);
+ init_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ init_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ init_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ init_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ init_profile ("decode_reply_profile", &decode_reply_profile);
+ init_profile ("check_reply_profile", &check_reply_profile);
+ init_profile ("add_create_object_profile", &add_create_object_profile);
+ init_profile ("check_timeout_profile", &check_timeout_profile);
+ init_profile ("adjust_play_window_profile",&adjust_play_window_profile);
+ init_profile ("fgets_profile",&fgets_profile);
+ init_profile ("read_line_profile",&read_line_profile);
+ init_profile ("read_trace_profile",&read_trace_profile);
+}
+
+static char trace_file[256]="anon-lair62-011130-1200.txt";
+int print_memory_usage()
+{
+ printf("size of fh_map_t %d size of fh_map %d\n", sizeof(fh_map_t), sizeof(fh_map));
+ printf("sizeof dep_tab_t %d sizeof dep_tab %d\n", sizeof(dep_tab_t), sizeof(dep_tab));
+ printf("size of memory_trace_ent_t %d sizeof memory_trace %d\n", sizeof(memory_trace_ent_t), sizeof(memory_trace));
+ printf("size of CREATE3args %d\n", sizeof( CREATE3args));
+ printf("size of MKDIR3args %d\n", sizeof( MKDIR3args));
+ printf("size of READ3args %d\n", sizeof( READ3args));
+ printf("size of WRITE3args %d\n", sizeof( WRITE3args));
+ printf("size of RENAME3args %d\n", sizeof( RENAME3args));
+ printf("size of GETATTR3args %d\n", sizeof( GETATTR3args));
+ printf("size of SETATTR3args %d\n", sizeof( SETATTR3args));
+ printf("size of LINK3args %d\n", sizeof( LINK3args));
+ printf("size of SYMLINK3args %d\n", sizeof( SYMLINK3args));
+ printf("size of MKNOD3args %d\n", sizeof( MKNOD3args));
+ printf("size of RMDIR3args %d\n", sizeof( RMDIR3args));
+ printf("size of REMOVE3args %d\n", sizeof( REMOVE3args));
+ printf("size of LOOKUP3args %d\n", sizeof( LOOKUP3args));
+ printf("size of READDIR3args %d\n", sizeof( READDIR3args));
+ printf("size of READDIRPLUS3args %d\n", sizeof( READDIRPLUS3args));
+ printf("size of FSSTAT3args %d\n", sizeof( FSSTAT3args));
+ printf("size of FSINFO3args %d\n", sizeof( FSINFO3args));
+ printf("size of COMMIT3args %d\n", sizeof( COMMIT3args));
+ printf("size of ACCESS3args %d\n", sizeof( ACCESS3args));
+ printf("size of READLINK3args %d\n", sizeof( READLINK3args));
+
+
+}
+
+void recv_thread()
+{
+ int last_print_time = -1;
+ int busy_flag;
+
+ while (send_num ==0) {
+ usleep(1000);
+ }
+
+ //while (!CYCLIC_EMPTY(dep_tab_index)) {
+ while (!exit_flag) {
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ static int recv_num_before_10_seconds = 0;
+ last_print_time = total_profile.in.tv_sec;
+ fprintf (stdout, "<<<<< recv_thread recv_num %d time %d num_out_reqs %d \n", recv_num, total_profile.in.tv_sec, num_out_reqs);
+ if (recv_num == recv_num_before_10_seconds) {
+ no_progress_flag = 1;
+ RFS_ASSERT (0);
+ } else
+ recv_num_before_10_seconds = recv_num;
+ }
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+
+ pthread_yield();
+ //usleep(1000);
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1) {
+ busy_flag = BUSY;
+ }
+#endif
+ end_profile (&receive_next_reply_profile);
+ }
+ printf ("<<<< recv thread EXIT\n");
+ exit_flag = 2;
+}
+
+int io_thread ()
+{
+/* number of seconds the I/O thread pauses after each time trying to read the requests */
+#define IO_THREAD_PAUSE_TIME 1
+
+ int i;
+ int j = 0;
+
+ disk_io_status = read_trace ();
+ while (disk_io_status == TRACE_BUF_FULL) {
+
+ usleep (10000);
+ if ((j++%1000)==0) {
+ printf("&&&&&&&&&& io thread, sleep %d seconds\n", j/100);
+ }
+
+ disk_io_status = read_trace ();
+ //printf ("io_thread, after read_trace, disk_index %d\n", disk_index);
+
+#ifdef SEQUEN_READ
+ for (i=0; i<SEQUEN_READ_NUM; i++) {
+ add_to_dep_tab(CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size));
+ }
+#endif
+ //printf ("***************** IO THREAD SLEEP 1 s\n");
+ //print_cyclic_buffers();
+ //pthread_yield();
+ }
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+ printf("io_thread EXIT\n");
+}
+
+int execute_thread()
+{
+ int i;
+ struct timeval playstart_time, playend_time;
+
+ sleep (10); /* first let io_thread to run for a while */
+ printf ("trace_play\n");
+
+ gettimeofday(&playstart_time, NULL);
+
+ init_time_offset();
+ trace_play ();
+
+ gettimeofday(&playend_time, NULL);
+
+ {
+ int usec, sec;
+ sec = playend_time.tv_sec - playstart_time.tv_sec;
+ usec = sec * 1000000 + (playend_time.tv_usec - playstart_time.tv_usec);
+ sec = usec / 1000000;
+ usec = usec % 1000000;
+ printf("Total play time: %d sec %d usec\n", sec, usec);
+ if (sec > WARMUP_TIME)
+ print_result();
+ else
+ printf ("the trace play time %d is less than WARMUP_TIME %d, no statistical results\n");
+ }
+#ifdef RECV_THREAD
+ exit_flag = 1;
+ while (exit_flag == 1) {
+ usleep (1000);
+ }
+#endif
+
+ clnt_destroy(NFS_client);
+ biod_term();
+}
+
+int init_thread ()
+{
+ pthread_attr_t attr;
+ int arg;
+ int ret = 0;
+ pthread_t io_thread_var;
+#ifdef RECV_THREAD
+ pthread_t recv_thread_var;
+#endif
+ pthread_t execute_thread_var;
+
+ ret = pthread_attr_init (&attr);
+ if (ret!=0) {
+ perror("pthread_attr_init attr");
+ return ret;
+ }
+#ifdef IO_THREAD
+ ret = pthread_create (&(io_thread_var), &attr, &io_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+#endif
+
+#ifdef RECV_THREAD
+ ret = pthread_create (&(recv_thread_var), &attr, &recv_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("recv_pthread_attr_init");
+ return ret;
+ }
+#endif
+
+/*
+ ret = pthread_create (&(execute_thread_var), &attr, &execute_thread, (void *)&arg );
+ if (ret!=0) {
+ perror("io_pthread_attr_init");
+ return ret;
+ }
+*/
+}
+
+void init_buffers()
+{
+ CYCLIC_INIT("memory_trace_index",memory_trace_index,MAX_MEMORY_TRACE_LINES);
+ CYCLIC_INIT("dep_tab_index ",dep_tab_index,DEP_TAB_SIZE);
+ CYCLIC_INIT("dep_window_index ",dep_window_index,DEP_TAB_SIZE);
+}
+
+int main(int argc, char ** argv)
+{
+ extern char * optarg;
+ int i;
+ struct timeval in={1000000, 100};
+
+ init_profile_variables();
+ if ((argc==1) || (argc==2 && !strcmp(argv[1],"-help"))) {
+ print_usage(0, argc, argv);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_write")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_write(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-pair_trace")) {
+ if (argc==3)
+ strcpy(trace_file, argv[2]);
+ else
+ strcpy(trace_file, "stdin");
+ pair_trace(trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_aging")) {
+ if (argc!=3) {
+ print_usage(3, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ check_aging (trace_file);
+ exit(0);
+ }
+ if (!strcmp(argv[1], "-check_statistics")) {
+ if (argc!=3) {
+ print_usage(1, argc, argv);
+ exit(0);
+ }
+ strcpy(trace_file, argv[2]);
+ memset(fh_htable, 0, sizeof (fh_htable));
+ check_statistics (trace_file);
+ exit(0);
+ }
+
+ if (argc!=6) {
+ print_usage(2, argc, argv);
+ exit(0);
+ }
+
+ PLAY_SCALE = atoi (argv[4]);
+ RFS_ASSERT (PLAY_SCALE >=1 && PLAY_SCALE <=10000);
+
+ WARMUP_TIME = atoi (argv[5]);
+ RFS_ASSERT (WARMUP_TIME >=0 && WARMUP_TIME <=1000);
+
+ print_memory_usage();
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+ /*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+ //Real_uid = 513;
+ //Cur_gid = 513;
+
+ Nfs_timers = Nfs_udp_timers;
+
+ init_semaphores();
+ init_file_system ();
+ init_signal();
+ init_play (argv[1]);
+ //init_play ("capella:/p5/RFSFS");
+ init_fh_map();
+ read_fh_map (argv[3]);
+ //read_fh_map ("fh-path-map-play");
+ strcpy(trace_file, argv[2]);
+
+/* If ordered by TIMESTAMP,
+ * memory_trace_index.tail <= dep_tab_index.tail < dep_window_max <=
+ * dep_tab_index.head <= memory_trace_index.head
+ */
+
+ init_global_variables();
+ init_buffers();
+ init_thread();
+ pthread_yield();
+ execute_thread();
+}
+
+int init_global_variables()
+{
+ memset (num_out_reqs_statistics, 0, sizeof(num_out_reqs_statistics));
+ memset (num_out_reqs_statistics_at_timeout, 0, sizeof(num_out_reqs_statistics_at_timeout));
+}
+
+int init_semaphores()
+{
+ async_rpc_sem = dest_and_init_sem (ASYNC_RPC_SEM_KEY, 1, "async_rpc_sem");
+}
+
+void init_ops (void)
+{
+ Ops = nfsv3_Ops;
+ nfs_version = NFS_V3;
+}
+
+/* Set up the signal handlers for all signals */
+void init_signal()
+{
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+#else
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ // RFS (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+#endif
+}
+
+void
+init_play (
+ char * mount_point) /* Mount point for remote FS */
+{
+ char namebuf[NFS_MAXNAMLEN] = "trace_play"; /* unique name for this program */
+ CLIENT * mount_client_ptr; /* Mount client handle */
+
+ if (!rfs_debug);
+ (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
+
+ sfs_Myname = namebuf;
+
+ /*
+ * May require root priv to perform bindresvport operation
+ */
+ mount_client_ptr = lad_getmnt_hand(mount_point);
+ if (mount_client_ptr == NULL) {
+ exit(145);
+ }
+
+ /*
+ * should be all done doing priv port stuff
+ */
+
+ if (init_rpc() == -1) {
+ (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
+ (void) generic_kill(0, SIGINT);
+ exit(146);
+ }
+
+
+ /*
+ * finish all priv bindresvport calls
+ * reset uid
+ */
+ if (setuid(Real_uid) != (uid_t)0) {
+ (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
+ "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ init_mount_point(0, mount_point, mount_client_ptr);
+
+
+ /*
+ * Cleanup client handle for mount point
+ */
+ clnt_destroy(mount_client_ptr);
+
+ init_counters();
+}
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+void inline format_line (char * line_before, char * line)
+{
+ char * pv = line_before;
+ char * pl = line;
+ char * p;
+ int i;
+
+ //printf("format_line before %s\n", line_before);
+ p = strstr (pv, "fh");
+ while (p!=NULL) {
+ while (pv<=p)
+ *pl++ = *pv++;
+ *pl++ = *pv++;
+ if (*pv==2) {
+ *pl++ = *pv++;
+ }
+ *pl++ = *pv++;
+ i = 0;
+ while (*pv !=' ') {
+ RFS_ASSERT ((*pv >='0' && *pv <='9') || (*pv >='a' && *pv<='f'));
+ *pl++ = *pv++;
+ i++;
+ }
+ RFS_ASSERT ((i==48) || (i==40) || (i==24));
+ while (i<48) {
+ *pl++ = '0';
+ i++;
+ }
+ p = strstr (pv, "fh");
+ }
+ while ((*pv)!=NULL) {
+ *pl++ = *pv++;
+ }
+ //printf("format_line after %s\n", line);
+}
+
+char * read_line (int disk_index)
+{
+ static FILE * fp=NULL;
+ static int start=0;
+ static int start_disk_index=0;
+ int i;
+ static int finish_flag = 0;
+ static int varfh_flag = 0;
+#define READ_LINE_BUF_SIZE (MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR+2)
+#define SAFE_BYTES 1000
+#define READ_LINE_LENGTH (MAX_TRACE_LINE_LENGTH+SAFE_BYTES)
+
+ static char line_buf[READ_LINE_BUF_SIZE][READ_LINE_LENGTH];
+ char varfh_line_buf[READ_LINE_LENGTH];
+
+ start_profile (&read_line_profile);
+
+ if (fp==NULL) {
+ if (strcmp(trace_file, "stdin")) {
+ fp = fopen(trace_file, "r");
+
+ if (strstr(trace_file, ".varfh")) {
+ varfh_flag = 1;
+ };
+ if (strstr(trace_file, ".fmt1")) {
+ TRACE_COMMAND_REPLY_FLAG_POS += 12;
+ TRACE_VERSION_POS +=12;
+ TRACE_MSGID_POS +=12;
+ TRACE_FH_SIZE =48;
+ }
+ if (!fp) {
+ printf("can not open files %s\n", fp);
+ perror("open");
+ }
+ } else {
+ fp = stdin;
+ }
+ RFS_ASSERT (fp!=NULL);
+ for (i=0; i<READ_LINE_BUF_SIZE; i++) {
+ start_profile(&fgets_profile);
+ if (varfh_flag) {
+ if (!fgets(varfh_line_buf, READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ format_line (varfh_line_buf, line_buf[i]);
+ } else {
+ if (!fgets(line_buf[i], READ_LINE_LENGTH, fp)) {
+ RFS_ASSERT (0);
+ }
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", i, line_buf[i]);
+ }
+ }
+
+ if (disk_index > start_disk_index+READ_LINE_BUF_SIZE) {
+ printf ("disk_index %d start_disk_index %d READ_LINE_BUF_SIZE %d\n",
+ disk_index, start_disk_index, READ_LINE_BUF_SIZE);
+ }
+ RFS_ASSERT (disk_index <= start_disk_index+READ_LINE_BUF_SIZE)
+ if (disk_index==(start_disk_index+READ_LINE_BUF_SIZE)) {
+ if (finish_flag) {
+ return NULL;
+ }
+ start_profile(&fgets_profile);
+ if (!fgets(line_buf[start], READ_LINE_LENGTH, fp)) {
+ end_profile(&fgets_profile);
+ finish_flag = 1;
+ return NULL;
+ }
+ end_profile(&fgets_profile);
+ //printf ("read_line, line_buf[%d]:%s", start, line_buf[start]);
+ start = (start+1) % READ_LINE_BUF_SIZE;
+ start_disk_index ++;
+ }
+ RFS_ASSERT (disk_index < start_disk_index+READ_LINE_BUF_SIZE)
+ i = (start+disk_index-start_disk_index)%READ_LINE_BUF_SIZE;
+
+/*
+ if (!(strlen(line_buf[i])>80)) {
+ printf ("start %d start_disk_index %d disk_index %d strlen %d line_buf[%d] %s\n", start, start_disk_index, disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT (strlen(line_buf[i])>80);
+ }
+ if (!((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH)))
+ printf ("disk_index %d strlen %d line_buf[%d] %s\n", disk_index, strlen(line_buf[i]), i, line_buf[i]);
+ RFS_ASSERT ((strlen(line_buf[i])>80) && (strlen(line_buf[i])<MAX_TRACE_LINE_LENGTH));
+*/
+
+ end_profile (&read_line_profile);
+ return (line_buf[i]);
+}
+
+#define OPS_FLAG_INC 0
+#define OPS_FLAG_PRINT 1
+int read_write_fh_statistics (int flag, char * buf, int timestamp)
+{
+ static FILE * fp = NULL;
+ char * p;
+ char c;
+ if (!fp) {
+ fp = fopen ("read_write_fh.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (buf, "fh");
+ RFS_ASSERT (p);
+ c = p[40+3];
+ p[40+3]=0;
+ fprintf(fp, "%s\n", p+3+24);
+ p[40+3]=c;
+ };
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+int write_statistics(int flag, char * buf, char * reply_buf, int trace_status)
+{
+ static FILE * fp = NULL;
+ int pre_size, size, count;
+ char * p = reply_buf;
+ if (!fp) {
+ fp = fopen ("write.output", "w");
+ RFS_ASSERT (fp);
+ }
+ if (flag == OPS_FLAG_INC) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ fprintf (fp, "%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ }
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = (int) buf;
+ int timestamp = (int) reply_buf;
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+void ops_statistics (int ops_flag, int proc, int timestamp)
+{
+ static FILE * fp = NULL;
+ static struct {
+ int count;
+ int count_K;
+ int update_flag;
+ char name[32];
+ } ops[NOPS]={{0, 0, 0, "NULL"},{0, 0, 0, "GETATTR"},{0, 0, 1, "SETATTR"},{0, 0, 0, "ROOT"},
+ {0, 0, 0, "LOOKUP"},{0, 0, 0, "READLINK"},{0, 0, 0, "READ"},{0, 0, 1, "WRCACHE"},
+ {0, 0, 1, "WRITE"}, {0, 0, 1, "CREATE"},{0, 0, 1, "REMOVE"},{0, 0, 1, "RENAME"},
+ {0, 0, 1, "LINK"}, {0, 0, 1, "SYMLINK"},{0, 0, 1, "MKDIR"},{0, 0, 1, "RMDIR"},
+ {0, 0, 0, "READDIR"},{0, 0, 0, "FSSTAT"},{0, 0, 0, "ACCESS"},{0, 0, 0, "COMMIT"},
+ {0, 0, 0, "FSINFO"},{0, 0, 1, "MKNOD"}, {0, 0, 0, "PATHCONF"}, {0, 0, 0, "READDIRPLUS"}};
+
+ if (ops_flag == OPS_FLAG_INC) {
+ RFS_ASSERT (proc>=0 && proc<NOPS);
+ ops[proc].count ++;
+ if (ops[proc].count == 1000) {
+ ops[proc].count_K ++;
+ ops[proc].count = 0;
+ }
+ }
+ if (ops_flag == OPS_FLAG_PRINT) {
+ int i;
+ int update_K=0, update=0, total_K=0, total=0;
+ float f1, f2;
+ int disk_index = proc;
+
+ if (!fp) {
+ fp = fopen ("mix.output", "w");
+ RFS_ASSERT (fp);
+ }
+ for (i=0; i<NOPS; i++) {
+ total_K += ops[i].count_K;
+ total += ops[i].count;
+ if (ops[i].update_flag) {
+ update_K += ops[i].count_K;
+ update += ops[i].count;
+ }
+ }
+ update_K += update/1000;
+ update = update%1000;
+ total_K += total/1000;
+ total = total%1000;
+
+ f1 = total_K;
+ f1 = f1*1000+total;
+ for (i=0; i<NOPS; i++) {
+ f2 = ops[i].count_K;
+ f2 = f2*1000+ops[i].count;
+ fprintf (fp, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ fprintf (stderr, "%12s %8d,%03d %3.2f\%\n", ops[i].name, ops[i].count_K, ops[i].count, f2*100/f1);
+ }
+ f2 = update_K;
+ f2 = f2*1000+update;
+ fprintf (fp, " total %8d,%03d\n", total_K, total);
+ fprintf (stderr, " total %8d,%03d\n", total_K, total);
+ fprintf (fp, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf (stderr, " update %8d,%03d %2.2f\%\n", update_K, update, f2*100/f1);
+ fprintf(fp, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ fprintf(stderr, "###### disk_index %d timestamp %d\n", disk_index, timestamp);
+ }
+}
+
+
+void truncate_statistics (int flag, int proc, char * buf, char * reply_buf)
+{
+#define TRACE_FH_SIZE2 16
+#define BLOCK_SIZE 4096
+ static int no_size_num = 0;
+ static int have_size_num = 0;
+ static int equal_size_num = 0;
+ static int first_size_num = 0;
+ static int truncate_num = 0;
+ static int truncate_size = 0;
+ static int truncate_KB = 0;
+ static int truncate_block_num = 0;
+ static int padding_num = 0;
+ static int padding_KB = 0;
+ static int padding_size = 0;
+ static FILE * fp = NULL;
+ char * p;
+ char * trace_fh;
+ int pre_size, size, count;
+ struct generic_entry * ent;
+
+ if (flag == OPS_FLAG_PRINT) {
+ int disk_index = proc;
+ int timestamp = (int)buf;
+ if (!fp) {
+ fp = fopen ("truncate.output", "w");
+ RFS_ASSERT (fp);
+ }
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ fprintf(fp, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ fprintf(stderr, "###### disk_index %d timestamp %d no_size_req %d have_size_req %d equal_size_req %d trunc_req %d trunc_KB %d trunc_block_num %d padding_num %d padding_KB %d\n", disk_index, timestamp, no_size_num, have_size_num, equal_size_num, truncate_num, truncate_KB, truncate_block_num, padding_num, padding_KB);
+ return;
+ }
+
+ p = strstr (&buf[TRACE_MSGID_POS], "fh");
+ RFS_ASSERT (p);
+ p+=3;
+ trace_fh = p;
+
+ if (proc==SETATTR) {
+ p = strstr (buf, " size ");
+ } else {
+ RFS_ASSERT (proc==WRITE);
+ p = strstr (reply_buf, " ftype 1 ");
+ RFS_ASSERT (p);
+ p = strstr (p, " size ");
+ RFS_ASSERT (p);
+ if (strstr(p, " ftype 1 ")) {
+ fprintf (fp, "#### %s%s\n", buf, reply_buf);
+ fprintf (stderr, "#### %s%s\n", buf, reply_buf);
+ }
+ RFS_ASSERT (!strstr(p, " ftype 1 "));
+ }
+ if (!p) {
+ no_size_num ++;
+ return;
+ }
+ have_size_num ++;
+
+ sscanf (p, " size %x", &size);
+ if (size <0 || size > 2000000000) {
+ fprintf (fp, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ fprintf (stderr, "#### size too big %x %s %s\n", size, buf, reply_buf);
+ }
+
+ RFS_ASSERT (size >=0 && size <2000000000);
+ ent = generic_lookup (trace_fh+24, TRACE_FH_SIZE2, 0, fh_htable, FH_HTABLE_SIZE);
+ if (ent) {
+ if (ent->key3 != size) {
+ if (proc==SETATTR) {
+ //printf ("%s\n", buf);
+ //printf ("size change fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ if (ent->key3 > size) {
+ truncate_num ++;
+ truncate_size += ent->key3 - size;
+ truncate_block_num += (ent->key3+BLOCK_SIZE-1)/BLOCK_SIZE;
+ if (size!=0) {
+ //fprintf (stderr, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ //fprintf (fp, "truncate: pre_size %x size %x %s\n", ent->key3, size, buf);
+ truncate_block_num -= (size + BLOCK_SIZE-1)/BLOCK_SIZE;
+ }
+ if (truncate_size > 1000000000) {
+ truncate_KB += truncate_size/1000;
+ truncate_size %= 1000;
+ }
+ } else {
+ padding_num ++;
+ //printf ("%s\n", buf);
+ //printf ("padding fh %s pre-size %x size %x\n", trace_fh, ent->key3, size);
+ padding_size += size - ent->key3;
+ if (padding_size > 1000000000) {
+ padding_KB += padding_size/1000;
+ padding_size %= 1000;
+ }
+ }
+ }
+ ent->key3 = size;
+ }else
+ equal_size_num++;
+ } else {
+ generic_insert(trace_fh+24, TRACE_FH_SIZE2, size, fh_htable, FH_HTABLE_SIZE);
+ first_size_num ++;
+ }
+};
+
+int get_timestamp_usec (char * buf)
+{
+ char str[128];
+ int ret;
+ strncpy(str, buf, 100);
+ RFS_ASSERT (str[10]=='.');
+ RFS_ASSERT (str[17]==' ');
+ str[17]=0;
+ ret = atoi(&str[11]);
+ RFS_ASSERT (ret >=0 && ret <=999999);
+ return ret;
+}
+
+int get_timestamp_sec (char * buf)
+{
+ char str[128];
+ int ret;
+ strncpy(str, buf, 100);
+ RFS_ASSERT (str[10]=='.');
+ str[10]=0;
+ ret = atoi(str);
+ RFS_ASSERT (ret >1000000000 && ret <2000000000);
+ return ret;
+}
+
+int check_aging (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ switch (proc) {
+ int off, count, size;
+ char * t;
+ case CREATE: printf("%s\n", "create"); break;
+ case MKDIR: printf("%s\n", "mkdir"); break;
+ case REMOVE: printf("%s\n", "remove"); break;
+ case RMDIR: printf("%s\n", "rmdir"); break;
+ case WRITE:
+ t = buf;
+ t = strstr (t, "off");
+ RFS_ASSERT (t);
+ t+=4;
+ sscanf (t, "%x", &off);
+ RFS_ASSERT (off>=0 && off<0x7FFFFFFF)
+ t = strstr (t, "count");
+ RFS_ASSERT (t);
+ t+=6;
+ sscanf (t, "%x", &count);
+ RFS_ASSERT (count <= 32768);
+ printf("%s off %x count %x\n", "write", off, count);
+ //printf("%s count %x\n", "write", count);
+ break;
+ case SETATTR:
+ t = strstr (buf, " size ");
+ if (t) {
+ sscanf (t, " size %x", &size);
+ printf ("%s size %x\n", "setattr", size);
+ }
+ }
+ if ((disk_index%10000)==0) {
+ fprintf(stderr, "%d disk trace passed\n", disk_index);
+ }
+ };
+
+ fprintf(stderr, "%d disk trace parsed\n", disk_index);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, -1);
+}
+
+
+int check_statistics (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid, proc;
+ static int last_timestamp_sec = -1;
+ int timestamp_sec;
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ timestamp_sec = get_timestamp_sec(buf);
+
+ proc = nfs3proc_to_rfsproc[nfs3proc];
+ ops_statistics (OPS_FLAG_INC, proc, -1);
+
+ if (proc!= WRITE && proc!=SETATTR && proc!=READ) {
+ continue;
+ }
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ if (proc==READ || proc==WRITE)
+ read_write_fh_statistics (OPS_FLAG_INC, buf, 0);
+ if (proc == WRITE)
+ write_statistics (OPS_FLAG_INC, buf, reply_buf, trace_status);
+ if (proc==WRITE || proc==SETATTR)
+ truncate_statistics (OPS_FLAG_INC, proc, buf, reply_buf);
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ //printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if *
+ if ((missing_reply_num > memory_trace_size/10) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ if (last_timestamp_sec == -1) {
+ last_timestamp_sec = timestamp_sec;
+ } else if (timestamp_sec - last_timestamp_sec >=3600) {
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+ last_timestamp_sec = timestamp_sec;
+ }
+/*
+ if ((memory_trace_size%10000)==0) {
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, -1);
+ truncate_statistics (OPS_FLAG_PRINT, -1, NULL, NULL);
+ }
+*/
+ };
+
+ fprintf(stderr, "%d disk trace parsed, missing_reply %d\n", disk_index, missing_reply_num);
+ ops_statistics (OPS_FLAG_PRINT, disk_index, timestamp_sec);
+ truncate_statistics (OPS_FLAG_PRINT, disk_index, (char *)timestamp_sec, NULL);
+ read_write_fh_statistics(OPS_FLAG_PRINT, (char *)disk_index, timestamp_sec);
+ write_statistics(OPS_FLAG_PRINT, (char *)disk_index, (char *)timestamp_sec, -1);
+}
+
+
+/* This routine output all the requests, together with their replies */
+int pair_trace (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+ FILE * outputfp;
+ char output_file[1024];
+
+ strcpy (output_file, tracefile);
+ strcat (output_file, ".pair");
+ outputfp = fopen (output_file, "w");
+ RFS_ASSERT (outputfp);
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (disk_index == 258)
+ f();
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+ fprintf (outputfp, "%s\n", buf);
+
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ fprintf(outputfp, "%s", reply_buf);
+ trace_status = find_reply_status(reply_buf);
+ if (debug)
+ fprintf(stderr, "reply found trace_status %d\n", find_reply_status(reply_buf));
+ break;
+ };
+ }
+ }
+
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ fprintf (stderr, "%s no reply\n", buf);
+ fprintf(outputfp, "missing_reply\n");
+ missing_reply_num ++;
+ }
+
+ if (missing_reply_num > memory_trace_size/10 && missing_reply_num >100) {
+ fprintf (stderr, "missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+
+ memory_trace_size ++;
+
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+/* This routine output all the write requests, together with their replies. It is used for
+ * analysis of write requests: appended bytes, overwrite bytes etc
+ */
+int pair_write (char * tracefile)
+{
+ int disk_index=-1;
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int trace_status;
+ int pair_write_debug = 0;
+ int nfs3proc, msgid;
+ int ops[NFS3_PROCEDURE_COUNT]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ int memory_trace_size = 0;
+
+ while ((buf=read_line(++disk_index))!=NULL) {
+ if (buf[TRACE_COMMAND_REPLY_FLAG_POS]!='C')
+ continue;
+ if (buf[TRACE_VERSION_POS]!='3')
+ continue;
+ sscanf (&buf[TRACE_MSGID_POS], "%x %x", &msgid, &nfs3proc);
+
+ RFS_ASSERT (nfs3proc>=0 && nfs3proc<NFS3_PROCEDURE_COUNT);
+ ops[nfs3proc]++;
+
+ if (!strstr(buf, "write"))
+ continue;
+
+ buf [strlen(buf)-1] = 0;
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("disk_index %d strlen(buf) %d buf %s \n", disk_index, strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+ if (pair_write_debug)
+ printf("read line disk_index %d %s\n", disk_index, buf);
+
+ /* store the request to memory */
+ //strcpy (memory_trace[memory_trace_size].line, buf);
+ //memory_trace[memory_trace_size].disk_index = disk_index;
+
+ /* find and store the reply status and reply fhandle to memory */
+ //memory_trace[memory_trace_size].trace_status = NFS3ERR_RFS_MISS;
+ trace_status = NFS3ERR_RFS_MISS;
+ for (i=disk_index+1; i<disk_index+MAX_COMMAND_REPLY_DISTANCE_FOR_PAIR; i++) {
+ reply_buf = read_line(i);
+ if (pair_write_debug)
+ printf("searching for reply: read line %s\n", reply_buf);
+ if (!reply_buf)
+ break;
+ if (reply_buf[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&reply_buf[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&reply_buf[TRACE_MSGID_POS], &buf[TRACE_MSGID_POS], p-&(reply_buf[TRACE_MSGID_POS]))) {
+ int pre_size, size, count;
+ //memory_trace[memory_trace_size].trace_status = find_reply_status(reply_buf);
+ if (pair_write_debug)
+ printf("reply found trace_status %d\n", find_reply_status(reply_buf));
+ //break;
+ trace_status = find_reply_status(reply_buf);
+ if (trace_status == NFS3_OK) {
+ p = strstr (p, "pre-size");
+ RFS_ASSERT (p);
+ sscanf (p, "pre-size %x", &pre_size);
+ p += strlen("pre-size");
+ p = strstr (p, "size");
+ RFS_ASSERT (p);
+ sscanf (p, "size %x", &size);
+ p = strstr (p, "count");
+ if (!p)
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ RFS_ASSERT (p);
+ sscanf (p, "count %x", &count);
+ printf ("%s status %x pre-size %x size %x count %x\n", buf, trace_status, pre_size, size, count);
+ break;
+ }
+ };
+ }
+ }
+ //if (memory_trace[memory_trace_size].trace_status == NFS3ERR_RFS_MISS)
+ if (trace_status == NFS3ERR_RFS_MISS) {
+ printf ("%s no reply\n", buf);
+ missing_reply_num ++;
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ if (missing_reply_num > memory_trace_size/10) {
+ printf ("missing_reply_num %d too high for memory_trace_size %d\n", missing_reply_num, memory_trace_size);
+ exit (0);
+ }
+#endif
+
+ memory_trace_size ++;
+
+ /*
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ */
+ if ((memory_trace_size%10000)==0)
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ };
+
+ fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, memory_trace_size, missing_reply_num );
+ //fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+
+}
+
+#ifdef notdef
+/* This function is not finished writing */
+int calculate_performance()
+{
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int debug = 0;
+
+typedef struct {
+ struct timeval start;
+ struct timeval stop;
+ int trace_status;
+ int op;
+} trace_performance_ent_t;
+
+ struct timeval req_time;
+ struct timeval reply_time;
+
+ trace_performance_ent_t * ent = NULL;
+
+ while (!CYCLIC_FULL(memory_trace_index)) {
+
+ if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS))
+ buf = reply_buf;
+ if ((buf=read_line(++disk_index))==NULL) {
+END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num );
+ fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+ end_profile (&read_trace_profile);
+ return TRACE_FILE_END;
+ }
+
+ get_timestamp (&ent->req_time, buf);
+ if (MAX_COMMAND_REPLY_DISTANCE ==1) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ reply_buf=read_line(++disk_index);
+ RFS_ASSERT (reply_buf);
+ if (!strcmp(reply_buf, "missing_reply\n")) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ get_timestamp (&ent->reply_time, buf);
+ ent->trace_status = find_reply_status(reply_buf);
+ }
+ }
+ }
+}
+#endif
+
+int read_trace ()
+{
+ char *buf;
+ char *reply_buf;
+ int i;
+ char * p;
+ int debug = 0;
+ memory_trace_ent_t * ent=NULL;
+
+ start_profile (&read_trace_profile);
+
+ while (!CYCLIC_FULL(memory_trace_index)) {
+ if (ent!=NULL && (ent->trace_status == NFS3ERR_RFS_MISS))
+ buf = reply_buf;
+ if ((buf=read_line(++disk_index))==NULL) {
+END: fprintf(stderr, "total %d disk lines %d memory lines missing_reply_num %d\n", disk_index, CYCLIC_NUM(memory_trace_index), missing_reply_num );
+ fprintf (stderr, "init_dep_tab, req_num_with_init_fh %d req_num_with_new_fh %d discard %d\n", req_num_with_init_fh, req_num_with_new_fh, req_num_with_discard_fh);
+ end_profile (&read_trace_profile);
+ return TRACE_FILE_END;
+ }
+
+ if (rfs_debug)
+ printf ("disk_index %d %s\n", disk_index, buf);
+
+ if (disk_index==0) {
+ trace_timestamp1 = get_timestamp_sec (buf);
+ trace_starttime.sec = get_timestamp_sec (buf);
+ trace_starttime.usec = get_timestamp_usec (buf);
+ trace_starttime.esec = 0;
+ printf ("trace starttime %d %d %d\n", trace_starttime.sec, trace_starttime.usec, trace_starttime.esec);
+ } else
+ trace_timestamp2 = get_timestamp_sec (buf);
+
+ /* store the request to memory */
+ ent = &(memory_trace[memory_trace_index.head]);
+ strcpy (ent->line, buf);
+ ent->disk_index = disk_index;
+
+ if (MAX_COMMAND_REPLY_DISTANCE ==1) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ reply_buf=read_line(++disk_index);
+ RFS_ASSERT (reply_buf);
+ if (!strcmp(reply_buf, "missing_reply\n")) {
+ ent->trace_status == NFS3ERR_RFS_MISS;
+ } else {
+ ent->trace_status = find_reply_status(reply_buf);
+ }
+ };
+
+ if (ent->trace_status == NFS3ERR_RFS_MISS)
+ missing_reply_num ++;
+
+ if (MAX_COMMAND_REPLY_DISTANCE > 1) {
+ if ((missing_reply_num > disk_index/5) && (missing_reply_num > 100)) {
+ printf ("missing_reply_num %d too high for disk_index %d\n", missing_reply_num, disk_index);
+ exit (0);
+ }
+ }
+
+ /* find and store the reply trace fhandle for create-class requests */
+ if (ent->trace_status==NFS3_OK) {
+ if (strstr(buf, "create") || strstr(buf, "mkdir")
+ || (strstr(buf, "symlink") && (buf[TRACE_VERSION_POS]!='2'))
+ || strstr(buf, "mknod") ) {
+ p = find_reply_trace_fh(reply_buf);
+ if (p==NULL) {
+ printf("skip line disk_index %d %s \n", disk_index, buf);
+ continue;
+ }
+ memcpy(ent->reply_trace_fh, p, TRACE_FH_SIZE);
+ } else
+ memset(ent->reply_trace_fh, 0, TRACE_FH_SIZE);
+ }
+
+ add_to_dep_tab(memory_trace_index.head);
+
+ if (((disk_index+1)%20000)==0) {
+ fprintf(stderr, "%d disk trace parsed \n", disk_index+1);
+ };
+ };
+
+ end_profile (&read_trace_profile);
+ return TRACE_BUF_FULL;
+}
+#else /* not defined REDUCE_MEMORY_TRACE_SIZE */
+int read_trace ()
+{
+ FILE * fp;
+ char buf[1024];
+ // char * t=buf;
+ int disk_index=0;
+
+ fp = fopen(trace_file, "r");
+ RFS_ASSERT (fp!=NULL);
+ while (fgets(buf, MAX_TRACE_LINE_LENGTH, fp)) {
+ if (!((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH)))
+ printf("strlen(buf) %d buf %s \n", strlen(buf), buf);
+ RFS_ASSERT ((strlen(buf)>80) && (strlen(buf)<MAX_TRACE_LINE_LENGTH));
+
+ /* store the request to memory */
+ strcpy (memory_trace[memory_trace_size].line, buf);
+ memory_trace[memory_trace_size].disk_index = disk_index;
+ memory_trace_size ++;
+
+ if (memory_trace_size >= MAX_MEMORY_TRACE_LINES) {
+ fprintf (stderr, "memory trace size %d is not enough\n", MAX_MEMORY_TRACE_LINES);
+ break;
+ }
+ if ((disk_index%100000)==0)
+ fprintf(stderr, "%d disk trace parsed, store %d trace lines to memory\n", disk_index, memory_trace_size);
+ disk_index ++;
+ }
+
+ fprintf(stderr, "total %d disk lines %d memory lines \n", disk_index, memory_trace_size );
+}
+#endif
+
+
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+inline int disk_index_to_memory_index (int disk_index)
+{
+ static int memory_index = 0;
+
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (memory_trace[memory_trace_index.tail].disk_index <= disk_index);
+ RFS_ASSERT (memory_trace[CYCLIC_MINUS(memory_trace_index.head,1,memory_trace_index.size)].disk_index >=disk_index);
+ if (disk_index > memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index < disk_index) {
+ memory_index = CYCLIC_ADD(memory_index,1,memory_trace_index.size);
+ }
+ };
+ if (disk_index < memory_trace[memory_index].disk_index) {
+ while (memory_trace[memory_index].disk_index > disk_index) {
+ memory_index = CYCLIC_MINUS(memory_index,1,memory_trace_index.size);
+ }
+ };
+
+ RFS_ASSERT (disk_index == memory_trace[memory_index].disk_index);
+ return memory_index;
+}
+#else
+#define disk_index_to_memory_index(disk_index) disk_index
+#endif
+
+#define get_line_by_disk_index(disk_index) \
+ memory_trace[disk_index_to_memory_index(disk_index)].line
+
+inline char * find_reply_line (char * command_line, int cur_disk_index)
+{
+ int i;
+ char * line;
+ char * p;
+ int request_memory_index = disk_index_to_memory_index (cur_disk_index);
+ for (i=request_memory_index+1; i<request_memory_index+MAX_COMMAND_REPLY_DISTANCE && i<MAX_MEMORY_TRACE_LINES; i++) {
+ line = memory_trace[i].line;
+ if (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R') {
+ p = strchr (&line[TRACE_MSGID_POS], ' ');
+ RFS_ASSERT (p);
+ if (!strncmp(&line[TRACE_MSGID_POS], &command_line[TRACE_MSGID_POS], p-&(line[TRACE_MSGID_POS])))
+ return line;
+ }
+ }
+ return NULL;
+}
+
+inline int find_reply_status (char * line)
+{
+ char * p;
+ int i=0;
+
+ if (rfs_debug)
+ printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ if (!(line[TRACE_COMMAND_REPLY_FLAG_POS]=='R')) {
+ printf ("disk_index %d %s\n", disk_index, line);
+ };
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ p = line+TRACE_MSGID_POS+2; /* at least one letter for msgid and one letter for space */
+ if (strstr(p, "OK"))
+ return NFS3_OK;
+ if (strstr(p, "lookup 2") || strstr(p, "lookup 2"))
+ return 0x2;
+ if (strstr(p, "create d"))
+ return 0xd;
+ if (strstr(p, "setattr 1"))
+ return 0x1;
+ if (strstr(p, "setattr 2712")) /* 10002 NFS3ERR_NOT_SYNC */
+ return 0x2712;
+ if (strstr(p, "lookup d"))
+ return 0xd;
+ if (strstr(p, "read d"))
+ return 0xd;
+ if (strstr(p, "write d"))
+ return 0xd;
+ if (strstr(p, "write 46"))
+ return 0x46;
+ if (strstr(p, "getattr 46"))
+ return 0x46;
+ if (strstr(p, "mkdir d"))
+ return 0xd;
+ printf ("line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ printf ("!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ fprintf (stderr, "line %s flag %c return value weird\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ fprintf (stderr, "!!!!!!!!!!!!!!!!!!!!!!!!\n");
+}
+
+inline int find_reply_status_old (char * line)
+{
+ char * p;
+ int i=0;
+
+ //printf ("line %s flag %c\n", line, line[TRACE_COMMAND_REPLY_FLAG_POS]);
+ RFS_ASSERT (line[TRACE_COMMAND_REPLY_FLAG_POS]=='R');
+ if (!strstr(line, "OK")) {
+ p=strstr(line, " 6 read ");
+ if (p) {
+ p+=strlen(" 6 read ");
+ } else {
+ p = strstr (line, "status=XXX");
+ RFS_ASSERT (p);
+ p--;
+ RFS_ASSERT (*p==' ');
+ while (*p==' ')
+ p--;
+ while (*p!=' ') {
+ p--;
+ }
+ p++;
+ }
+ sscanf (p, "%x", &i);
+ if ((i<=0) || (i>10000))
+ printf("line %s\n", line);
+ RFS_ASSERT (i>0 && i<10009);
+ }
+ return i;
+}
+
+inline char * find_reply_trace_fh (char * line)
+{
+ char * p;
+ p = strstr (line, "OK fh");
+ if (!p) {
+ printf ("disk_index %d find_reply_trace_fh line %s\n", disk_index, line);
+ return NULL;
+ } else
+ return p+6;
+}
+
+#ifndef NO_DEPENDENCY_TABLE
+inline int disk_index_to_dep_index(int cur_dep_index, int disk_index)
+{
+ int i;
+ for (i=cur_dep_index; i>min_dep_index; i--) {
+ if (dep_tab[i].disk_index == disk_index)
+ return i;
+ }
+ RFS_ASSERT (0);
+}
+#endif
+
+inline int is_play_candidate (int dep_index)
+{
+ int proc = dep_tab[dep_index].proc;
+ int status = dep_tab[dep_index].status;
+ int trace_status = dep_tab[dep_index].trace_status;
+
+#ifndef TAKE_CARE_CREATE_MODE_BY_DAN
+ /* for a failed create in trace, trace_replay just ignore many time the trace create fail
+ * due to access control, but trace_play will success because our access control
+ * may be loose (all uid/gid is mapped to single one 513:513, so we just skip these requests
+ */
+ if ((proc==CREATE || proc==MKDIR) && (trace_status!=NFS3_OK) && (status!=NFS3ERR_RFS_MISS)) {
+ if (dependency_debug)
+ printf ("disk[%d] ignore failed create/mkdir in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+ failed_create_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_OTHER_FAILED_COMMAND
+ if (((trace_status == NFS3ERR_ACCES) && (proc==READ || proc==WRITE || proc==LOOKUP)) ||
+ ((trace_status == NFS3ERR_PERM) && (proc==SETATTR)) ){
+ if (dependency_debug)
+ printf ("disk[%d] ignore other failed command in trace, trace_status %d line %s",
+ dep_tab[dep_index].disk_index, trace_status, dep_tab[dep_index].line);
+
+ failed_other_command_num ++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((dep_tab[dep_index].proc==READLINK) ) { /* send request */
+ skipped_readlink_command_num ++;
+ return FALSE;
+ }
+#endif
+/* This is actually take care in get_nextop by checking fh_map error when dep_index==min_dep_index */
+#ifndef TAKE_CARE_CUSTOM_COMMAND
+ /* this line has a file handle which should belong to discard but it is not
+ * the file handle directly appears as parent directory in a lookup request
+ * the return value is NOENT, the parent directory should have been initialized
+ * but the initialization code just ignored all lookup request which didn't success
+ * including NOENT even though the parent directory is still valid.
+ */
+/*
+ if (( ((dep_tab[dep_index].disk_index==262213) || (dep_tab[dep_index].disk_index==214402))
+ && !(strcmp(trace_file, "anon-lair62-011130-1100.txt"))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+*/
+ if (( ((dep_tab[dep_index].disk_index==423727) || (0))
+ && !(strncmp(trace_file, "anon-lair62-011130-1500.txt", strlen("anon-lair62-011130-1500.txt")))
+ ) ||
+ ( ((dep_tab[dep_index].disk_index==238460) || (dep_tab[dep_index].disk_index ==238470))
+ && !(strcmp(trace_file, "anon-lair62-011130-1000.txt"))
+ )) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+ /* this line is about the mkdir 116d9d originally in anon-lair62-011130-1400.txt */
+ if (!strncmp(dep_tab[dep_index].line, "1007147245.194201", strlen("1007147245.194201"))) {
+ skipped_custom_command_num++;
+ return FALSE;
+ }
+#endif
+#ifndef TAKE_CARE_FSSTAT_COMMAND
+ /* the file handle used in this command is not processed properly by pre-processing */
+ if (proc==FSSTAT) {
+ char * trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ fh_map_t * fh = lookup_fh (trace_fh);
+ if (!fh) {
+ skipped_fsstat_command_num++;
+ return FALSE;
+ }
+ }
+#endif
+ return TRUE;
+}
+
+inline int is_dir_op (int proc)
+{
+ switch (proc) {
+ case MKDIR:
+ case CREATE:
+ case LINK:
+ case SYMLINK:
+ case MKNOD:
+ case REMOVE:
+ case RMDIR:
+ case RENAME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+inline int is_create_op (int proc)
+{
+ if (proc==CREATE || proc==MKDIR || proc==LINK || proc==SYMLINK || proc==MKNOD || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+inline int is_delete_op (int proc)
+{
+ if (proc==REMOVE || proc==RMDIR || proc==RENAME)
+ return 1;
+ return 0;
+}
+
+static inline char * find_lead_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh");
+ RFS_ASSERT (p);
+ p+=3; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+inline char * find_another_trace_fh(int proc, char * line)
+{
+ char * p;
+ /* check the file handle availability */
+ p = strstr (line, "fh2");
+ RFS_ASSERT (p);
+ p+=4; //printf ("check dependency dep_tab[%d] trace_fh %s line %s \n", dep_index, trace_fh, line);
+ return p;
+}
+
+/* return the index of next request in dep_tab.
+ * Return -1 if there is no suitable request to send
+ */
+inline int get_nextop(void)
+{
+ int i,j, k;
+ int * t;
+ static int dep_index = -2;
+ char * line;
+ char * p;
+#define INIT_MIN_WAIT_VALUE -999
+ static int min_wait_fhandle_dep_index = INIT_MIN_WAIT_VALUE;
+ int proc;
+ int flag;
+
+ if (min_wait_fhandle_dep_index == -999)
+ min_wait_fhandle_dep_index = dep_window_index.head;
+
+ for (i=0; i<CYCLIC_NUM(dep_window_index); i++) {
+ dep_index = (dep_window_index.tail+i) % dep_window_index.size;
+
+ proc = dep_tab[dep_index].proc;
+ flag = dep_tab[dep_index].flag;
+
+ if (dependency_debug)
+ printf ("get_nextop check dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+#ifdef NO_DEPENDENCY_TABLE
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT) {
+ if (is_play_candidate(dep_index)==TRUE) {
+ /* the trace_fh is the file handle for the operation directory, trace_fh_2 is other file handle
+ * used in the request */
+ if (proc==LINK || proc==RENAME) {
+ dep_tab[dep_index].trace_fh = find_another_trace_fh (proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].trace_fh_2 = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = 0;
+ } else {
+ dep_tab[dep_index].trace_fh = find_lead_trace_fh(proc, dep_tab[dep_index].line);
+ dep_tab[dep_index].fh = 0;
+ dep_tab[dep_index].fh_2 = (fh_map_t *)1;
+ };
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+#ifdef TIME_PLAY
+ dep_tab[dep_index].skip_sec = skip_sec;
+#endif
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_CANDIDATE\n", dep_tab[dep_index].disk_index);
+ } else {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_INIT to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE) ) {
+
+ if (!dep_tab[dep_index].fh)
+ dep_tab[dep_index].fh = lookup_fh (dep_tab[dep_index].trace_fh);
+ if (!dep_tab[dep_index].fh_2)
+ dep_tab[dep_index].fh_2 = lookup_fh (dep_tab[dep_index].trace_fh_2);
+
+ /* need to wait for file handle */
+ if ((!dep_tab[dep_index].fh) || (!dep_tab[dep_index].fh_2)) {
+ if (dependency_debug)
+ printf("disk[%d] can not lookup file handle\n", dep_tab[dep_index].disk_index);
+ if (dep_tab[dep_index].flag == DEP_FLAG_CANDIDATE) {
+ if (dependency_debug)
+ printf ("disk[%d] state DEP_FLAG_CANDIDATE to DEP_FLAG_WAIT_FHANDLE\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_FHANDLE;
+ sfs_gettime (&dep_tab[dep_index].start);
+ if (CYCLIC_LESS(dep_tab_index,dep_index,min_wait_fhandle_dep_index))
+ min_wait_fhandle_dep_index = dep_index;
+ } else {
+ struct ladtime tmp;
+ if (dep_index==dep_window_index.tail) {
+ if (!profile_debug)
+ printf ("fh_path_map error disk[%d] state DEP_FLAG_WAIT_FHANDLE to DEP_FLAG_DONE\n", dep_tab[dep_index].disk_index);
+ fh_path_map_err_num ++;
+ dep_tab[dep_index].flag = DEP_FLAG_DONE;
+ continue;
+ }
+ sfs_gettime (&tmp);
+ SUBTIME (tmp, dep_tab[dep_index].start);
+#define DEPENDENCY_TIMEOUT 50
+#ifdef TIME_PLAY
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT + (skip_sec - dep_tab[dep_index].skip_sec));
+#else
+ if (tmp.sec >= DEPENDENCY_TIMEOUT) {
+ printf("dep_tab[%d].flag %d disk_index %d line %s\n", dep_index,
+ dep_tab[dep_index].flag, dep_tab[dep_index].disk_index,
+ dep_tab[dep_index].line);
+ }
+ RFS_ASSERT (tmp.sec < DEPENDENCY_TIMEOUT );
+#endif
+ }
+ continue;
+ }
+
+ /* file handle ready, adjust_min_wait_fhandle_dep_index */
+ if ((dep_tab[dep_index].flag == DEP_FLAG_WAIT_FHANDLE)) {
+ if (dep_index == min_wait_fhandle_dep_index) {
+ min_wait_fhandle_dep_index = dep_window_index.head;
+ for (j=CYCLIC_ADD(dep_index,1,dep_window_index.size); CYCLIC_LESS(dep_window_index,j,dep_window_index.head); j++) {
+ if (dep_tab[j].flag ==DEP_FLAG_WAIT_FHANDLE) {
+ min_wait_fhandle_dep_index = j;
+ break;
+ }
+ }
+ }
+ }
+ if (dependency_debug)
+ printf("disk[%d] found file handle\n", dep_tab[dep_index].disk_index);
+ dep_tab[dep_index].flag = DEP_FLAG_FHANDLE_READY;
+
+ /* the normal file operation can be executed now */
+ if (!is_dir_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ if (dependency_debug)
+ printf("disk[%d] directory operation \n", dep_tab[dep_index].disk_index);
+ /* the directory operation need to lock the directory first */
+ if (dep_tab[dep_index].fh->lock) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DIRECTORY\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DIRECTORY;
+ continue;
+ }
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_FHANDLE_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DIRECTORY)) {
+ int j = dep_tab[dep_index].fh - fh_map;
+ if (dependency_debug) {
+ printf ("dep_tab[%d].disk_index %d, fh_map[%d] lock=%d\n",dep_index, dep_tab[dep_index].disk_index, j, dep_tab[dep_index].fh->lock);
+ printf ("trace_fh %s path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %s path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ if ((dep_tab[dep_index].fh->lock) || ((proc==RENAME) && (dep_tab[dep_index].fh_2->lock)) ) {
+ if (dependency_debug)
+ printf ("continue to wait for directory lock\n");
+ continue;
+ }
+ if (dependency_debug)
+ printf ("dep_tab[%d] disk index %d LOCK fh_map[%d] \n", dep_index, dep_tab[dep_index].disk_index, j);
+ dep_tab[dep_index].fh->lock = 1;
+ if (proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 1;
+
+ /* the non-delete directory operation can proceed now */
+ if (!is_delete_op (dep_tab[dep_index].proc)) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+
+ /* the delete operation can proceed if nobody ahead is waiting for fhandle */
+ /* probably this condition is not strong enough */
+// if ((min_wait_fhandle_dep_index<dep_index) ) {
+ if (dep_index!=dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("disk[%d] state %d to DEP_FLAG_WAIT_DELETE\n", dep_tab[dep_index].disk_index, dep_tab[dep_index].flag);
+ dep_tab[dep_index].flag = DEP_FLAG_WAIT_DELETE;
+ continue;
+ }
+ dep_tab[dep_index].flag = DEP_FLAG_DIRECTORY_READY;
+ }
+
+ if ((dep_tab[dep_index].flag == DEP_FLAG_DIRECTORY_READY) || (dep_tab[dep_index].flag == DEP_FLAG_WAIT_DELETE)) {
+// if (min_wait_fhandle_dep_index > dep_index) {
+ if (dep_index==dep_window_index.tail) {
+ if (dependency_debug)
+ printf ("return disk[%d]\n", dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ }
+#else /*NO_DEPENDENCY_TABLE undefined */
+ /* this part of code will be invalid after CYCLIC buffer design */
+ if (dep_tab[dep_index].flag == DEP_FLAG_INIT){
+ for (j=0, t=&(dep_tab[dep_index].dep_ops[0]);
+ (j<dep_tab[dep_index].init_dep_num) && (dep_tab[dep_index].cur_dep_num>0);
+ j++, t++) {
+ if (*t !=-1) {
+ if (dep_tab[disk_index_to_dep_index(dep_index, *t)].flag == DEP_FLAG_DONE) {
+ /* The depended request has been finished */
+ *t = -1;
+ dep_tab[dep_index].cur_dep_num --;
+ }
+ }
+ }
+
+ if (dep_tab[dep_index].cur_dep_num == 0) {
+ return dep_index;
+ }
+ }
+#endif
+ }
+
+ if (dependency_debug)
+ printf ("get_nexop return -1\n");
+ return -1;
+}
+
+int check_timeout(void)
+{
+ static int biod_index = 0;
+ int i;
+ int dep_index; /* index into dep_tab */
+ int proc;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime timeout;
+ struct ladtime current_time;
+
+ sfs_gettime (¤t_time);
+
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (biod_reqp[biod_index].in_use==TRUE) {
+ timeout = biod_reqp[biod_index].timeout;
+ if ((current_time.sec>timeout.sec) ||
+ ((current_time.sec==timeout.sec) && (current_time.usec>timeout.usec))) {
+
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+ op_ptr->results.timeout_calls++;
+ Ops[TOTAL].results.timeout_calls++;
+
+
+ if (is_create_op(proc)) {
+ dep_tab[dep_index].flag = DEP_FLAG_CANDIDATE;
+ printf ("resend dep_tab[%d], disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_CANDIDATE);
+ } else {
+ finish_request (biod_index, dep_index, NFS3ERR_RFS_TIMEOUT, DEP_FLAG_DONE);
+ }
+ timeout_num ++;
+ num_out_reqs_statistics_at_timeout[num_out_reqs]++;
+
+ //RFS_ASSERT (!is_create_op(proc));
+
+ if (per_packet_debug)
+ printf ("timeout request: disk_index %d, dep_index %d biod_reqp[%d].start %d:%d timeout %d:%d current %d:%d\n", dep_tab[dep_index].disk_index, dep_index, biod_index, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, timeout.sec, timeout.usec, current.sec, current.usec);
+ }
+ }
+ }
+}
+
+/* Allocate a biod_req entry to send and receive request dep_tab[dep_index]
+ * build the cross reference between dep_tab entry and biod_req entry
+ */
+struct biod_req * get_biod_req(int dep_index) /* index into dep_tab */
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ if (!biod_reqp[biod_index].in_use) {
+ biod_reqp[biod_index].in_use = 1;
+ biod_reqp[biod_index].dep_tab_index = dep_index;
+ dep_tab[dep_index].biod_req_index = biod_index;
+ num_out_reqs++;
+ return &(biod_reqp[biod_index]);
+ }
+ }
+ return NULL;
+}
+
+/* Return index into biod_reqp
+ * return -1 upon failure
+ */
+int lookup_biod_req (int xid)
+{
+ static int biod_index = 0;
+ int i;
+ for (i=0; i<max_biod_reqs; i++, biod_index = (biod_index+1)%max_biod_reqs) {
+ /* give a NULL as timeout pointer may cause indefinitely block */
+ if ((biod_reqp[biod_index].in_use == TRUE) &&( biod_reqp[biod_index].xid == xid)) {
+ return biod_index;
+ }
+ }
+ return -1;
+}
+
+extern struct ladtime test_start;
+void init_time_offset(void)
+{
+ struct ladtime tmp1;
+ struct ladtime tmp2;
+
+ test_start.sec = 0;
+ test_start.usec = 0;
+ sfs_gettime (&tmp1); /* called at initial time: tmp1 = play_starttime */
+#ifdef SPEED_UP
+ DIVTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime / SCALE */
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp1, PLAY_SCALE) /* tmp1 = play_starttime * SCALE */
+#endif
+
+ tmp2 = trace_starttime; /* tmp2 = trace_starttime */
+ SUBTIME (tmp2, tmp1); /* tmp2 = trace_starttime - play_starttime *|/ SCALE */
+ time_offset = tmp2; /* time_offset = trace_starttime - play_starttime *|/ SCALE */
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void init_dep_tab_entry (int dep_index)
+{
+ char * line;
+ int version;
+ int nfsproc;
+ int msgid;
+
+ //line = get_line_by_disk_index (dep_tab[dep_index].disk_index);
+ line = dep_tab[dep_index].line;
+ sscanf (line, "%d.%d", &(dep_tab[dep_index].timestamp.tv_sec), &(dep_tab[dep_index].timestamp.tv_usec));
+ sscanf (&line[TRACE_MSGID_POS], "%x %x", &msgid, &nfsproc);
+ //printf ("msgid %x nfsproc %x\n", msgid, nfsproc);
+ if (line[TRACE_VERSION_POS]=='2') {
+ dep_tab[dep_index].proc = nfs2proc_to_rfsproc[nfsproc];
+ RFS_ASSERT (nfsproc <18);
+ } else {
+ /* This is for debug purpose */
+ if (line[TRACE_VERSION_POS] !='3') {
+ fprintf(stderr, "line[TRACE_VERSION_POS] %c line %s\n", line[TRACE_VERSION_POS], line);
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-1);
+ if (!line)
+ line = get_line_by_disk_index (dep_tab[dep_index].disk_index-2);
+ RFS_ASSERT (line);
+ fprintf(stderr, "previousline %s\n", line);
+ }
+ RFS_ASSERT (line[TRACE_VERSION_POS] =='3');
+ if (nfsproc >= NFS3_PROCEDURE_COUNT) {
+ fprintf(stderr, "proc %d line %s\n", nfsproc, line);
+
+ }
+ RFS_ASSERT (nfsproc <NFS3_PROCEDURE_COUNT);
+ dep_tab[dep_index].proc = nfs3proc_to_rfsproc[nfsproc];
+ }
+ RFS_ASSERT (dep_tab[dep_index].proc >= 0 && dep_tab[dep_index].proc < NOPS);
+ dep_tab[dep_index].flag = DEP_FLAG_INIT;
+}
+
+void adjust_play_window (int flag, int * poll_timeout_arg)
+{
+ struct ladtime max_window_time;
+ static struct ladtime max_poll_time = {0, 2000, 0};
+ struct ladtime t;
+ int i;
+ char * line;
+ cyclic_index_t old_dep_window_index = dep_window_index;
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, begin\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, begin\n");
+#endif
+
+ while ((!CYCLIC_EMPTY(dep_window_index)) && (dep_tab[dep_window_index.tail].flag == DEP_FLAG_DONE)) {
+#ifdef notdef
+ //CYCLIC_PRINT (memory_trace_index);
+ //printf("MOVE_TAIL_TO memory_index %d\n", dep_tab[dep_tab_index.tail].memory_index);
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+ printf("%d is done\n", dep_window_index.tail);
+#endif
+ CYCLIC_MOVE_TAIL(dep_tab_index);
+ CYCLIC_MOVE_TAIL(dep_window_index);
+
+#ifdef notdef
+ CYCLIC_PRINT (dep_tab_index);
+ CYCLIC_PRINT (dep_window_index);
+
+ if (! (dep_tab_index.tail == dep_window_index.tail)) {
+ CYCLIC_PRINT(dep_tab_index);
+ CYCLIC_PRINT(dep_window_index);
+ };
+ RFS_ASSERT ( dep_tab_index.tail == dep_window_index.tail);
+#endif
+
+ if (!CYCLIC_EMPTY(dep_tab_index)) {
+#ifdef notdef
+ RFS_ASSERT (!CYCLIC_EMPTY(memory_trace_index));
+ if (!(CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head))) {
+ CYCLIC_PRINT(memory_trace_index);
+ CYCLIC_PRINT(dep_tab_index);
+ printf("dep_tab[head-1].memory_index, %d [tail].memory_index %d\n",
+ dep_tab[CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size)].memory_index,
+ dep_tab[dep_tab_index.tail].memory_index);
+ }
+ RFS_ASSERT (CYCLIC_LESS (memory_trace_index, dep_tab[dep_tab_index.tail].memory_index, memory_trace_index.head));
+#endif
+ CYCLIC_SET_TAIL_TO(&memory_trace_index, dep_tab[dep_tab_index.tail].memory_index);
+ //printf ("set memory_trace_index to %d=%d, dep_tab_index.tail %d\n", memory_trace_index.tail, dep_tab[dep_tab_index.tail].memory_index, dep_tab_index.tail);
+ } else {
+ // CYCLIC_MOVE_TAIL (memory_trace_index);
+ }
+ }
+
+ while (CYCLIC_EMPTY(dep_tab_index)) {
+
+ if (disk_io_status == TRACE_FILE_END)
+ return;
+ else {
+ //printf ("************** ADJUST_PLAY_WINDOW sleep 1 s\n");
+ //print_cyclic_buffers();
+ //pthread_yield();
+ //usleep (1000);
+ }
+ }
+
+ /* max_trace_window_time = current *|/ SCALE + trace_starttime */
+ sfs_gettime (¤t);
+
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+ MULTIME (current, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ DIVTIME (current, PLAY_SCALE);
+#endif
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+
+ /* Right now it is not clear how to deal with the situation where MAX_PLAY_WINDOW is reached */
+ if (CYCLIC_NUM(dep_window_index) == MAX_PLAY_WINDOW) {
+ //printf ("can not catch up the speed, dep_tab_size %d dep_window_max %d reach min_dep_index %d+MAX_PLAY_WINDOW\n", dep_tab_size, dep_window_max, min_dep_index);
+ //printf (".");
+ can_not_catch_speed_num ++;
+ }
+ while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) &&
+ (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) {
+ struct ladtime t;
+ int dep_index = (dep_window_index.tail+i) % dep_window_index.size;
+ t.sec = dep_tab[dep_index].timestamp.tv_sec;
+ t.usec = dep_tab[dep_index].timestamp.tv_usec;
+ if ((t.sec>max_window_time.sec)||(t.sec==max_window_time.sec && t.usec>max_window_time.usec))
+ break;
+ CYCLIC_MOVE_HEAD(dep_window_index);
+ }
+#else
+ ADDTIME (current, trace_starttime);
+ max_window_time = current;
+ while ((CYCLIC_NUM(dep_window_index) < MAX_PLAY_WINDOW) &&
+ (CYCLIC_NUM(dep_window_index) < CYCLIC_NUM(dep_tab_index)) ) {
+ CYCLIC_MOVE_HEAD(dep_window_index);
+ }
+#endif
+
+ if (flag == BUSY)
+ *poll_timeout_arg = 0;
+ else if (CYCLIC_NUM(dep_window_index)==CYCLIC_NUM(dep_tab_index)) {
+ *poll_timeout_arg = 1000; /* poll_timeout set to 1 second for the last request */
+ } else {
+#ifdef TIME_PLAY
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_index.head].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_index.head].timestamp.tv_usec;
+ if (adjust_play_window_debug>=2)
+ printf ("dep_tab[dep_window_index.head %d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_index.head, tmp.sec, tmp.usec, max_window_time.sec, max_window_time.usec);
+
+ SUBTIME (tmp, max_window_time);
+#ifdef SPEED_UP
+ DIVTIME (tmp, PLAY_SCALE);
+#endif
+#ifdef SLOW_DOWN
+ MULTIME (tmp, PLAY_SCALE);
+#endif
+/*
+ tmp1 = tmp;
+
+ if (tmp.sec > max_poll_time.sec) {
+
+ if (rfs_debug)
+ printf ("dep_tab[%d].timestamp %d:%d, max_window_time %d:%d\n",
+ dep_window_max, dep_tab[dep_window_max].timestamp.tv_sec, dep_tab[dep_window_max].timestamp.tv_usec, max_window_time.sec, max_window_time.usec);
+ printf ("skip %d seconds\n", tmp.sec-max_poll_time.sec);
+ SUBTIME (tmp, max_poll_time);
+ tmp.usec = 0;
+ skip_sec += tmp.sec;
+ SUBTIME (test_start, tmp);
+ tmp = max_poll_time;
+ }
+*/
+
+ //RFS_ASSERT ((tmp.sec < 1000));
+ if (tmp.sec > 1000)
+ tmp.sec = 1000;
+ if ((tmp.sec ==0) && (tmp.usec==0)) {
+ *poll_timeout_arg = 0;
+ } else
+ *poll_timeout_arg = tmp.sec*1000000+tmp.usec;
+#else
+ /*
+ struct ladtime tmp;
+ struct ladtime tmp1;
+ tmp.sec = dep_tab[dep_window_max].timestamp.tv_sec;
+ tmp.usec = dep_tab[dep_window_max].timestamp.tv_usec;
+ tmp1.sec = dep_tab[dep_window_max-1].timestamp.tv_sec;
+ tmp1.usec = dep_tab[dep_window_max-1].timestamp.tv_usec;
+ SUBTIME (tmp, tmp1);
+ RFS_ASSERT ((tmp.sec < 1000));
+ RFS_ASSERT ((tmp.sec>0) || ((tmp.sec==0) && (tmp.usec>0)));
+ *poll_timeout = tmp.sec*1000000+tmp.usec;
+ */
+
+ *poll_timeout_arg = 100000;
+#endif
+ }
+ if (rfs_debug)
+ printf ("adjust_play_window: flag %d min %d -> %d, max %d -> %d poll_timeout_arg %d \n",
+ flag, old_dep_window_index.tail, dep_window_index.tail, old_dep_window_index.head,
+ dep_window_index.head, *poll_timeout_arg);
+
+#ifdef notdef
+ printf ("^^^^^^^^^^^^^^^ adjust_play_window, end\n");
+ CYCLIC_PRINT (dep_tab_index);
+ printf ("dep_tab[%d].memory_index %d\n", dep_tab_index.tail, dep_tab[dep_tab_index.tail].memory_index);
+ CYCLIC_PRINT (dep_window_index);
+ CYCLIC_PRINT (memory_trace_index);
+ printf (" adjust_play_window, end\n\n");
+#endif
+ //CYCLIC_ASSERT(4);
+}
+
+/* poll for usecs and receive, after receive one reply,
+ * return index in biod_reqp of the corresponding request
+ */
+int poll_and_get_reply (int usecs)
+{
+ int biod_index = -1;
+ int xid;
+ int error;
+ struct timeval zero_time = {0, 0}; /* Immediately return */
+
+#ifdef RECV_THREAD
+ //printf("recv thread waitsem 1\n");
+ waitsem (async_rpc_sem);
+ //printf("recv thread got sem 1\n");
+#endif
+ do {
+ error = biod_poll_wait (NFS_client, usecs);
+ switch (error) {
+ case -1:
+ if (errno == EINTR) {
+ error = 1;
+ continue;
+ }
+ if (rfs_debug) {
+ (void) fprintf(stderr, "biod_poll_wait error\n");
+ perror ("");
+ (void) fflush(stderr);
+ }
+ break;
+ case 0:
+ break;
+ default:
+#ifdef UDP
+ //printf("recv thread waitsem 2\n");
+ //waitsem (async_rpc_sem);
+ //printf("recv thread got sem 2\n");
+ error = get_areply_udp (NFS_client, &xid, &zero_time);
+ //postsem (async_rpc_sem);
+ //printf("recv thread postsem 2\n");
+ // RFS_ASSERT (error!= RPC_TIMEOUT); /* we have polled and know there is data */
+ // RFS_ASSERT (error!= RPC_CANTRECV);
+ RFS_ASSERT (error == RPC_SUCCESS);
+
+ biod_index = lookup_biod_req (xid);
+ sfs_gettime (&(biod_reqp[biod_index].stop));
+#else
+ RFS_ASSERT (0);
+#endif
+ }
+ } while (0);
+#ifdef RECV_THREAD
+ postsem (async_rpc_sem);
+ //printf("recv thread postsem 1\n");
+#endif
+ return biod_index;
+}
+
+void print_result(void)
+{
+ int i, j;
+ struct ladtime t;
+ int dep_index;
+ int avg_msecs;
+ unsigned long long tmp;
+ int avg_usecs;
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stdout, "trace play result:\n");
+ (void) fprintf(stdout, "\t percentage good_cnt bad_cnt timeout_cnt\telapsed time\t\t\taverage time\n");
+ for (i=0; i<NOPS+1; i++) {
+ if (Ops[i].results.good_calls==0) {
+ avg_msecs = 0;
+ avg_usecs = 0;
+ } else {
+ tmp = Ops[i].results.time.sec*1000000 + Ops[i].results.time.usec;
+ avg_msecs = 0;
+ avg_usecs = tmp/Ops[i].results.good_calls;
+/*
+ avg_msecs = (Ops[i].results.time.sec*1000 + Ops[i].results.time.usec/1000)/Ops[i].results.good_calls;
+ avg_usecs = (Ops[i].results.time.usec%1000)/Ops[i].results.good_calls;
+*/
+ }
+ (void) fprintf(stdout, "%11s\t%4.1f\t%4d\t%4d\t%4d\t\tsec %8d usec %8d \tusec %8d\n",
+ Ops[i].name,
+ (float)(100*Ops[i].results.good_calls)/(float)Ops[TOTAL].results.good_calls,
+ Ops[i].results.good_calls, Ops[i].results.bad_calls, Ops[i].results.timeout_calls,
+ Ops[i].results.time.sec, Ops[i].results.time.usec, avg_msecs*1000+avg_usecs);
+ }
+ (void) fflush (stdout);
+ }
+
+#if 0 /* commented out by G. Jason Peng */
+ RFS_ASSERT (read_data_owe_GB==0);
+ printf("read_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",read_data_total_GB, read_data_total, read_data_owe_GB, read_data_owe, (read_data_owe)/(read_data_total/100), read_data_adjust_times);
+ printf("write_data_total %d GB and %d bytes, owe %d GB and %d bytes, %d percent, adjusted %d times \n",write_data_total_GB, write_data_total, write_data_owe_GB, write_data_owe, (write_data_owe)/(write_data_total/100), write_data_adjust_times);
+ printf("poll_timeout_0_num %d poll_timeout_pos_num %d\n", poll_timeout_0_num, poll_timeout_pos_num);
+ printf("failed_create_command_num_in_original_trace %d\nfailed_other_command_num_in_original_trace %d\nskipped_readlink_command_num %d\nskipped_custom_command_num %d\nfh_path_map_err_num %d\nskipped_fsstat_command_num %d\nmissing_reply_num %d\nrename_rmdir_noent_reply_num %d\nrmdir_not_empty_reply_num %d\nloose_access_control_reply_num %d\nlookup_err_due_to_rename %d\nlookup_err_due_to_parallel_remove %d\nlookup_eaccess_enoent_mismatch %d\nread_io_err_num %d\nstale_fhandle_err_num %d abnormal_EEXIST_num %d abnormal_ENOENT_num %d proper_reply_num %d run_stage_proper_reply_num %d\n",
+ failed_create_command_num,
+ failed_other_command_num,
+ skipped_readlink_command_num,
+ skipped_custom_command_num,
+ fh_path_map_err_num,
+ skipped_fsstat_command_num,
+ missing_reply_num,
+ rename_rmdir_noent_reply_num,
+ rmdir_not_empty_reply_num,
+ loose_access_control_reply_num,
+ lookup_err_due_to_rename_num,
+ lookup_err_due_to_parallel_remove_num,
+ lookup_eaccess_enoent_mismatch_num,
+ read_io_err_num,
+ stale_fhandle_err_num,
+ abnormal_EEXIST_num,
+ abnormal_ENOENT_num,
+ proper_reply_num, run_stage_proper_reply_num);
+#endif
+
+// print_dump(Client_num, Child_num);
+}
+
+/*
+ * allocate and initialize client handles
+ */
+static int
+init_rpc(void)
+{
+ int dummy = 0;
+
+ /*
+ * Set up the client handles. We get them all before trying one
+ * out to insure that the client handle for LOOKUP class is allocated
+ * before calling op_getattr().
+ */
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
+ }
+
+ NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ RPC_ANYSOCK, &Nfs_timers[0]);
+
+ if (NFS_client == ((CLIENT *) NULL)) {
+ return(-1);
+ }
+
+ /*
+ * create credentials using the REAL uid
+ */
+ NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
+ (int)Cur_gid, 0, NULL);
+
+ if (biod_init(dummy, dummy) == -1) {
+ return(-1);
+ }
+
+ return(0);
+} /* init_rpc */
+
+void
+init_counters(void)
+{
+ uint_t i;
+ uint_t start_msec;
+
+ /* Ready to go - initialize operation counters */
+ for (i = 0; i < NOPS + 1; i++) {
+ Ops[i].req_cnt = 0;
+ Ops[i].results.good_calls = 0;
+ Ops[i].results.bad_calls = 0;
+ Ops[i].results.timeout_calls = 0; // RFS
+ Ops[i].results.fast_calls = 0;
+ Ops[i].results.time.sec = 0;
+ Ops[i].results.time.usec = 0;
+ Ops[i].results.msec2 = 0;
+ }
+
+ /* initialize timers and period variables */
+ sfs_gettime(&Starttime);
+ Cur_time = Starttime;
+ start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
+ Previous_chkpnt_msec = start_msec;
+ Calls_this_period = 0;
+ Reqs_this_period = 0;
+ Sleep_msec_this_period = 0;
+ Calls_this_test = 0;
+ Reqs_this_test = 0;
+ Sleep_msec_this_test = 0;
+}
+
+static char *
+nfs3_strerror(int status)
+{
+ static char str[40];
+ switch (status) {
+ case NFS3_OK:
+ (void) strcpy(str, "no error");
+ break;
+ case NFS3ERR_PERM:
+ (void) strcpy(str, "Not owner");
+ break;
+ case NFS3ERR_NOENT:
+ (void) strcpy(str, "No such file or directory");
+ break;
+ case NFS3ERR_IO:
+ (void) strcpy(str, "I/O error");
+ break;
+ case NFS3ERR_NXIO:
+ (void) strcpy(str, "No such device or address");
+ break;
+ case NFS3ERR_ACCES:
+ (void) strcpy(str, "Permission denied");
+ break;
+ case NFS3ERR_EXIST:
+ (void) strcpy(str, "File exists");
+ break;
+ case NFS3ERR_XDEV:
+ (void) strcpy(str, "Cross-device link");
+ break;
+ case NFS3ERR_NODEV:
+ (void) strcpy(str, "No such device");
+ break;
+ case NFS3ERR_NOTDIR:
+ (void) strcpy(str, "Not a directory");
+ break;
+ case NFS3ERR_ISDIR:
+ (void) strcpy(str, "Is a directory");
+ break;
+ case NFS3ERR_INVAL:
+ (void) strcpy(str, "Invalid argument");
+ break;
+ case NFS3ERR_FBIG:
+ (void) strcpy(str, "File too large");
+ break;
+ case NFS3ERR_NOSPC:
+ (void) strcpy(str, "No space left on device");
+ break;
+ case NFS3ERR_ROFS:
+ (void) strcpy(str, "Read-only file system");
+ break;
+ case NFS3ERR_MLINK:
+ (void) strcpy(str, "Too many links");
+ break;
+ case NFS3ERR_NAMETOOLONG:
+ (void) strcpy(str, "File name too long");
+ break;
+ case NFS3ERR_NOTEMPTY:
+ (void) strcpy(str, "Directory not empty");
+ break;
+ case NFS3ERR_DQUOT:
+ (void) strcpy(str, "Disc quota exceeded");
+ break;
+ case NFS3ERR_STALE:
+ (void) strcpy(str, "Stale NFS file handle");
+ break;
+ case NFS3ERR_REMOTE:
+ (void) strcpy(str, "Object is remote");
+ break;
+ case NFS3ERR_BADHANDLE:
+ (void) strcpy(str, "Bad file handle");
+ break;
+ case NFS3ERR_NOT_SYNC:
+ (void) strcpy(str, "Not sync write");
+ break;
+ case NFS3ERR_BAD_COOKIE:
+ (void) strcpy(str, "Bad cookie");
+ break;
+ case NFS3ERR_NOTSUPP:
+ (void) strcpy(str, "Operation not supported");
+ break;
+ case NFS3ERR_TOOSMALL:
+ (void) strcpy(str, "Value too small");
+ break;
+ case NFS3ERR_SERVERFAULT:
+ (void) strcpy(str, "Server fault");
+ break;
+ case NFS3ERR_BADTYPE:
+ (void) strcpy(str, "Bad type");
+ break;
+ case NFS3ERR_JUKEBOX:
+ (void) strcpy(str, "Jukebox");
+ break;
+ case NFS3ERR_RFS_TIMEOUT:
+ (void) strcpy(str, "Timeout");
+ break;
+ default:
+ (void) sprintf(str, "Unknown status %d", status);
+ break;
+ }
+ return (str);
+}
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "trace play usage");
+}
+extern void init_file_system (void)
+{
+ return;
+}
+
+void show_fhandle (nfs_fh3 * fhp)
+{
+ struct knfs_fh * kfhp = (struct knfs_fh *)fhp;
+
+ int dev;
+
+ if (quiet_flag)
+ return;
+
+ RFS_ASSERT (kfhp->fh_version == 1);
+ RFS_ASSERT (kfhp->fh_fsid_type == 0);
+ RFS_ASSERT (kfhp->fh_auth_type == 0);
+
+ dev = ntohs(kfhp->fh_dev_major);
+ dev = dev<<8;
+ dev = dev + ntohs(kfhp->fh_dev_minor);
+
+ /* kfhp->fh_dev_ino hold the inode number of export point of the mounted
+ * file system. For example, if /tmp/t1 is exported, /tmp/t1/t2 is mounted,
+ * then fh_dev_ino hold the inode number of t1, not t2
+ */
+
+ switch (kfhp->fh_fileid_type) {
+ case 0:
+ printf("fh:type 0 root dev 0x%x dev_ino %d\n", dev, kfhp->fh_dev_ino);
+ break;
+ case 1:
+ printf("fh:type 1 %d %x dev %x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, dev, kfhp->fh_dev_ino);
+ break;
+ case 2:
+ printf("fh:type2 %d %x dirino %d dev 0x%x dev_ino %x\n",
+ kfhp->fh_ino, kfhp->fh_generation, kfhp->fh_dirino, dev, kfhp->fh_dev_ino);
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+}
+
+nfs_fh3 zero_fhandle;
+int init_fh_map ()
+{
+ memset (fh_map, 0, sizeof (fh_map));
+ memset(fh_htable, 0, sizeof (fh_htable));
+ memset (&zero_fhandle, 0, sizeof(nfs_fh3));
+ printf ("SIZE of fh map %d KB\n", sizeof (fh_map)/1000);
+ fh_i = 0;
+}
+
+int add_fh (int map_flag, char * trace_fh, char * path, nfs_fh3 * play_fh)
+{
+ char * old_trace_fh;
+
+ /* first lookup if the entry for fh is already in the table */
+ struct generic_entry * p;
+
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (p) {
+ RFS_ASSERT (fh_map[p->key3].flag = FH_MAP_FLAG_PARTIAL);
+ RFS_ASSERT (map_flag ==FH_MAP_FLAG_COMPLETE);
+ fh_map[p->key3].flag = map_flag;
+ //RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ if (memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE)) {
+ int i;
+ printf ("fh_map[%d].trace_fh %s trace_fh %s", p->key3, fh_map[p->key3].trace_fh, trace_fh);
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+ RFS_ASSERT (0);
+ }
+ RFS_ASSERT (!strcmp(fh_map[p->key3].path, path));
+ /* It's possible that in fh-path-map, many trace_fh are corresponding to one path
+ * some of it may be the result of lookup after symlink, which is not handled
+ * properly as new created objects
+ */
+#ifdef TAKE_CARE_SYMBOLIC_LINK
+ RFS_ASSERT (!memcmp(&fh_map[p->key3].play_fh, &zero_fhandle, sizeof(nfs_fh3)));
+#endif
+ memcpy (&fh_map[p->key3].play_fh, play_fh, sizeof (nfs_fh3));
+ if ((fh_map_debug==1)) // || (stage ==TRACE_PLAY_STAGE))
+ printf ("update the play_fh for trace_fh %s path %s \n", trace_fh, path);
+ return 0;
+ }
+
+ fh_map[fh_i].flag = map_flag;
+ fh_map[fh_i].lock = 0;
+ strncpy(fh_map[fh_i].trace_fh, trace_fh, TRACE_FH_SIZE);
+
+ RFS_ASSERT (strlen(path) < MAX_PLAY_PATH_SIZE);
+ strcpy (fh_map [fh_i].path, path);
+ if (map_flag==FH_MAP_FLAG_COMPLETE)
+ memcpy (&fh_map[fh_i].play_fh, play_fh, sizeof(nfs_fh3));
+ else
+ memset (&fh_map[fh_i].play_fh, 0, sizeof(nfs_fh3));
+
+ if ((fh_map_debug==1)) { // || (stage ==TRACE_PLAY_STAGE)) {
+ printf ("insert trace_fh %s path %s play_fh:\n", trace_fh, path);
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ //show_fhandle(play_fh);
+ } else
+ printf("null\n");
+ }
+
+/*
+ if (map_flag == FH_MAP_FLAG_DISCARD)
+ printf ("insert flag %d trace_fh %s path %s play_fh:\n", map_flag, trace_fh, path);
+*/
+
+ generic_insert(trace_fh, TRACE_FH_SIZE, fh_i, fh_htable, FH_HTABLE_SIZE);
+
+ fh_i = (fh_i+1);
+ RFS_ASSERT (fh_i < FH_MAP_SIZE);
+
+ return 0;
+};
+
+inline fh_map_t * lookup_fh (char * trace_fh )
+{
+ struct generic_entry * p;
+ p = generic_lookup (trace_fh, TRACE_FH_SIZE, 0, fh_htable, FH_HTABLE_SIZE);
+ if (fh_map_debug==1)
+ printf ("lookup trace_fh %s\n", trace_fh);
+
+ if (p) {
+ if (fh_map_debug==1) {
+ printf ("found: fh_i[%d] trace_fh %s path %s play_fh:\n", p->key3, fh_map[p->key3].trace_fh, fh_map[p->key3].path);
+ //show_fhandle(&fh_map[p->key3].play_fh);
+ }
+ RFS_ASSERT (!memcmp(fh_map[p->key3].trace_fh, trace_fh, TRACE_FH_SIZE));
+ return (&(fh_map[p->key3]));
+ } else {
+ //printf ("lookup_fh %s not found\n", trace_fh);
+ if (stage != READ_DEP_TAB_STAGE && (fh_map_debug==1)) {
+ printf ("lookup not found trace_fh %s\n", trace_fh);
+ }
+ return NULL;
+ }
+ RFS_ASSERT (0);
+}
+
+int delete_fh (char * trace_fh, int fh_map_index)
+{
+ generic_delete (trace_fh, TRACE_FH_SIZE, fh_map_index, fh_htable, FH_HTABLE_SIZE);
+ return 0;
+};
+
+int lookup_init_filesystem (nfs_fh3 * parent, char * name, nfs_fh3 * result)
+{
+ LOOKUP3args args;
+ LOOKUP3res reply; /* the reply */
+ enum clnt_stat rpc_stat; /* result from RPC call */
+ struct ladtime start;
+ struct ladtime stop;
+ static int i=0;
+
+ /* set up the arguments */
+ (void) memcpy((char *) &args.what.dir, (char *) parent,
+ sizeof (nfs_fh3));
+ args.what.name = name;
+ (void) memset((char *) &reply.resok.object, '\0', sizeof (nfs_fh3));
+
+ /* make the call */
+ sfs_gettime(&start);
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_LOOKUP,
+ xdr_LOOKUP3args, (char *) &args,
+ xdr_LOOKUP3res, (char *) &reply,
+ Nfs_timers[Init]);
+ sfs_gettime(&stop);
+
+ if (rpc_stat !=RPC_SUCCESS) {
+ printf("rpc_stat %d\n", rpc_stat);
+ perror("");
+ }
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ (void) memcpy((char *) result, (char *) &reply.resok.object, sizeof (nfs_fh3));
+ return (reply.status);
+}
+
+void read_fh_map(char * fh_map_file)
+{
+ FILE * fp;
+ int i = 0;
+ char buf[1024];
+ char trace_fh[MAX_TRACE_FH_SIZE];
+ char intbuf[9];
+ char * trace_path;
+ char * p;
+ int map_flag;
+#define MAX_PATH_DEPTH 20
+ nfs_fh3 parents[MAX_PATH_DEPTH];
+ char * lookup_path_ptr[MAX_PATH_DEPTH];
+ char lookup_path [MAX_PLAY_PATH_SIZE];
+ int depth;
+ int new_dir_flag = 0;
+ int lineno = 0;
+
+ depth = 0;
+ memset(lookup_path_ptr, 0, sizeof(lookup_path_ptr));
+ memcpy(&parents[depth], &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ strcpy(lookup_path, "/");
+ lookup_path_ptr[depth]=&lookup_path[0];
+
+ fp = fopen(fh_map_file, "r");
+ if (!fp) {
+ printf ("can not opern %s\n", fh_map_file);
+ perror("open");
+ exit (0);
+ }
+ RFS_ASSERT (fp!=NULL);
+ if (strstr(fh_map_file, "fmt1")) {
+ TRACE_FH_SIZE = 48;
+ }
+
+ intbuf[8]=0;
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets(buf, 1024, fp)) {
+ lineno ++;
+ if (fh_i % 10000==0)
+ printf("%d fh_map entry read\n", fh_i);
+
+ RFS_ASSERT (buf[strlen(buf)-1]=='\n');
+ buf[strlen(buf)-1]=0;
+ if (fh_map_debug) {
+ printf("%d fgets return %s\n", fh_i, buf);
+ printf("depth %d lookup_path %s\n", depth, lookup_path);
+ }
+ //for (i=0; i<=depth; i++)
+ //printf("lookup_path_ptr[%d] %s ", i, lookup_path_ptr[i]);
+ //printf("\n");
+#ifdef COMPRESS_TRACE_FH
+ for (i=0; i<TRACE_FH_SIZE/8; i++) {
+ strncpy(intbuf, buf+i*8, 8);
+ sscanf(intbuf, "%x", trace_fh+i*8); // maybe it should be 4, anyway we don't compress for now
+ }
+ trace_path = buf+TRACE_FH_SIZE*2+1; /* +1 the trace contains only initial file handle */
+#else
+ memcpy(trace_fh, buf, TRACE_FH_SIZE);
+ trace_path = buf + TRACE_FH_SIZE +1;
+#endif
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ trace_path = +=2; /* +3 if the trace contains both initial and later created file handle */
+#endif
+
+#ifdef NO_DEPENDENCY_TABLE
+ if (!strncmp (trace_path, "DISCARD", 7) ||
+ !strncmp (trace_path, "LN", 2) ) {
+ map_flag = FH_MAP_FLAG_DISCARD;
+ add_fh (map_flag, buf, trace_path, 0);
+ continue;
+ }
+#endif
+
+ p = trace_path+strlen(trace_path)-2;
+ while (*p!='/')
+ p--;
+ p++;
+ //RFS_ASSERT (p-trace_path<=strlen(lookup_path)+1);
+ //RFS_ASSERT (p>trace_path);
+
+ if (strncmp(lookup_path, trace_path, p-trace_path)) {
+ printf("strncmp lookup_path %s trace_path %s for length %d\n", lookup_path, trace_path, p-trace_path);
+ }
+ RFS_ASSERT (!strncmp(lookup_path, trace_path, p-trace_path));
+ //while (strncmp(lookup_path, trace_path, p-trace_path)) { /* one step deeper */
+ while (strlen(lookup_path)>p-trace_path && depth>0) {
+ //printf("depth--\n");
+ if (depth<=0)
+ printf ("lookup_path %s trace_path %s p-trace_path %d depth %d\n", lookup_path, trace_path, p-trace_path, depth);
+ RFS_ASSERT (depth>0);
+ *lookup_path_ptr[depth]=0;
+ lookup_path_ptr[depth]=0;
+ depth--;
+ }
+ RFS_ASSERT (strlen(lookup_path)==(p-trace_path) || (depth==0));
+
+
+#ifdef TRACE_CONTAIN_LATER_FHANDLE
+ if (buf[TRACE_FH_SIZE*2+1]=='Y') {
+ map_flag = FH_MAP_FLAG_COMPLETE;
+ } else {
+ map_flag = FH_MAP_FLAG_PARTIAL;
+ RFS_ASSERT (buf[TRACE_FH_SIZE*2+1]=='N');
+ }
+#else
+ map_flag = FH_MAP_FLAG_COMPLETE;
+#endif
+ if ((*(p+strlen(p)-1))=='/') {
+ *(p+strlen(p)-1)=0;
+ new_dir_flag = 1;
+ } else
+ new_dir_flag = 0;
+
+ if (map_flag == FH_MAP_FLAG_COMPLETE) {
+ int ret = lookup_init_filesystem (&parents[depth], p, &parents[depth+1]);
+ if (ret!=NFS3_OK) {
+ printf ("lineno %d %s\n", lineno, buf);
+ }
+ RFS_ASSERT (ret == NFS3_OK);
+ add_fh (map_flag, buf, trace_path, &parents[depth+1]);
+ } else
+ add_fh (map_flag, buf, trace_path, 0);
+
+ if (new_dir_flag) {
+ /* the new fhandle is of a directory */
+ lookup_path_ptr[depth+1] = lookup_path+strlen(lookup_path);
+ strcat (lookup_path, p);
+ strcat (lookup_path, "/");
+
+ //printf("depth++\n");
+ depth++;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (fh_map_debug) {
+ for (i=0; i<fh_i; i++) {
+ int * p1 = (int *)&(fh_map[i].play_fh);
+#ifdef COMPRESS_TRACE_FH
+ int * p = (int *)fh_map[i].trace_fh;
+ printf("fh_map[%d].trace_fh %8x%8x%8x%8x%8x%8x%8x%8x path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+#else
+ printf("fh_map[%d].trace_fh %s path %s \nnew_filehandle %8x%8x%8x%8x%8x%8x%8x%8x\n",
+ i, fh_map[i].trace_fh, fh_map[i].path,
+ *p1, *(p1+1), *(p1+2), *(p1+3), *(p1+4), *(p1+5), *(p1+6), *(p1+7));
+ }
+#endif
+
+ fprintf(stderr, "total %d requests \n", i);
+ }
+}
+
+int f()
+{
+ return 1;
+}
+
+inline free_biod_req (int biod_index)
+{
+ RFS_ASSERT (biod_reqp[biod_index].in_use == TRUE);
+ biod_reqp[biod_index].in_use = FALSE;
+ num_out_reqs --;
+}
+
+void finish_request (int biod_index, int dep_index, int status, int dep_flag)
+{
+ static int i = 0;
+ /* the ending operation, same as when a request time out */
+
+ dep_tab[dep_index].stop = biod_reqp[biod_index].stop; /* RFS: to dump data */
+ free_biod_req (biod_index);
+
+ dep_tab[dep_index].status = status;
+
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = -dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].flag = dep_flag;
+ if (is_dir_op(dep_tab[dep_index].proc)) {
+ int j;
+ RFS_ASSERT (dep_tab[dep_index].fh->lock = 1);
+ dep_tab[dep_index].fh->lock = 0;
+ if (dep_tab[dep_index].proc==RENAME)
+ dep_tab[dep_index].fh_2->lock = 0;
+ j = dep_tab[dep_index].fh-fh_map;
+ if (dependency_debug) {
+ printf ("fh_map[%d] is UNlocked\n",j);
+ printf ("trace_fh %d path %s\n", dep_tab[dep_index].fh->trace_fh, dep_tab[dep_index].fh->path);
+ printf ("trace_fh %d path %s\n", fh_map[j].trace_fh, fh_map[j].path);
+ }
+ }
+}
+
+/* the request argument may have pointers pointing to buffers, e.g. the name in lookup,
+ * the target of symlink, the write data */
+char arg_res[MAX_ARG_RES_SIZE];
+char buf1 [MAX_BUF1_SIZE];
+char buf2 [MAX_BUF2_SIZE];
+
+int execute_next_request ()
+{
+ int dep_index;
+ int proc;
+ char * line;
+ struct biod_req * reqp;
+ sfs_op_type *op_ptr; /* per operation info */
+ struct ladtime call_timeout;
+ static int last_print_time = -1;
+
+ if (num_out_reqs == max_biod_reqs) {
+ return -1;
+ }
+
+ start_profile (&valid_get_nextop_profile);
+ start_profile (&invalid_get_nextop_profile);
+ dep_index = get_nextop();
+ if (dep_index == -1) {
+ end_profile (&invalid_get_nextop_profile);
+ return dep_index;
+ };
+ end_profile (&valid_get_nextop_profile);
+
+ start_profile (&prepare_argument_profile);
+ line = dep_tab[dep_index].line;
+
+ if (per_packet_debug)
+ fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+
+ end_profile(&total_profile);
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ last_print_time = total_profile.in.tv_sec;
+ //fprintf (stdout, "time %d processing dep_tab[%d] disk_index %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, dep_index, dep_tab[dep_index].disk_index, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+/*
+ CYCLIC_PRINT (dep_tab_index);
+ {
+ int tmp = CYCLIC_MINUS(dep_tab_index.head,1,dep_tab_index.size);
+ printf("dep_tab_index.head-1 %d disk_index %d tail %d disk_index %d\n", tmp, dep_tab[tmp].disk_index,
+ dep_tab_index.tail, dep_tab[dep_tab_index.tail].disk_index);
+ }
+*/
+#ifdef TIME_PLAY
+#ifdef SPEED_UP
+/*
+ if (can_not_catch_speed_num < 2000) {
+ PLAY_SCALE ++;
+ printf ("set PLAY_SCALE to %d\n", PLAY_SCALE);
+ };
+ if (can_not_catch_speed_num > 50000) {
+ PLAY_SCALE /= 2;
+ } else {
+ if (can_not_catch_speed_num > 5000) {
+ PLAY_SCALE -= 2;
+ if (PLAY_SCALE < 1)
+ PLAY_SCALE = 1;
+ }
+ }
+*/
+#endif
+ if ((total_profile.in.tv_sec > 100)) {
+ can_not_catch_speed_num_total += can_not_catch_speed_num;
+ }
+ can_not_catch_speed_num = 0;
+#endif
+ }
+ if (rfs_debug)
+ printf ("processing dep_tab[%d] disk_index %d %s\n", dep_index, dep_tab[dep_index].disk_index, line);
+
+ proc = dep_tab[dep_index].proc;
+ rfs_Ops[proc].setarg (dep_index, line, arg_res, buf1, buf2);
+
+ op_ptr = &Ops[proc];
+ reqp = get_biod_req (dep_index);
+ RFS_ASSERT (reqp);
+
+#ifdef notdef /* place to set request timeout. G. Jason Peng */
+ call_timeout.sec = 2; //Nfs_timers[op_ptr->call_class].tv_sec;
+ call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
+#else
+ call_timeout.sec = 0;
+ call_timeout.usec = 300000;
+ //call_timeout.usec = 14000;
+ //call_timeout.usec = 13000;
+ //call_timeout.usec = 6000;
+ //call_timeout.usec = 8000;
+ //call_timeout.usec = 10000;
+#endif
+
+ /* make the call */
+ sfs_gettime(&(reqp->start));
+ end_profile (&prepare_argument_profile);
+ start_profile (&biod_clnt_call_profile);
+#define REAL_PLAY
+#ifdef REAL_PLAY
+
+#ifdef RECV_THREAD
+ //printf ("send thread waitsem\n");
+ waitsem(async_rpc_sem);
+ //printf ("send thread got sem\n");
+#endif
+ reqp->xid = biod_clnt_call(NFS_client, rfs_Ops[proc].nfsv3_proc,
+ rfs_Ops[proc].xdr_arg, arg_res);
+#ifdef RECV_THREAD
+ postsem(async_rpc_sem);
+ //printf ("send thread postsem\n");
+#endif
+
+#else // REAL_PLAY
+ reqp->xid = dep_index+1; /* just fake a message id and let it expire */
+#endif
+ RFS_ASSERT (reqp->xid != 0);
+ reqp->timeout = reqp->start;
+ ADDTIME (reqp->timeout, call_timeout);
+ dep_tab[dep_index].flag = DEP_FLAG_SENT;
+ if (event_order_index < EVENT_ORDER_SIZE)
+ event_order[event_order_index++] = dep_tab[dep_index].disk_index;
+
+ dep_tab[dep_index].start = reqp->start; /* RFS: to dump data */
+ end_profile (&biod_clnt_call_profile);
+
+ send_num ++;
+}
+
+void check_reply (int proc, int biod_index, int dep_index, int status, char * errmsg, int trace_status)
+{
+ if (((status!=trace_status)) && (status!=NFS3_OK) && (trace_status!=NFS3ERR_RFS_MISS)) {
+ if (!profile_debug)
+ printf ("receive problem reply, xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d command disk index %d\n", biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec, dep_tab[dep_index].disk_index);
+#ifndef TAKE_CARE_UNLOOKED_UP_NON_NEW_FILES
+ /* these files is not looked up and is not create/mkdir/symlink/link/mknod ed before they
+ * are refered by name through rename, remove
+ */
+ if ((proc==RENAME || proc==REMOVE) && (status==NFS3ERR_NOENT) && (trace_status ==0)) {
+ /* current initialization doesnot take care of rename source, if there is no
+ * create or lookup before that source, the source object will not exist when
+ * rename occurs
+ */
+ rename_rmdir_noent_reply_num++;
+ } else
+#endif
+#ifndef TAKE_CARE_SYMBOLIC_LINK
+ if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status==NFS3ERR_NOENT)) {
+ /* in the original trace, first lookup return NOENT, then symlink is executed, then lookup return OK
+ * the initialization considers only the lookup return OK and created the file in the initialization
+ * so in trace play the first lookup return OK
+ */
+ RFS_ASSERT (1);
+ } else // if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) && (trace_status == 0)) {
+ /* trace_status could be EAGAIN */
+ if ((proc==SYMLINK) && (status == NFS3ERR_EXIST) ) {
+ /* due to similar reason as above, the initialization code initializes the symbolic link as a normal
+ * file already
+ */
+ RFS_ASSERT (1);
+ } else
+#endif
+#ifndef TAKE_CARE_NOEMPTY_RMDIR
+ /* the remove packet seems got lost in the trace capture, so replay can not finish */
+ if ((proc==RMDIR) && (status==NFS3ERR_NOTEMPTY)) {
+ RENAME3args args;
+ RENAME3res reply; /* the reply */
+ RMDIR3args * rmdir_argp;
+ enum clnt_stat rpc_stat; /* result from RPC call */
+
+ rfs_Ops[proc].setarg (dep_index, dep_tab[dep_index].line, arg_res, buf1, buf2);
+ rmdir_argp = (RMDIR3args *)arg_res;
+
+ memcpy(&args.from, &(rmdir_argp->object), sizeof (diropargs3));
+ memcpy(&args.to.dir, &(Export_dir.fh_data->sfs_fh_un.f_fh3), sizeof(nfs_fh3));
+ args.from.name = buf1; /* the buf1 is already filled when parsing rmdir */
+ args.to.name = buf2;
+ sprintf(buf2, "rmdir_%d_%s", dep_tab[dep_index].disk_index, rmdir_argp->object.name);
+
+ rpc_stat = clnt_call(NFS_client, NFSPROC3_RENAME,
+ xdr_RENAME3args, (char *) &args,
+ xdr_RENAME3res, (char *) &reply,
+ Nfs_timers[Init]);
+ RFS_ASSERT (rpc_stat == RPC_SUCCESS);
+ if (reply.status!=NFS3_OK)
+ printf ("change rmdir into rename, reply.status %d\n", reply.status);
+ RFS_ASSERT (reply.status==NFS3_OK);
+ rmdir_not_empty_reply_num ++;
+#endif
+#ifndef TAKE_CARE_ACCESS_ERROR
+ } else if ((status==0) && (trace_status==NFS3ERR_ACCES)) {
+ loose_access_control_reply_num ++;
+#endif
+#ifdef NO_DEPENDENCY_TABLE
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3_OK)) {
+ lookup_err_due_to_rename_num ++;
+ } else if ((proc==LOOKUP) && (status==NFS3_OK) && (trace_status == NFS3ERR_NOENT)) {
+ /* if there is a remove in front of the lookup, but it is
+ * actually executed later than the lookup
+ */
+ lookup_err_due_to_parallel_remove_num ++;
+#endif
+#ifndef TAKE_CARE_LOOKUP_EACCESS_ENOENT_MISMATCH
+ /* if the looked return EACCESS in the trace, means the object still exists
+ * should have initialized, right not don't initialize it, hence play status
+ * could be ENOENT
+ */
+ } else if ((proc==LOOKUP) && (status==NFS3ERR_NOENT) && (trace_status==NFS3ERR_ACCES)) {
+ lookup_eaccess_enoent_mismatch_num ++;
+#endif
+#ifdef TOLERANT_READ_IO_ERR
+ } else if ((proc==READ) && (status==NFS3ERR_IO) && (trace_status==NFS3_OK)) {
+ read_io_err_num ++;
+#endif
+#ifdef TOLERANT_STALE_FHANDLE_ERR
+ } else if ((status==NFS3ERR_STALE) && (trace_status==NFS3_OK)) {
+ printf ("!!!!!!! STALE FILE HANDLE \n");
+ //sleep(1);
+ stale_fhandle_err_num ++;
+#endif
+ } else {
+ int i;
+ for (i=dep_window_index.tail; CYCLIC_LESS(dep_window_index,i,dep_window_index.head); i++) {
+ if (dep_tab[i].flag!=1)
+ printf ("dep_tab[%d].disk_index %d, flag %d line %s\n", i, dep_tab[i].disk_index, dep_tab[i].flag, dep_tab[i].line);
+ }
+
+ if (status==EEXIST) {
+ abnormal_EEXIST_num ++;
+ } else if (status == ENOENT) {
+ abnormal_ENOENT_num ++;
+ } else {
+ printf ("!!!!!!!!!!!!!1 should fail\n");
+ //RFS_ASSERT (0);
+ }
+ }
+ } else {
+ proper_reply_num ++;
+ if (total_profile.in.tv_sec >= WARMUP_TIME)
+ run_stage_proper_reply_num ++;
+ }
+
+}
+
+/* return -1 if there is no reply being received
+ * return the dep_index if the corresponding reply has been received
+ */
+int receive_next_reply (int busy_flag)
+{
+ int dep_index;
+ int biod_index;
+ int proc;
+ char * line;
+ char * reply_line;
+ sfs_op_type *op_ptr; /* per operation info */
+ int ret;
+ int status;
+ int trace_status;
+ char * errmsg;
+ int poll_timeout = 0; /* timeout in usecs */
+
+ /* wait for reply */
+ start_profile (&valid_poll_and_get_reply_profile);
+ start_profile (&invalid_poll_and_get_reply_profile);
+
+ if (busy_flag == BUSY) {
+ poll_timeout = 0;
+ poll_timeout_0_num ++;
+ } else {
+ poll_timeout = 2000; /* 10000 or 2000 is a better number in non-debugging state */
+ //poll_timeout = 0; /* 10000 or 2000 is a better number in non-debugging state */
+ poll_timeout_pos_num ++;
+ }
+
+ biod_index = poll_and_get_reply (poll_timeout);
+ if (biod_index==-1) {
+ end_profile (&invalid_poll_and_get_reply_profile);
+ return -1;
+ };
+ end_profile (&valid_poll_and_get_reply_profile);
+
+ start_profile (&decode_reply_profile);
+ /* check the corresponding request */
+ dep_index = biod_reqp[biod_index].dep_tab_index;
+ if (biod_reqp[biod_index].in_use==1) {
+ RFS_ASSERT (dep_tab[dep_index].biod_req_index == biod_index);
+ } else {
+ printf ("biod_index %d reply received but the request has been time out\n", biod_index);
+ return -1;
+ }
+
+ proc = dep_tab[dep_index].proc;
+ op_ptr = &Ops[proc];
+
+ if (dep_tab[dep_index].flag != DEP_FLAG_SENT) {
+ printf("dep_tab[%d].flag %d proc %d status %d start %d:%d stop %d:%d\n",
+ dep_index, dep_tab[dep_index].flag, proc, dep_tab[dep_index].status,
+ dep_tab[dep_index].start.sec, dep_tab[dep_index].start.usec,
+ dep_tab[dep_index].stop.sec, dep_tab[dep_index].stop.usec );
+ printf ("received reply for timeout requests dep_tab[%d].disk_index %d\n", dep_index, dep_tab[dep_index].disk_index);
+ return dep_index;
+ }
+ RFS_ASSERT (dep_tab[dep_index].flag == DEP_FLAG_SENT);
+
+ /* decode the reply */
+ rfs_Ops[proc].setres (arg_res, buf1);
+ ret = proc_header (NFS_client, rfs_Ops[proc].xdr_res, arg_res);
+ RFS_ASSERT (ret == RPC_SUCCESS);
+ status = *((int *)arg_res);
+ errmsg = nfs3_strerror (status);
+ end_profile (&decode_reply_profile);
+
+ start_profile (&check_reply_profile);
+ /* compare with the reply in the trace */
+ line = dep_tab[dep_index].line;
+ reply_line = dep_tab[dep_index].reply_line;
+ trace_status = dep_tab[dep_index].trace_status;
+
+ if (per_packet_debug || rfs_debug )
+ fprintf (stdout, "dep_tab[%d], disk_index %d, receive reply, rpc_ret %d xid %x nfs_ret %d %s trace_status %d start %d:%d stop %d:%d \n", dep_index, dep_tab[dep_index].disk_index, ret, biod_reqp[biod_index].xid, status, errmsg, trace_status, biod_reqp[biod_index].start.sec, biod_reqp[biod_index].start.usec, biod_reqp[biod_index].stop.sec, biod_reqp[biod_index].stop.usec);
+
+ /* error checking */
+ check_reply (proc, biod_index, dep_index, status, errmsg, trace_status);
+
+ /* free resources */
+ finish_request (biod_index, dep_index, status, DEP_FLAG_DONE);
+ recv_num ++;
+
+ /* we set 100 seconds warm up time */
+ if ((total_profile.in.tv_sec >= WARMUP_TIME)) {
+ /* get statistics */
+ if (status == trace_status || (status==NFS3_OK && trace_status==NFS3ERR_RFS_MISS) ) {
+ op_ptr->results.good_calls++;
+ Ops[TOTAL].results.good_calls++;
+ } else {
+ op_ptr->results.bad_calls++;
+ Ops[TOTAL].results.bad_calls++;
+ }
+ sfs_elapsedtime (op_ptr, &(biod_reqp[biod_index].start), &(biod_reqp[biod_index].stop));
+ end_profile (&check_reply_profile);
+ }
+
+ //start_profile (&add_create_object_profile);
+
+ if (trace_status == NFS3_OK && (proc==CREATE || proc==MKDIR || proc==SYMLINK || proc==MKNOD)) {
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_line);
+#endif
+ if (status!=NFS3_OK) {
+ /* commented out for 1022 */
+ printf ("!!!!!! Should have been an ASSERTION FAILURE \n");
+ RFS_ASSERT (proc==SYMLINK);
+ RFS_ASSERT (0);
+ } else {
+ if (proc!=SYMLINK || line[TRACE_VERSION_POS]!='2')
+ add_new_file_system_object(proc, dep_index, line, reply_line);
+ }
+ }
+ //end_profile (&add_create_object_profile);
+}
+
+inline void add_new_file_system_object (int proc, int dep_index, char * line, char * reply_line)
+{
+ char * child_trace_fh;
+ fh_map_t * parent_entryp;
+ char component_name[MAX_PLAY_PATH_SIZE];
+ char * parent_trace_fh;
+ char child_path[MAX_PLAY_PATH_SIZE];
+ post_op_fh3 * post_op_fh3_child;
+ char * reply_trace_fh;
+ nfs_fh3 * child_fh3;
+
+ parent_trace_fh = strstr (line, "fh");
+ RFS_ASSERT (parent_trace_fh);
+ parent_trace_fh +=3;
+ parent_entryp = lookup_fh (parent_trace_fh);
+ RFS_ASSERT (parent_entryp);
+ parse_name (parent_trace_fh+65, component_name);
+ strcpy (child_path, parent_entryp->path);
+ strcat (child_path, "/");
+ strcat (child_path, component_name);
+
+ /* find the corresponding create request */
+ //printf ("before find reply trace_fh reply_line %s\n", reply_line);
+#ifdef REDUCE_MEMORY_TRACE_SIZE
+ reply_trace_fh = dep_tab[dep_index].reply_trace_fh;
+#else
+ reply_trace_fh = find_reply_trace_fh (reply_line);
+#endif
+ RFS_ASSERT (reply_trace_fh != NULL);
+ switch (proc) {
+ case CREATE:
+ RFS_ASSERT (((CREATE3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((CREATE3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKDIR:
+ RFS_ASSERT (((MKDIR3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKDIR3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case SYMLINK:
+ RFS_ASSERT (((SYMLINK3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((SYMLINK3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case MKNOD:
+ RFS_ASSERT (((MKNOD3res *)arg_res)->res_u.ok.obj.handle_follows==TRUE);
+ child_fh3 = &((MKNOD3res *)arg_res)->res_u.ok.obj.handle;
+ break;
+ case LOOKUP:
+ RFS_ASSERT (proc==LOOKUP);
+ child_fh3 = &((LOOKUP3res *)arg_res)->res_u.ok.object;
+ break;
+ default:
+ RFS_ASSERT (0);
+ }
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ RFS_ASSERT (reply_trace_fh[TRACE_FH_SIZE]==' ');
+#endif
+ reply_trace_fh[TRACE_FH_SIZE] = 0;
+ add_fh (FH_MAP_FLAG_COMPLETE, reply_trace_fh, child_path, child_fh3); /* exist flag is not used now, set to 1 */
+#ifndef REDUCE_MEMORY_TRACE_SIZE
+ /* just to preserve the original reply line not changed */
+ reply_trace_fh[TRACE_FH_SIZE] = ' ';
+#endif
+}
+
+/* initialize timestamp and proc field of dep_tab entry */
+void trace_play(void)
+{
+
+ /* The flag to indicate whether trace_player is BUSY. Trace_player is BUSY
+ * when either there is request to send or there is reply being
+ * received. Otherwise it is IDLE. The timeout for polling replies
+ * is set to 0 when BUSY, it is set to the waiting time to the first
+ * request outside of current <min_dep_index, dep_window_max> window when IDLE.
+ */
+ int busy_flag = BUSY;
+ //int dep_index; /* index into dependency table: dep_tab */
+ //int biod_index; /* index into outstanding requests: biod_reqp */
+ static int last_print_time = -1;
+ int poll_timeout = 0;
+
+#ifndef IO_THREAD
+ disk_io_status = read_trace();
+#endif
+
+ RFS_ASSERT (!CYCLIC_EMPTY(dep_tab_index));
+ CYCLIC_MOVE_HEAD(dep_window_index);
+
+ adjust_play_window(busy_flag, &poll_timeout);
+
+ start_profile (&total_profile);
+ while (!CYCLIC_EMPTY(dep_tab_index)) {
+ end_profile(&total_profile);
+ if ((total_profile.in.tv_sec - last_print_time >= 10)) {
+ int i;
+
+ last_print_time = total_profile.in.tv_sec;
+ fprintf (stdout, ">>>> sendng thread: time %d send_num %d recv_num %d timeout_num %d num_out_reqs %d can_not_catch_speed_num %d PLAY_SCALE %d \n", total_profile.in.tv_sec, send_num, recv_num, timeout_num, num_out_reqs, can_not_catch_speed_num, PLAY_SCALE);
+ for (i=0; i<=MAX_OUTSTANDING_REQ; i++) {
+ if (num_out_reqs_statistics[i]!=0) {
+ printf("num_out_req[%d]=%d,", i, num_out_reqs_statistics[i]);
+ num_out_reqs_statistics[i]=0;
+ }
+ }
+ printf("\n");
+ for (i=0; i<=MAX_OUTSTANDING_REQ; i++) {
+ if (num_out_reqs_statistics_at_timeout[i]!=0) {
+ printf("num_out_req_at_timeout[%d]=%d,", i, num_out_reqs_statistics_at_timeout[i]);
+ num_out_reqs_statistics_at_timeout[i]=0;
+ }
+ }
+ printf("\n");
+ // CYCLIC_PRINT(dep_tab_index);
+ }
+
+ if ((total_profile.in.tv_sec > 6000)) {
+ printf ("the process has run for more than 600 seconds, exit\n");
+ goto END;
+ }
+
+ if (busy_flag == IDLE) {
+#ifndef RECV_THREAD
+ //start_profile (&check_timeout_profile);
+ check_timeout();
+ //end_profile (&check_timeout_profile);
+#endif
+#ifndef IO_THREAD
+ if (disk_io_status!=TRACE_FILE_END) {
+ disk_io_status = read_trace();
+ };
+#endif
+ }
+
+ //start_profile (&adjust_play_window_profile);
+ adjust_play_window (busy_flag, &poll_timeout);
+ if (rfs_debug)
+ printf("num_out_reqs %d\n", num_out_reqs);
+ num_out_reqs_statistics[num_out_reqs]++;
+ busy_flag = IDLE;
+ //end_profile (&adjust_play_window_profile);
+
+ start_profile (&execute_next_request_profile);
+ while (execute_next_request()!=-1) {
+ busy_flag = BUSY;
+ }
+ end_profile (&execute_next_request_profile);
+
+#ifndef RECV_THREAD
+ start_profile (&receive_next_reply_profile);
+ /* actually the performance of two policy seems to be same */
+//#define SEND_HIGHER_PRIORITY_POLICY
+#define SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+
+#ifdef SEND_HIGHER_PRIORITY_POLICY
+ receive_next_reply(IDLE);
+#endif
+#ifdef SEND_RECEIVE_EQUAL_PRIORITY_POLICY
+ busy_flag = IDLE;
+ while (receive_next_reply(busy_flag)!=-1)
+ busy_flag = BUSY;
+#endif
+ end_profile (&receive_next_reply_profile);
+#endif
+ CYCLIC_ASSERT (0);
+ }
+ end_profile (&total_profile);
+
+ RFS_ASSERT (disk_io_status == TRACE_FILE_END);
+ if (num_out_reqs !=0 ) {
+ printf ("num_out_reqs %d\n", num_out_reqs);
+ CYCLIC_PRINT(dep_tab_index);
+ }
+ RFS_ASSERT(num_out_reqs==0);
+
+END:
+ printf ("trace starttime %d, trace_end_time %d trace_duration %d\n", trace_timestamp1, trace_timestamp2,
+ trace_timestamp2 - trace_timestamp1);
+ printf ("can_not_catch_speed_num_total %d can_not_catch_speed_num_last_10_seconds %d",
+ can_not_catch_speed_num_total, can_not_catch_speed_num);
+ printf ("total_profile.about: %s\n", total_profile.about);
+ print_profile ("total_profile", &total_profile);
+ printf("\n");
+ //print_profile ("check_timeout", &check_timeout_profile);
+ //printf("\n");
+ //print_profile ("adjust_play_window", &adjust_play_window_profile);
+ //printf("\n");
+ print_profile ("execute_next_request_profile", &execute_next_request_profile);
+ print_profile ("valid_get_nextop_profile", &valid_get_nextop_profile);
+ print_profile ("invalid_get_nextop_profile", &invalid_get_nextop_profile);
+ print_profile ("prepare_argument_profile", &prepare_argument_profile);
+ print_profile ("biod_clnt_call_profile", &biod_clnt_call_profile);
+ printf("\n");
+ print_profile ("receive_next_reply_profile", &receive_next_reply_profile);
+ print_profile ("valid_poll_and_get_reply_profile", &valid_poll_and_get_reply_profile);
+ print_profile ("invalid_poll_and_get_reply_profile", &invalid_poll_and_get_reply_profile);
+ print_profile ("decode_reply_profile", &decode_reply_profile);
+ print_profile ("check_reply_profile", &check_reply_profile);
+ print_profile ("fgets_profile", &fgets_profile);
+ print_profile ("read_line_profile", &read_line_profile);
+ print_profile ("read_trace_profile", &read_trace_profile);
+ //print_profile ("add_create_object", &add_create_object_profile);
+ printf("\n");
+
+ printf ("dep_tab_index.tail %d dep_tab_index.head %d num_out_reqs %d\n", dep_tab_index.tail, dep_tab_index.head, num_out_reqs);
+}
+
+
+int CYCLIC_SET_TAIL_TO(cyclic_index_t * index, int dest)
+{
+ cyclic_index_t indextmp, indextmp2;
+ int oldnum, num;
+ indextmp = *index;
+ indextmp2 = indextmp;
+ oldnum = CYCLIC_NUM(indextmp);
+
+ if (! ((dest>=0) && (dest<indextmp.size))) {
+ CYCLIC_PRINT(indextmp);
+ printf("dest %d\n", dest);
+ }
+ RFS_ASSERT ((dest>=0) && (dest<indextmp.size));
+ index->tail = dest;
+ indextmp2.tail = dest;
+ num = CYCLIC_NUM(indextmp2);
+
+ if (num > oldnum) {
+ CYCLIC_PRINT(indextmp);
+ CYCLIC_PRINT(indextmp2);
+ printf("dest %d old_num %d num %d\n", dest, oldnum, num);
+ }
+ RFS_ASSERT (num <= oldnum);
+}
+
+int flush_junk()
+{
+ int i;
+ for (i=0; i<500; i++) {
+ printf ("*************************************************************\n");
+ }
+ fflush(stdout);
+}
+
+int CYCLIC_ASSERT (int i)
+{
+ int j;
+ if (!(dep_tab_index.tail == dep_window_index.tail)) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+
+ if (!((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ) )) {
+ printf("%s head %d tail %d, size %d\n", dep_tab_index.name, dep_tab_index.head, dep_tab_index.tail, dep_tab_index.size);
+ printf("%s head %d tail %d, size %d\n", dep_window_index.name, dep_window_index.head, dep_window_index.tail, dep_window_index.size);
+ printf("pos %d\n", i);
+ flush_junk();
+ sleep (10);
+ RFS_ASSERT (0);
+ };
+ for (i=0, j=0; i<max_biod_reqs; i++) {
+ if (biod_reqp[i].in_use == 1)
+ j++;
+ }
+#ifndef RECV_THREAD
+ RFS_ASSERT (num_out_reqs==j);
+#endif
+/*
+ RFS_ASSERT ((dep_window_index.head == dep_tab_index.head) ||
+ CYCLIC_LESS(dep_tab_index, dep_window_index.head, dep_tab_index.head ));
+*/
+}
+
+/* sfs_c_chd.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_clkSid[] = "@(#)sfs_c_clk.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_clk.c ---------------------
+ *
+ * Clock handling. Routines to read the clock, measure elapsed
+ * time and sleep for a timed interval.
+ *
+ *.Exported_Routines
+ * void sfs_gettime(struct ladtime *)
+ * void sfs_elapsedtime(sfs_op_type *, struct ladtime *,
+ * struct ladtime *)
+ * int msec_sleep(int)
+ *
+ *.Local_Routines
+ * None.
+ *
+ *.Revision_History
+ * 05-Jan-92 Pawlowski Added raw data dump hooks.
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+
+#include <sys/signal.h>
+
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+#if !(defined(USE_GETTIMEOFDAY) || defined(USE_TIMES))
+#define USE_GETTIMEOFDAY
+#endif
+
+#define CLK_RESOLUTION 10 /* clock resolution in msec */
+
+/*
+ * ------------------------- Clock Handling -------------------------
+ */
+
+#if defined(USE_GETTIMEOFDAY)
+/*
+ * The time that the test first starts, all times are relative to this
+ * value to prevent integer overflows.
+ */
+extern struct ladtime test_start = {0, 0, 0};
+
+/*
+ * Get the time of day, offset by 'tz_ptr', and return it in 'time_ptr'.
+ * This is a local time of day interface to allow support for system
+ * dependent clock resolution.
+ */
+void
+sfs_gettime(
+ struct ladtime *time_ptr)
+{
+ time_t t;
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, NULL);
+ /*
+ * Use standard time function to get epoch time since 1970 for
+ * setattr/create.
+ */
+ t = time((time_t *)NULL);
+
+ if (test_start.sec == 0 && test_start.usec == 0) {
+ test_start.sec = tv.tv_sec;
+ test_start.usec = 0;
+ }
+ time_ptr->sec = tv.tv_sec - test_start.sec;
+ time_ptr->usec = tv.tv_usec;
+
+ time_ptr->esec = (int32_t)t;
+} /* sfs_gettime */
+#endif /* USE_GETTIMEOFDAY */
+
+#if defined(USE_TIMES)
+static uint32_t test_start = 0;
+
+void
+sfs_gettime(
+ struct ladtime *time_ptr)
+{
+ time_t t;
+ uint32_t clock_ticks;
+ struct tms tms;
+ int32_t ticks_per_sec = 100;
+
+ /*
+ * Try use possible conversions from least accurate to most accurate
+ */
+#if defined(HZ)
+ ticks_per_sec = HZ;
+#endif /* HZ */
+#if defined(CLK_TCK)
+ ticks_per_sec = CLK_TCK;
+#endif /* CLK_TCK */
+#if defined(_SC_CLK_TCK)
+ ticks_per_sec = sysconf(_SC_CLK_TCK);
+#endif /* _SC_CLK_TCK */
+
+ if ((clock_ticks = (uint32_t)times(&tms)) == -1) {
+ (void) fprintf(stderr, "%s: can't get time of day from system: %s\n",
+ sfs_Myname, strerror(errno));
+ (void) generic_kill(0, SIGINT);
+ exit(175);
+ }
+
+ /*
+ * Use standard time function to get epoch time since 1970 for
+ * setattr/create.
+ */
+ t = time((time_t *)NULL);
+
+ if (test_start == 0) {
+ test_start = clock_ticks;
+ }
+
+ time_ptr->sec = (clock_ticks - test_start) / ticks_per_sec;
+ time_ptr->usec = ((clock_ticks - test_start) -
+ (time_ptr->sec * ticks_per_sec)) *
+ (1000000/ticks_per_sec);
+ time_ptr->esec = (int32_t)t;
+}
+#endif /* USE_TIMES */
+
+/*
+ * Compute the elapsed time between 'stop_ptr' and 'start_ptr', and
+ * add the result to the time acculated for operation 'op_ptr'.
+ */
+void
+sfs_elapsedtime(
+ sfs_op_type * op_ptr,
+ struct ladtime * start_ptr,
+ struct ladtime * stop_ptr)
+{
+ double msec2;
+ struct ladtime time_ptr1, time_ptr2;
+
+ /* get the elapsed time */
+ if (stop_ptr->usec >= 1000000) {
+ stop_ptr->sec += (stop_ptr->usec / 1000000);
+ stop_ptr->usec %= 1000000;
+ }
+
+ if (stop_ptr->usec < start_ptr->usec) {
+ stop_ptr->usec += 1000000;
+ stop_ptr->sec--;
+ }
+
+ if (stop_ptr->sec < start_ptr->sec) {
+ stop_ptr->sec = 0;
+ stop_ptr->usec = 0;;
+ } else {
+ stop_ptr->usec -= start_ptr->usec;
+ stop_ptr->sec -= start_ptr->sec;
+ }
+
+ /* count ops that take zero time */
+ if ((stop_ptr->sec == 0) && (stop_ptr->usec == 0))
+ op_ptr->results.fast_calls++;
+
+ /* add the elapsed time to the total time for this operation */
+
+ time_ptr1 = op_ptr->results.time;
+ time_ptr2 = *stop_ptr;
+
+ ADDTIME(time_ptr1, time_ptr2);
+
+ op_ptr->results.time = time_ptr1;
+ stop_ptr = &time_ptr2;
+
+ /* square the elapsed time */
+ msec2 = (stop_ptr->sec * 1000.0) + (stop_ptr->usec / 1000.0);
+ msec2 *= msec2;
+
+ /* add the squared elapsed time to the total of squared elapsed time */
+ op_ptr->results.msec2 += msec2;
+
+ /*
+ * Log this point if logging is on. The (op_ptr - Ops)
+ * calculation is a baroque way of deriving the "NFS Operation
+ * code" we just executed--given available information.
+ *
+ * stop_ptr at this time contains the *elapsed* time, or
+ * response time of the operation.
+ */
+ log_dump(start_ptr, stop_ptr, op_ptr - Ops);
+
+} /* sfs_elapsedtime */
+
+long cumulative_resets;
+long cumulative_adjusts;
+long msec_calls;
+/*
+ * Use select to sleep for 'msec' milliseconds.
+ * Granularity is CLK_RESOLUTION msec.
+ * Return the amount we actually slept.
+ */
+int
+msec_sleep(
+ int msecs)
+{
+ int actual_msecs;
+ static long cumulative_error_msecs = 0;
+ int select_msecs;
+ struct timeval sleeptime;
+ int Saveerrno;
+ struct ladtime start;
+ struct ladtime end;
+
+ sfs_gettime(&start);
+
+ select_msecs = msecs + cumulative_error_msecs;
+ if (select_msecs < 0) {
+ sleeptime.tv_sec = 0;
+ sleeptime.tv_usec = 0;
+ } else {
+ sleeptime.tv_sec = select_msecs / 1000;
+ sleeptime.tv_usec = (select_msecs % 1000) * 1000;
+ }
+
+ /* sleep */
+ if (select(0, NULL, NULL, NULL, &sleeptime) == -1) {
+ if (errno != EINTR) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: select failed ",
+ sfs_Myname);
+ errno = Saveerrno;
+ perror("select");
+ (void) generic_kill(0, SIGINT);
+ exit(176);
+ }
+ }
+ sfs_gettime(&end);
+
+ SUBTIME(end, start);
+ actual_msecs = ((end.sec * 1000) + (end.usec / 1000));
+
+ cumulative_error_msecs += (msecs - actual_msecs);
+ if(cumulative_error_msecs > 100 ||
+ cumulative_error_msecs < -100) /* Clip tops */
+ {
+ cumulative_error_msecs = 0;
+ cumulative_resets++;
+ }
+ else
+ {
+ if(cumulative_error_msecs != 0)
+ cumulative_adjusts++;
+ }
+ msec_calls++;
+ return actual_msecs;
+} /* msec_sleep */
+
+
+/* sfs_c_clk.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_clntSid[] = "@(#)sfs_c_clnt.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+#if !defined(_XOPEN_SOURCE)
+#include <sys/socket.h>
+#else
+#define AF_INET 2
+#endif
+
+CLIENT *
+lad_clnt_create(int prot, struct hostent *hostent, uint32_t program,
+ uint32_t version, int sock, struct timeval *wait)
+{
+ struct sockaddr_in sin;
+ CLIENT *client_ptr;
+ uint_t sendsz = 0;
+ uint_t recvsz = 0;
+
+ /* set up the socket address for the remote call */
+ (void) memset((char *) &sin, '\0', sizeof(sin));
+ (void) memmove((char *) &sin.sin_addr,
+ hostent->h_addr,
+ hostent->h_length);
+ sin.sin_family = AF_INET;
+
+ switch (prot) {
+ case 0: /* UDP */
+ if (sendsz == 0)
+ client_ptr = sfs_cudp_create(&sin, program, version,
+ *wait, &sock);
+ else
+ client_ptr = sfs_cudp_bufcreate(&sin, program, version,
+ *wait, &sock,
+ sendsz, recvsz);
+ break;
+ case 1: /* TCP */
+ sendsz = NFS_MAXDATA + 1024;
+ recvsz = NFS_MAXDATA + 1024;
+
+ client_ptr = sfs_ctcp_create(&sin, program, version, &sock,
+ sendsz, recvsz);
+ break;
+ }
+
+ if (client_ptr == ((CLIENT *) NULL)) {
+ char buf[128];
+
+ (void) sprintf(buf, "%s: server not responding",
+ sfs_Myname);
+ clnt_pcreateerror(buf);
+ return((CLIENT *) NULL);
+ }
+
+ return (client_ptr);
+}
--- /dev/null
+#ifndef lint
+static char sfs_c_datSid[] = "@(#)sfs_c_dat.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ *
+ * ------------------------- sfs_c_dat.c --------------------------
+ *
+ * Space declarations for sfs.
+ *
+ *.Revision_History
+ * 11-Jul-94 ChakChung Ng Add codes for NFS/v3
+ * 24-Aug-92 Wittle New file set access.
+ * 05-Jan-92 Pawlowski Added hooks for raw data dump.
+ * 04-Dec-91 Bruce Keith Include string.h for SYSV/SVR4.
+ * 17-May-90 Richard Bean Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+
+/*
+ * ------------------------- NFS Operations -------------------------
+ */
+
+/*
+ * RPC timeout values by call type.
+ * Index'ed by #defines in sfs_def.h.
+ */
+struct timeval *Nfs_timers;
+
+struct timeval Nfs_udp_timers[] = {
+
+ /* secs usecs */
+ { 10, 0 }, /* All commands during initialization phase */
+ { 1, 0 }, /* Lookup during warmup and test run phases */
+ { 2, 0 }, /* Read during warmup and test run phases */
+ { 3, 0 }, /* Write during warmup and test run phases */
+};
+
+/*
+ * TCP is a "reliable" protocol so timeouts should never occur. Set
+ * the values to be large enough to ensure all servers will respond
+ * but not too large so a broken implementation will still complete.
+ */
+struct timeval Nfs_tcp_timers[] = {
+
+ /* secs usecs */
+ { 60, 0 }, /* All commands during initialization phase */
+ { 60, 0 }, /* Lookup during warmup and test run phases */
+ { 60, 0 }, /* Read during warmup and test run phases */
+ { 60, 0 }, /* Write during warmup and test run phases */
+};
+
+/*
+ * ----------------------- Transfer Size Distribution -----------------------
+ *
+ * Over-the-wire transfer sizes are divided into 2 cases: read and write.
+ * For each case, a percentile rank determines the basic size unit of the
+ * transfer which is multiplied by a count to give the total size for the
+ * percentile.
+ *
+ * The block transfer size distribution is specified by a table of values.
+ * The first column gives the percent of operations that will be a
+ * specific block transfer size. The second column gives the number of
+ * blocks units that will be transferred. Normally the block unit size
+ * is 8KB. The third column is a boolean specifying whether a trailing
+ * fragment block should be transferred. The fragment size for each transfer
+ * is a random multiple of 1 KB, up to the block size - 1 KB. Two tables
+ * are needed, one for read operation and one for write operations. The
+ * following table gives the default distributions.
+ *
+ * Read - Default Block Transfer Size Distribution Table
+ * percent block count fragment resulting transfer (8KB block size)
+ * ------- ----------- -------- -----------------------------------
+ * 0 0 0 0% 1 - 7 KB
+ * 85 1 0 85% 9 - 15 KB
+ * 8 2 1 8% 17 - 23 KB
+ * 4 4 1 4% 33 - 39 KB
+ * 2 8 1 2% 65 - 71 KB
+ * 1 16 1 1% 129 - 135 KB
+ *
+ * Write - Default Block Transfer Size Distribution Table
+ * percent block count fragment resulting transfer (8KB block size)
+ * ------- ----------- -------- -----------------------------------
+ * 49 0 1 49% 1 - 7 KB
+ * 36 1 1 36% 9 - 15 KB
+ * 8 2 1 8% 17 - 23 KB
+ * 4 4 1 4% 33 - 39 KB
+ * 2 8 1 2% 65 - 71 KB
+ * 1 16 1 1% 129 - 135 KB
+ *
+ * The user may specify a a different distribution by using the '-b' option.
+ * The format for the block size distribution file consists of the first
+ * three columns given above: percent, block count, and fragment. Read
+ * and write distribution tables are identified by the keywords "Read" and
+ * "Write". An example input file, using the default values, is given below:
+ *
+ * Read
+ * 0 0 0
+ * 85 1 0
+ * 8 2 1
+ * 4 4 1
+ * 2 8 1
+ * 1 16 1
+ * Write
+ * 49 0 1
+ * 36 1 1
+ * 8 2 1
+ * 4 4 1
+ * 2 8 1
+ * 1 16 1
+ *
+ * A second parameter controlled by the block transfer size distribution table
+ * is the network transport packet size. The distribution tables define the
+ * relative proportion of full blocks packets to fragment packets. For
+ * instance, the default tables have been constructed to produce a specific
+ * distribution of ethernet packet sizes for i/o operations by controlling
+ * the amount of data in each packet. The write packets produced consist
+ * of 50% 8-KB packets, and 50% 1-7 KB packets. The read packets consist
+ * of 85% 8-KB packets, and 15% 1-7 KB packets. These figures are
+ * determined by multiplying the percentage for the type of transfer by
+ * the number of blocks and fragments generated, and adding the totals.
+ * These conmputations are performed below for the default block size
+ * distribution tables:
+ *
+ * Read blocks fragments
+ * 0 0 0 0 0
+ * 85 1 0 85 0
+ * 8 2 1 16 8
+ * 4 4 1 16 4
+ * 2 8 1 16 2
+ * 1 16 1 16 1
+ * --- ---
+ * 149 (90%) 15 (10%)
+ *
+ * Write
+ * 49 0 1 0 49
+ * 36 1 1 36 36
+ * 8 2 1 16 8
+ * 4 4 1 16 4
+ * 2 8 1 16 2
+ * 1 16 1 16 1
+ * --- ---
+ * 100 (50%) 100 (50%)
+ *
+ *
+ */
+static sfs_io_op_dist_type Default_read_size_dist[] = {
+ /* percentile 8KB xfers fragments */
+ { 0, 0, 0 }, /* 0% 1 - 7 KB */
+ { 85, 1, 0 }, /* 85% 8 KB */
+ { 93, 2, 1 }, /* 8% 17 - 23 KB */
+ { 97, 4, 1 }, /* 4% 33 - 39 KB */
+ { 99, 8, 1 }, /* 2% 65 - 71 KB */
+ { 100, 16, 1 }, /* 1% 129 - 135 KB */
+};
+
+static sfs_io_op_dist_type Default_write_size_dist[] = {
+ /* percentile 8KB xfers fragments */
+ { 49, 0, 1 }, /* 49% 1 - 7 KB */
+ { 85, 1, 1 }, /* 36% 9 - 15 KB */
+ { 93, 2, 1 }, /* 8% 17 - 23 KB */
+ { 97, 4, 1 }, /* 4% 33 - 39 KB */
+ { 99, 8, 1 }, /* 2% 65 - 71 KB */
+ { 100, 16, 1 }, /* 1% 129 - 135 KB */
+};
+
+static sfs_io_dist_type Default_io_dist = {
+ Default_read_size_dist, /* read size distribution */
+ Default_write_size_dist, /* write size distribution */
+ 17, /* max file size in Block_size units */
+ 1.64, /* average read ops / request */
+ 2 /* average write ops / request */
+};
+
+sfs_io_file_size_dist Default_file_size_dist[] = {
+ /* percentage KB size */
+ { 33, 1}, /* 33% */
+ { 54, 2}, /* 21% */
+ { 67, 4}, /* 13% */
+ { 77, 8}, /* 10% */
+ { 85, 16}, /* 8% */
+ { 90, 32}, /* 5% */
+ { 94, 64}, /* 4% */
+ { 97, 128}, /* 3% */
+ { 99, 256}, /* 2% */
+ { 100, 1024}, /* 1% */
+ { 0, 0}
+};
+
+/*
+ * ------------------------- Remote File Information -------------------------
+ */
+int Num_io_file_sizes; /* # of different size of files */
+int Num_io_files; /* # of files used for i/o */
+int Num_non_io_files; /* # of non-i/o regular files */
+int Num_dirs; /* # of pre-created directories */
+int Num_dir_files; /* # of directories */
+int Num_symlinks; /* # of pre-created symlinks */
+int Num_symlink_files; /* # of symlinks */
+
+int Num_working_io_files; /* # of i/o files in working set */
+int Num_working_non_io_files; /* # of non i/o files in working set */
+int Num_working_dirs; /* # of dirs in working set */
+int Num_working_symlinks; /* # of symlinks in working set */
+int files_per_megabyte; /* # of files created of each MB */
+
+
+sfs_io_dist_type *Io_dist_ptr= /* block transfer distribution info */
+ &Default_io_dist;
+
+sfs_fh_type *Io_files; /* list of i/o files */
+sfs_fh_type *Non_io_files; /* list of non-i/o files */
+sfs_fh_type *Dirs; /* list of directories */
+sfs_fh_type *Symlinks; /* list of symlinks */
+
+sfs_fh_type *Cur_file_ptr; /* current file */
+char Cur_filename[SFS_MAXNAMLEN]; /* current dir entry name */
+char Filespec[SFS_MAXNAMLEN] /* sprintf spec for file names */
+ = "file_en.%05d";
+char Dirspec[SFS_MAXNAMLEN] /* sprintf spec for directory names */
+ = "dir_ent.%05d";
+char Symspec[SFS_MAXNAMLEN] /* sprintf spec for symlink names */
+ = "symlink.%05d";
+
+/*
+ * ------------------------- Run Parameters -------------------------
+ */
+
+int nfs_version;
+sfs_phase_type Current_test_phase; /* current phase of the test */
+
+sfs_fh_type Export_dir; /* filehandle for exported fs */
+CLIENT * NFS_client; /* RPC client handle */
+CLIENT * NFS_client_recv; /* RPC client handle used for recv_thread */
+
+bool_t Timed_run = TRUE; /* Timed run or call target ? */
+int Runtime = DEFAULT_RUNTIME; /* seconds in benchmark run */
+int Warmuptime = DEFAULT_WARMUP; /* seconds to warmup */
+int Access_percent = DEFAULT_ACCESS; /* % of file set to access */
+int Append_percent = DEFAULT_APPEND; /* % of writes that append */
+int Fss_delta_percent = DEFAULT_DELTA_FSS; /* allowed change to file set */
+int Kb_per_block = DEFAULT_KB_PER_BLOCK; /* i/o pkt block sz in KB */
+int Bytes_per_block = DEFAULT_KB_PER_BLOCK * 1024;/* i/o pkt sz in bytes */
+int Num_failed_lookup = DEFAULT_FAILED_LOOKUP; /* percent failed lookups */
+int Testop = -1; /* test mode operation number */
+int Interactive = 0; /* test synchronized by user*/
+int Populate_only = 0; /* populate test dirs & quit */
+int Debug_level = 0xFFFF; /* debugging switch */
+
+
+/*
+ * --------------------- Biod Simulation Variables ---------------------
+ */
+
+int Biod_max_outstanding_writes = DEFAULT_BIOD_MAX_WRITE;
+int Biod_max_outstanding_reads = DEFAULT_BIOD_MAX_READ;
+
+
+/*
+ * ------------------- File Set Size Control -------------------------
+ */
+
+int avg_bytes_per_file = 136*1024; /* calculated average file size */
+int Base_fss_bytes = 0; /* base file set size in bytes */
+int Most_fss_bytes = 0; /* most bytes ever in file set */
+int Least_fss_bytes = 0; /* least bytes ever in file set */
+int Limit_fss_bytes = 0; /* target upper limit on fileset size */
+int Cur_fss_bytes = 0; /* bytes currently in file set */
+int Total_fss_bytes = 0; /* Total bytes created */
+
+
+/*
+ * ------------- Per Child Load Generation Rate Variables -----------
+ */
+
+int Msec_per_period; /* total msec during the current run period */
+
+
+/*
+ * ------------------------- Miscellaneous -------------------------
+ */
+
+struct ladtime Cur_time; /* current time of day */
+struct ladtime Starttime; /* start of test */
+int start_run_phase = 0;
+
+char lad_hostname[HOSTNAME_LEN];
+/* sfs_c_dat.c */
--- /dev/null
+#ifndef _sfs_c_def_h
+#define _sfs_c_def_h
+
+#define RFS
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * @(#)sfs_c_def.h 2.1 97/10/23
+ *
+ * -------------------------- sfs_c_def.h -----------------------
+ *
+ * Literals and types for the sfs program.
+ *
+ *.Revision_History
+ * Wittle 24-Aug-92 - New file set access code.
+ * Bruce Keith 04-Dec-91 - Fix SVR4 definitions for NGROUPS.
+ * - Include param.h/limits.h as
+ * appropriate (BSD/SVR4).
+ * - Use bcopy, bzero, bcmp for BSD;
+ * memcpy, memset, memcmp for SYSV.
+ * Richard Bean 17-May-90 Created.
+ */
+
+#include <sys/stat.h>
+
+#include <limits.h>
+
+#if !defined(_XOPEN_SOURCE)
+/*
+ * Some non-XOPEN_compilent systems are missing these definitions
+ */
+#if !defined(SVR4) /* Assume BSD */
+#include <sys/param.h>
+#endif /* SVR4 */
+#endif /* !_XOPEN_SOURCE */
+
+#include <sys/time.h>
+
+#if defined(SVR4)
+#include <sys/select.h>
+#endif
+
+#include "rpc/rpc.h"
+
+#if (defined (SVR4) || defined (AIX))
+#include <netinet/in.h>
+#endif
+
+#include <netdb.h>
+
+#define SFS_MIN_RES 100 /* 100 usec resolution */
+#define SFS_MAXNAMLEN 32 /* max test file name length */
+#define SFS_MAXPATHLEN 1024 /* max test path name length */
+#define HOSTNAME_LEN 31 /* length of client's hostname */
+
+#include "sfs_c_nfs.h"
+
+/* -------------------------- Constants ----------------------- */
+
+#define SFS_VERSION_DATE "11 July 2001"
+#define SFS_VERSION_NUM "3.0"
+
+/*
+ * Debugging levels
+ */
+#define CHILD_TO_DEBUG 0 /* per child debugging uses child 0 */
+
+#define DEBUG_NEW_CODE (Debug_level & 0x00000001) /* 1 */
+#define DEBUG_PARENT_GENERAL (Debug_level & 0x00000002) /* 2 */
+#define DEBUG_PARENT_SIGNAL (Debug_level & 0x00000004) /* 3 */
+#define DEBUG_CHILD_ERROR (Debug_level & 0x00000008) /* 4 */
+#define DEBUG_CHILD_SIGNAL (Debug_level & 0x00000010) /* 5 */
+#define DEBUG_CHILD_XPOINT (Debug_level & 0x00000020) /* 6 */
+#define DEBUG_CHILD_GENERAL ((Debug_level & 0x00000040) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 7 */
+#define DEBUG_CHILD_OPS ((Debug_level & 0x00000080) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 8 */
+#define DEBUG_CHILD_FILES ((Debug_level & 0x00000100) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 9 */
+#define DEBUG_CHILD_RPC ((Debug_level & 0x00000200) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 10 */
+#define DEBUG_CHILD_TIMING ((Debug_level & 0x00000400) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 11 */
+#define DEBUG_CHILD_SETUP ((Debug_level & 0x00000800) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 12 */
+#define DEBUG_CHILD_FIT ((Debug_level & 0x00001000) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 13 */
+#define DEBUG_CHILD_BIOD ((Debug_level & 0x00002000) && \
+ (Child_num == CHILD_TO_DEBUG)) /* 14 */
+
+
+/*
+ * General constants for benchmark
+ */
+#define DEFAULT_LOAD 60 /* calls per sec */
+#define DEFAULT_CALLS 5000 /* number of calls */
+#define DEFAULT_NPROCS 4 /* number of children to run */
+#define DEFAULT_RUNTIME (5 * 60) /* runtime in seconds */
+#define DEFAULT_WARMUP (5 * 60) /* warmup time in seconds */
+#define DEFAULT_ACCESS 10 /* % of file set accessed */
+#define DEFAULT_APPEND 70 /* % of writes that append */
+#define DEFAULT_DELTA_FSS 10 /* % change to file set size */
+#define DEFAULT_KB_PER_BLOCK 8 /* block xfer size in KB */
+#define DEFAULT_BYTES_PER_OP (10*1024*1024) /* bytes per unit load */
+#define DEFAULT_NFILES 100 /* # of regular NON-IO files */
+#define DEFAULT_FILES_PER_DIR 30 /* # of files per directories */
+#define DEFAULT_NSYMLINKS 20 /* # of symlinks */
+#define DEFAULT_FAILED_LOOKUP 35 /* # of failed lookups */
+
+#define DEFAULT_WARM_RATE_CHECK 2 /* check progress each 2 secs */
+#define DEFAULT_RUN_RATE_CHECK 10 /* check progress each 10 sec */
+#define DEFAULT_MAX_BUFSIZE NFS_MAXDATA /* max buffer size for i/o */
+
+#define DEFAULT_BIOD_MAX_WRITE 2 /* max outstanding biod write */
+#define DEFAULT_BIOD_MAX_READ 2 /* max outstanding biod reads */
+#define MAX_BIODS 32
+
+
+/*
+ * For now we only read a fixed number of files from a directory. Ideally
+ * we would like to read a random number from 0-MAX but we will need a new
+ * workload.
+ */
+#define SFS_MAXDIRENTS 128
+
+/*
+ * If you change the default Chi_Sqr value,
+ * then also change the field label in the results print out.
+ */
+#define DEFAULT_CHI_SQR_CI CHI_SQR_95 /* chi-sqr value to use */
+#define CHI_SQR_90 2.71 /* chi-sqr value for 90% CI */
+#define CHI_SQR_95 3.84 /* chi-sqr value for 95% CI */
+#define CHI_SQR_975 5.02 /* chi-sqr value for 97.5% CI */
+#define CHI_SQR_99 6.63 /* chi-sqr value for 99% CI */
+#define CHI_SQR_995 7.88 /* chi-sqr value for 99.5% CI */
+
+/*
+ * NFS operation numbers
+ */
+#define NULLCALL 0
+#define GETATTR 1
+#define SETATTR 2
+#define ROOT 3
+#define LOOKUP 4
+#define READLINK 5
+#define READ 6
+#define WRCACHE 7
+#define WRITE 8
+#define CREATE 9
+#define REMOVE 10
+#define RENAME 11
+#define LINK 12
+#define SYMLINK 13
+#define MKDIR 14
+#define RMDIR 15
+#define READDIR 16
+#define FSSTAT 17
+#define ACCESS 18
+#define COMMIT 19
+#define FSINFO 20
+#define MKNOD 21
+#define PATHCONF 22
+#define READDIRPLUS 23
+#define NOPS (READDIRPLUS+1) /* number of NFS ops */
+#define TOTAL NOPS /* slot for totals */
+
+/*
+ * Constants for i/o distribution table
+ */
+#define IO_DIST_START 0
+#define IO_DIST_READ 1
+#define IO_DIST_WRITE 2
+
+/*
+ * Ratio of non_io_files that are initialized
+ * NOTE: initializing half the non-i/o files works ok with the
+ * default op mix. If the mix is changed affecting the
+ * ratio of creations to removes, there may not be enough
+ * empty slots for file creation (or there may not be
+ * enough created during initialization to handle a lot of
+ * removes that occur early in the test run), and this would
+ * cause do_op() to fail to find a file appropriate for the
+ * chosen op. This will result in the global variable
+ * Ops[op].no_calls being incremented (turn on child level
+ * debugging to check this count), and the do_op() local
+ * variable aborted_ops to be incremented and checked during
+ * runtime for too many failures.
+ */
+#define RATIO_NON_IO_INIT 0.5
+
+/*
+ *---------------------------- TCP stuff ---------------------
+ */
+
+#define TCPBUFSIZE 1024 * 32 + 200
+
+/* -------------------------- Macros ----------------------- */
+
+struct ladtime {
+ uint32_t sec; /* seconds */
+ uint32_t usec; /* and microseconds */
+ uint32_t esec; /* seconds since standard epoch */
+};
+
+#define SUBTIME(t1, t2) { \
+ if ((t1.sec < t2.sec) || \
+ ((t1.sec == t2.sec) && (t1.usec < t2.usec))) { \
+ t1.sec = t1.usec = 0 ; \
+ } else { \
+ if (t1.usec < t2.usec) { \
+ t1.usec += 1000000; \
+ t1.sec--; \
+ } \
+ t1.usec -= t2.usec; \
+ t1.sec -= t2.sec; \
+ } \
+}
+
+#define ADDTIME(t1, t2) {if ((t1.usec += t2.usec) >= 1000000) {\
+ t1.sec += (t1.usec / 1000000); \
+ t1.usec %= 1000000; \
+ } \
+ t1.sec += t2.sec; \
+ }
+
+#define MINIMUM(a, b) ((a < b) ? (a) : (b))
+#define MAXIMUM(a, b) ((a > b) ? (a) : (b))
+
+#define MULTIME(t1, s) { \
+ t1.usec *=s; \
+ t1.sec *=s; \
+ if (t1.usec >= 1000000) {\
+ t1.sec += (t1.usec/1000000); \
+ t1.usec %= 1000000; \
+ }\
+ }
+#define DIVTIME(t1, s) { \
+ t1.usec += (t1.sec % s ) *1000000; \
+ t1.sec /= s; \
+ t1.usec /= s; \
+ }
+#define LARGERTIME(t1, t2) \
+ ((t1.sec>t2.sec) || ((t1.sec==t2.sec) && (t1.usec>t2.usec)))
+
+/* -------------------------- Types ----------------------- */
+
+/*
+ * SFS test phases. Values are well-ordered for use of "<" operations.
+ */
+#define Mount_phase 1 /* test directories are being mounted */
+#define Populate_phase 2 /* files being created in the test directories */
+#define Warmup_phase 3 /* reach steady state (load is being generated) */
+#define Testrun_phase 4 /* timed test run (load is being generated) */
+#define Results_phase 5 /* results collection and reporting */
+typedef int sfs_phase_type;
+
+/*
+ * Index constants for lookups into the RPC timer array.
+ */
+#define Init 0
+#define Lookup 1
+#define Read 2
+#define Write 3
+
+/*
+ * SFS results information structure
+ */
+typedef struct {
+ int good_calls; /* successful calls */
+ int bad_calls; /* failed (timed out) calls */
+ int timeout_calls; /* RFS timeout calls */
+ int fast_calls; /* calls that competed in 0 time */
+ struct ladtime time; /* cumulative execution time */
+ double msec2; /* cumulative squared time - msecs**2 */
+} sfs_results_type;
+
+/*
+ * SFS information reported from child back to parent.
+ */
+typedef struct {
+ int version;
+ sfs_results_type results_buf[NOPS+1];
+ int total_fss_bytes;
+ int least_fss_bytes;
+ int most_fss_bytes;
+ int base_fss_bytes;
+} sfs_results_report_type;
+
+
+/*
+ * SFS operation information structure
+ */
+
+typedef struct {
+ char * name; /* operation name */
+ int mix_pcnt; /* percentage of call target */
+#ifndef RFS
+ int (*funct)(); /* op routine */
+#endif
+ int call_class; /* call class: client handle & timeo */
+ int target_calls; /* number of calls to make */
+ int no_calls; /* # of times a call couldn't be made */
+ double req_pcnt; /* cumulative request percentile */
+ int req_cnt; /* number of requests made */
+ int target_reqs; /* number of req to be made */
+ sfs_results_type results; /* test results */
+} sfs_op_type;
+
+
+/*
+ * Flags used with randfh
+ */
+#define RANDFH_TRUNC 0x01 /* pick a file to truncate */
+#define RANDFH_APPEND 0x02 /* pick a file to append to */
+
+/*
+ * SFS file information structure
+ * The particular values assiged are used to perform mod operations.
+ */
+#define Sfs_io_file 0 /* read, write ops only (0) */
+#define Sfs_non_io_file 1 /* other regular file ops only (1) */
+#define Sfs_symlink 2 /* symlink ops only (2) */
+#define Sfs_dir 3 /* dir ops only (3) */
+#define Sfs_regular 4 /* any regular file (0,1) */
+#define Sfs_non_dir 5 /* any non-directory file (0,1,2) */
+#define Sfs_any_file 6 /* any file, link, or dir (0,1,2,3) */
+typedef int sfs_file_type;
+
+#define Exists 1 /* op needs an object that already exists */
+#define Nonexistent 2 /* op will create an object */
+#define Empty_dir 3 /* op needs an empty directory */
+typedef char sfs_state_type;
+
+/*
+ * One file (dir, or symlink) in the file set.
+ */
+typedef struct sfs_fh_data {
+ union {
+ fhandle_t f_fh2; /* the NFS V2 file handle */
+ nfs_fh3 f_fh3; /* the NFS V3 file handle */
+ } sfs_fh_un;
+ union {
+ fattr a_attributes2; /* its V2 attributes */
+ fattr3 a_attributes3; /* its V3 attributes */
+ } sfs_fattr_un;
+ char file_name[SFS_MAXNAMLEN]; /* path component*/
+} sfs_fh_data;
+
+typedef struct sfs_fh_type {
+ struct sfs_fh_type *dir; /* Parent Directory */
+ struct sfs_fh_data *fh_data; /* Data area */
+ int size; /* its size */
+ int unique_num; /* unique part of filename */
+ int use_cnt; /* count of op to this file */
+ int xfer_cnt; /* count of KB xfered */
+ sfs_state_type state; /* existence state */
+ char working_set; /* is in the working set */
+ char initialize; /* should be initialized */
+#define attributes2 fh_data->sfs_fattr_un.a_attributes2
+#define attributes3 fh_data->sfs_fattr_un.a_attributes3
+#define fh2 fh_data->sfs_fh_un.f_fh2
+#define fh3 fh_data->sfs_fh_un.f_fh3
+#define file_name fh_data->file_name
+} sfs_fh_type;
+
+#define fh_size(fhptr) (nfs_version == NFS_VERSION ? \
+ (fhptr)->attributes2.size : \
+ (fhptr)->attributes3.size._p._l)
+
+#define fh_uid(fhptr) (nfs_version == NFS_VERSION ? \
+ (uint32_t)((fhptr)->attributes2.uid) : \
+ (uint32_t)((fhptr)->attributes3.uid))
+
+#define fh_gid(fhptr) (nfs_version == NFS_VERSION ? \
+ (uint32_t)((fhptr)->attributes2.gid) : \
+ (uint32_t)((fhptr)->attributes3.gid))
+
+#define fh_mode(fhptr) (nfs_version == NFS_VERSION ? \
+ (uint32_t)((fhptr)->attributes2.mode) : \
+ (uint32_t)((fhptr)->attributes3.mode))
+
+#define fh_isdir(fhptr) (nfs_version == NFS_VERSION ? \
+ ((fhptr)->attributes2.type == NFDIR) : \
+ ((fhptr)->attributes3.type == NF3DIR))
+
+#define fh_isfile(fhptr) (nfs_version == NFS_VERSION ? \
+ ((fhptr)->attributes2.type == NFREG) : \
+ ((fhptr)->attributes3.type == NF3REG))
+
+/*
+ * One file (dir, or symlink) in the working file set.
+ */
+typedef struct {
+ int range; /* random access range for this file */
+ int index; /* points into actual file array */
+} sfs_work_fh_type;
+
+typedef struct {
+ sfs_work_fh_type *entries; /* array of working files */
+ int access_group_size; /* # files in a group */
+ int access_group_cnt; /* # groups in workset */
+ int max_range; /* access range of workset */
+} sfs_work_set_type;
+
+
+
+/*
+ * SFS file size distribution structures
+ */
+typedef struct {
+ int pcnt; /* percentile */
+ int bufs; /* Block_size KB xfers */
+ int frags; /* boolean - is a frag present */
+} sfs_io_op_dist_type;
+
+typedef struct {
+ sfs_io_op_dist_type *read; /* read size table */
+ sfs_io_op_dist_type *write; /* write size table */
+ int max_bufs; /* max # of Block_size xfers */
+ double avg_ops_per_read_req; /* avg read ops/req */
+ double avg_ops_per_write_req; /* avg write ops/req */
+} sfs_io_dist_type;
+
+typedef struct {
+ int pcnt; /* percentile */
+ int size; /* file size in KB */
+} sfs_io_file_size_dist;
+
+
+/* sfs child processes synchronization logfile */
+#define CHILD_SYNC_LOG "/tmp/sfs_log"
+#define SFS_PNT_PID "/tmp/sfs_pnt_pid"
+#define SFS_PRM_PID "/tmp/sfs_prm_pid"
+#define SFS_SYNCD_PID "/tmp/sfs_syncd_pid"
+
+/*
+ * Values for invalid runs
+ */
+#define INVALID_UNKNOWN 1 /* Old value reserved as unknown */
+#define INVALID_IODIST 2
+#define INVALID_MIX 3
+#define INVALID_RUNTIME 4
+#define INVALID_ACCESS 5
+#define INVALID_APPEND 6
+#define INVALID_KB 7
+#define INVALID_NDIRS 8
+#define INVALID_FSS 9
+#define INVALID_BIODREAD 10
+#define INVALID_NSYMLINKS 11
+#define INVALID_BIODWRITE 12
+#define INVALID_WARMUP 13
+#define INVALID_GOODCALLS 14
+#define INVALID_FAILEDRPC 15
+#define INVALID_NOTMIX 16
+#define INVALID_MAX (INVALID_NOTMIX + 1)
+
+/*
+ * External variable declarations
+ */
+extern int Access_percent;
+extern int Append_percent;
+extern int Base_fss_bytes;
+extern int Biod_max_outstanding_reads;
+extern int Biod_max_outstanding_writes;
+extern int Bytes_per_block;
+extern int Child_num;
+extern int Client_num;
+extern sfs_fh_type *Cur_file_ptr;
+extern char Cur_filename[];
+extern gid_t Cur_gid;
+extern struct ladtime Cur_time;
+extern uid_t Cur_uid;
+extern sfs_phase_type Current_test_phase;
+extern int Debug_level;
+extern sfs_io_file_size_dist Default_file_size_dist[];
+extern sfs_fh_type *Dirs;
+extern uint32_t Dump_count;
+extern int dump_create_existing_file;
+extern int Dump_data;
+extern uint32_t Dump_length;
+extern uint32_t Dump_offset;
+extern int dump_truncate_op;
+extern sfs_fh_type Export_dir;
+extern char Filespec[];
+extern char Dirspec[];
+extern char Symspec[];
+extern int avg_bytes_per_file;
+extern int files_per_megabyte;
+extern int Fss_delta_percent;
+extern int Interactive;
+extern sfs_io_dist_type * Io_dist_ptr;
+extern sfs_fh_type *Io_files;
+extern int Kb_per_block;
+extern char * sfs_Myname;
+extern int Least_fss_bytes;
+extern int Limit_fss_bytes;
+extern int Cur_fss_bytes;
+extern int Total_fss_bytes;
+extern int Log_fd;
+extern char Logname[];
+extern int Most_fss_bytes;
+extern int Msec_per_period;
+extern CLIENT * NFS_client;
+extern CLIENT * NFS_client_recv;
+extern struct timeval *Nfs_timers;
+extern struct timeval Nfs_udp_timers[];
+extern struct timeval Nfs_tcp_timers[];
+extern int nfs_version;
+extern sfs_fh_type *Non_io_files;
+extern int Num_dirs;
+extern int Num_dir_files;
+extern int Num_failed_lookup;
+extern int Num_io_files;
+extern int Num_non_io_files;
+extern int Num_symlinks;
+extern int Num_symlink_files;
+extern int Num_working_dirs;
+extern int Num_working_io_files;
+extern int Num_working_non_io_files;
+extern int Num_working_symlinks;
+extern sfs_op_type *Ops;
+extern int Populate_only;
+extern char * Prime_client;
+extern uid_t Real_uid;
+extern int Runtime;
+extern struct ladtime Starttime;
+extern int start_run_phase;
+extern sfs_fh_type *Symlinks;
+extern int Tcp;
+extern int Testop;
+extern int Files_per_dir;
+extern int Tot_client_num_io_files;
+extern int Tot_client_num_non_io_files;
+extern int Tot_client_num_symlinks;
+extern int Timed_run;
+extern int Validate;
+extern int Warmuptime;
+extern char *invalid_str[];
+extern char lad_hostname[];
+
+
+/*
+ * External function declarations
+ */
+extern int biod_init(int, int);
+extern void biod_term(void);
+extern void biod_turn_on(void);
+extern int check_access(struct stat *);
+extern int check_fh_access(sfs_fh_type *);
+extern void child(int, int, float, int, char **);
+extern void generic_catcher(int);
+extern int generic_kill(int, int);
+extern void init_counters(void);
+extern void init_fileinfo(void);
+extern void init_mount_point(int, char *, CLIENT *);
+extern void init_ops(void);
+extern char * init_write_buffer(void);
+extern CLIENT * lad_getmnt_hand(char *);
+extern CLIENT * lad_clnt_create(int, struct hostent *, uint32_t,
+ uint32_t, int, struct timeval *);
+extern char * lad_timestamp(void);
+extern int set_debug_level(char *s);
+extern void sfs_alarm(int);
+extern void sfs_cleanup(int);
+extern void sfs_elapsedtime(sfs_op_type *, struct ladtime *,
+ struct ladtime *);
+extern void sfs_gettime(struct ladtime *);
+extern int32_t sfs_random(void);
+extern void sfs_srandom(int);
+extern int init_rand_range(int);
+extern int rand_range(int);
+extern void sfs_startup(int);
+extern void sfs_stop(int);
+extern void log_dump(struct ladtime *, struct ladtime *, int);
+extern void parent(int, int, char *, char *);
+extern void print_dump(int, int);
+extern sfs_fh_type *
+ randfh(int, int, uint_t, sfs_state_type, sfs_file_type);
+extern int msec_sleep(int);
+extern void Validate_ops(int, char **);
+
+/* Reliable RPC functions for initialization */
+extern int lad_lookup(sfs_fh_type *, char *);
+extern int lad_remove(sfs_fh_type *, char *);
+extern int lad_rmdir(sfs_fh_type *, char *);
+extern int lad_symlink(sfs_fh_type *, char *, char *);
+extern int lad_mkdir(sfs_fh_type *, char *);
+extern int lad_write(sfs_fh_type *, int32_t, int32_t);
+extern int lad_create(sfs_fh_type *, char *);
+extern int lad_truncate(sfs_fh_type *, int32_t);
+
+/* RFS: moved the definition from sfs_c_bio.c to here because it is used in
+ * sfs_c_chd.c in the new code, information associated with outstanding requests
+ */
+struct biod_req {
+ uint32_t xid; /* RPC transmission ID */
+ bool_t in_use; /* Indicates if the entry is in use */
+ int dep_tab_index; /* corresponding index in dep_tab */
+ unsigned int count; /* Count saved for Dump routines */
+ unsigned int offset; /* Offset saved for Dump routines */
+ struct ladtime start; /* Time RPC call was made */
+ struct ladtime stop; /* Time RPC reply was received */
+ struct ladtime timeout; /* Time RPC call will time out */
+};
+
+#endif /* sfs_def.h */
--- /dev/null
+#ifndef lint
+static char sfs_c_dmpSid[] = "@(#)sfs_c_dmp.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_dmp.c ---------------------
+ *
+ * Raw data dump routines for SFS.
+ *
+ * The routines contained here capture and dump the raw data
+ * points (operation, response time) to allow the researcher
+ * to perform detailed analysis on the response time characteristics
+ * of an NFS server.
+ *
+ *.Exported Routines
+ * void log_dump(ladtime *, ladtime *, int)
+ * void print_dump(int, int)
+ *
+ *.Local Routines
+ * None.
+ *
+ *.Revision_History
+ * 11-Jul-94 ChakChung Ng Add codes for NFS/v3
+ * 03-Feb-92 0.0.20 Pawlowski
+ * Use of "Current_test_phase"
+ * obviates need for dump init
+ * routine, so it has been deleted.
+ * 10-Jan-92 0.0.19 Teelucksingh
+ * Changed dump file names to be
+ * < 14 chars. Added header to
+ * output.
+ * 04-Jan-92 0.0.18 Pawlowski
+ * Added raw data dump code.
+ */
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+
+struct savedata {
+ struct savedata *next;
+ int32_t rtv_sec; /* The response time */
+ int32_t rtv_usec;
+ int32_t stv_sec; /* The start time */
+ int32_t stv_usec;
+ uint32_t count; /* Used only for read and write */
+ uint32_t offset; /* Used only for read and write */
+ uint32_t length; /* Used only for read and write */
+ uint16_t unique_num; /* unique id for file */
+ unsigned int flags; /* Things like whether truncating, etc. */
+ int16_t op;
+};
+
+uint32_t Dump_count; /* place to put read and write count */
+uint32_t Dump_offset; /* place to put r/w offset */
+uint32_t Dump_length; /* place to put r/w offset */
+int Dump_data; /* Flag set by command line option */
+int dump_create_existing_file = FALSE;
+int dump_truncate_op = FALSE;
+
+/*
+ * ---------------------- Static Declarations ----------------------
+ */
+
+static struct savedata *saveary = 0;
+
+/*
+ * -------------------------- Constants --------------------------
+ */
+
+/* flags bit values */
+#define CREATE_OF_EXISTING_FILE 0x0001
+#define TRUNCATE_OPERATION 0x0002
+
+/*
+ * ---------------------- Dump Routines ----------------------
+ */
+
+/*
+ * log_dump()
+ *
+ * This function is called on the completion of every operation
+ * to log the data point. We log the operation and the elapsed time
+ * (storing the microsecond component also).
+ *
+ * The data is kept in a singly linked list, elements dynamically
+ * allocated as needed.
+ *
+ * Dynamic allocation of elements using malloc and single link list
+ * pointer adds overhead to the storage space for the data for each
+ * point. Dynamic allocation can result in system calls to get more
+ * space for elements, adding to execution overhead. However, if you're
+ * doing a raw data dump run, you assume costs are negligible.
+ */
+
+void
+log_dump(
+ struct ladtime *start,
+ struct ladtime *elapsed,
+ int op)
+{
+ static struct savedata *saveptr = 0;
+
+ if (!Dump_data || Current_test_phase != Testrun_phase)
+ return;
+
+ if (saveary == (struct savedata *)NULL) {
+ if ((saveary = (struct savedata *)
+ malloc(sizeof(struct savedata)))
+ == (struct savedata *)NULL) {
+ (void) fprintf(stderr, "Unable to allocate dump element\n");
+ return;
+ }
+ saveptr = saveary;
+ } else {
+ if ((saveptr->next = (struct savedata *)
+ malloc(sizeof(struct savedata)))
+ == (struct savedata *)NULL) {
+ (void) fprintf(stderr, "Unable to allocate dump element\n");
+ return;
+ }
+ saveptr = saveptr->next;
+ }
+ saveptr->next = (struct savedata *)NULL;
+ saveptr->flags = 0;
+ saveptr->op = op;
+ saveptr->rtv_sec = elapsed->sec;
+ saveptr->rtv_usec = elapsed->usec;
+ saveptr->stv_sec = start->sec;
+ saveptr->stv_usec = start->usec;
+ saveptr->unique_num = Cur_file_ptr->unique_num;
+ if (op == NFSPROC3_READ || op == NFSPROC3_WRITE ||
+ op == NFSPROC_READ || op == NFSPROC_WRITE) {
+ saveptr->count = (uint16_t) Dump_count;
+ saveptr->offset = Dump_offset;
+ saveptr->length = Dump_length;
+ }
+
+ if (dump_create_existing_file == TRUE) {
+ saveptr->flags |= CREATE_OF_EXISTING_FILE;
+ dump_create_existing_file = FALSE;
+ }
+
+ if (dump_truncate_op == TRUE) {
+ saveptr->flags |= TRUNCATE_OPERATION;
+ dump_truncate_op = FALSE;
+ }
+}
+
+/*
+ * print_dump()
+ *
+ * Dumps the raw data to a file in the format:
+ *
+ * opcode sec.msec sec.msec file-unique-id <length> <offset> <count>
+ * opcode sec.msec sec.msec file-unique-id
+ * . . .
+ *
+ * The opcode is the numeric NFS operation code as defined in the
+ * NFS protocol specification. The first "sec.msec" field is the
+ * response time of the operation. The second "sec.msec" field
+ * is the start time of the operation. For read and write calls,
+ * the "length", "offset" and "count" from the operation arguments are put out
+ * as the fourth, fifth, and sixth field.
+ *
+ * This simple (x, y) pairing should be suitable input for a wide variety
+ * of plotting programs.
+ *
+ * Note that the raw data is precious! Several points to be observed:
+ *
+ * 1. The raw data for each individual child is dumped to
+ * its own data file. So each individual child process
+ * data can be inspected (possibly useful to debug client
+ * load generation per child process anomalies).
+ *
+ * 2. More importantly, each raw data dump file presents
+ * the operations generated by the child in their exact
+ * order of generation. This can be used to analyze possible
+ * time dependent behaviour of the server.
+ *
+ * Client process output files can be merged for analysis using cat(1).
+ *
+ * If any other data (additional fields) are added to raw data dump
+ * file, please add those fields after primary fields. awk(1) scripts
+ * and the like can be used to re-arrange data files, but it would
+ * be nice if the primary (x, y) data points are the default format.
+ */
+
+void
+print_dump(
+ int clientnum,
+ int childnum)
+{
+ char buf[256];
+ FILE *datap;
+ struct savedata *p = saveary;
+
+ buf[0] = 0;
+
+ if (!Dump_data)
+ return;
+
+ /*
+ * We write raw data files to the /tmp directory, and
+ * the manager will retrieve to the prime client.
+ *
+ * Removed preladraw prefix from file names to fit
+ * in 14 chars - K.T.
+ */
+
+ (void) sprintf(buf, "/tmp/c%3.3d-p%3.3d", clientnum, childnum);
+
+ if ((datap = fopen(buf, "w")) == NULL) {
+ (void) fprintf(stderr, "couldn't open %s for writing\n", buf);
+ return;
+ }
+
+ (void) fprintf(datap,"%s\n%s\n%s\n%s\n",
+"-----------------------------------------------------------------------------",
+" Op Response Start Unique File",
+" Code Time Time File Id Length Offset Size",
+"-----------------------------------------------------------------------------");
+
+ p = saveary;
+ while(p) {
+ (void) fprintf(datap, "%11s %8.3f %19.3f %8d",
+ Ops[p->op].name,
+ (p->rtv_sec * 1000.0) + (p->rtv_usec / 1000.0),
+ (p->stv_sec * 1000.0) + (p->stv_usec / 1000.0),
+ p->unique_num);
+ if (p->op == NFSPROC3_READ || p->op == NFSPROC3_WRITE ||
+ p->op == NFSPROC_READ || p->op == NFSPROC_WRITE) {
+ (void) fprintf(datap,
+ " %8d %8d %5d\n", p->length, p->offset, p->count);
+ }
+ else if (p->op == NFSPROC3_SETATTR || p->op == NFSPROC3_CREATE ||
+ p->op == NFSPROC_SETATTR || p->op == NFSPROC_CREATE) {
+ if (p->flags & TRUNCATE_OPERATION) {
+ (void) fprintf(datap, " %s", "TRUNCATE");
+ }
+ if (p->flags & CREATE_OF_EXISTING_FILE) {
+ (void) fprintf(datap, " %s", "CREATE_EXISTING");
+ }
+ (void) fprintf(datap, "\n");
+ }
+ else {
+ (void) fprintf(datap, "\n");
+ }
+ p = p->next;
+ }
+
+ (void) fprintf(datap,
+"-----------------------------------------------------------------------------\n\n");
+ (void) fclose(datap);
+}
--- /dev/null
+#ifndef lint
+static char sccsid_hp[] = "@(#)sfs_c_man.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*
+ * Generates an artifical NFS client load based on a given mix of operations,
+ * and block transfer distribution.
+ *
+ * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time]
+ * [-m mix_file] [-B block_size] [-b blocksz_file]
+ * [-f file_set_delta] [-a access_pnct]
+ * [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]
+ * [-d debug_level] [-i] [-P] [-T op_num]
+ * [-V validation_level] [-z] [-Q]
+ * [-R biod_reads] [-W biod_writes]
+ * [-M prime_client_hostname] [-N client_cnt]
+ *
+ * NOTE: REFER TO SFS MAN PAGE (sfs.1) FOR A DESCRIPTION OF ALL
+ * SFS OPTIONS
+ *
+ *
+ * Single Client Options
+ *
+ * option description default
+ * --------------- ------------------------------------------------ -----------
+ * -a access_pcnt % of file set to access 20% access
+ * -A append_pcnt % of writes that append rather than overwrite 70% append
+ * -b blocksz_file file specifying distribution of block xfer sizes (see below)
+ * -B block_size # of KB in block, up to 8 KB 8 KB
+ * -Q Do TCP connection for NFS rather than UDP off
+ * -d debug_level debug level (higher number gives more output) off
+ * -D dir_cnt # directories used for directory operations 20 dirs
+ * -f fileset_delta % change in file set size allowed 10%
+ * -F file_cnt # files used for read and write operations 100 files
+ * -i interactive; wait for input before starting test off
+ * -l load # NFS calls/second to generate from each client 60 calls/sec
+ * -m mix_file file specifying NFS call distribution (see below)
+ * -p procs # processes used to generate load on each client 7 procs
+ * -P populate test directories, but don't run a test off
+ * -R biod_reads max # of outstanding read requests at one time 2 reqs
+ * -S symlink_cnt # symbolic links used for symlink operations 20 symlinks
+ * -t time # seconds to generate load for the timed test 600 secs
+ * -T op_num test the NFS operation specified one time off
+ * -V validate correctness of server's NFS off
+ * -W biod_writes max # of outstanding writes req at one time 2 reqs
+ * -w warmup # secs to generate load before starting test 60 secs
+ * -z If specified, collect and dump raw data. off
+ *
+ *
+ * Multi Client Options
+ *
+ * option description default
+ * ------------------------ ----------------------------------- -----------
+ * -M prime_client_hostname hostname of prime client no default
+ * -N client_cnt # client machines in test no default
+ *
+ *
+ *
+ * Block Transfer Size Distribution
+ *
+ * The block transfer size distribution is specified by a table of values.
+ * The first column gives the percent of operations that will be a
+ * specific block transfer size. The second column gives the number of
+ * blocks units that will be transferred. Normally the block unit size
+ * is 8KB. The third column is a boolean specifying whether a trailing
+ * fragment block should be transferred. The fragment size for each transfer
+ * is a random multiple of 1 KB, up to the block size - 1 KB. Two tables
+ * are needed, one for read operation and one for write operations. The
+ * following table gives the default distributions.
+ *
+ * Read - Default Block Transfer Size Distribution Table
+ * percent block count fragment resulting transfer (8KB block size)
+ * ------- ----------- -------- -----------------------------------
+ * 0 0 0 0% 1 - 7 KB
+ * 85 1 0 85% 9 - 15 KB
+ * 8 2 1 8% 17 - 23 KB
+ * 4 4 1 4% 33 - 39 KB
+ * 2 8 1 2% 65 - 71 KB
+ * 1 16 1 1% 129 - 135 KB
+ *
+ * Write - Default Block Transfer Size Distribution Table
+ * percent block count fragment resulting transfer (8KB block size)
+ * ------- ----------- -------- -----------------------------------
+ * 49 0 1 49% 1 - 7 KB
+ * 36 1 1 36% 9 - 15 KB
+ * 8 2 1 8% 17 - 23 KB
+ * 4 4 1 4% 33 - 39 KB
+ * 2 8 1 2% 65 - 71 KB
+ * 1 16 1 1% 129 - 135 KB
+ *
+ * The user may specify a a different distribution by using the '-b' option.
+ * The format for the block size distribution file consists of the first
+ * three columns given above: percent, block count, and fragment. Read
+ * and write distribution tables are identified by the keywords "Read" and
+ * "Write". An example input file, using the default values, is given below:
+ *
+ * Read
+ * 0 0 0
+ * 85 1 0
+ * 8 2 1
+ * 4 4 1
+ * 2 8 1
+ * 1 16 1
+ * Write
+ * 49 0 1
+ * 36 1 1
+ * 8 2 1
+ * 4 4 1
+ * 2 8 1
+ * 1 16 1
+ *
+ * A second parameter controlled by the block transfer size distribution
+ * table is ethernet packet size. The distribution tables define the
+ * relative proportion of full blocks packets to fragment packets. For
+ * instance, the default tables have been constructed to produce a specific
+ * distribution of ethernet packet sizes for i/o operations by controlling
+ * the amount of data in each packet. The write packets produced consist
+ * of 50% 8-KB packets, and 50% 1-7 KB packets. The read packets consist
+ * of 85% 8-KB packets, and 15% 1-7 KB packets. These figures are
+ * determined by multiplying the percentage for the type of transfer by
+ * the number of blocks and fragments generated, and adding the totals.
+ * These conmputations are performed below for the default block size
+ * distribution tables:
+ *
+ * Read blocks fragments
+ * 0 0 0 0 0
+ * 85 1 0 85 0
+ * 8 2 1 16 8
+ * 4 4 1 16 4
+ * 2 8 1 16 2
+ * 1 16 1 16 1
+ * --- ---
+ * 149 (90%) 15 (10%)
+ *
+ * Write
+ * 49 0 1 0 49
+ * 36 1 1 36 36
+ * 8 2 1 16 8
+ * 4 4 1 16 4
+ * 2 8 1 16 2
+ * 1 16 1 16 1
+ * --- ---
+ * 100 (50%) 100 (50%)
+ *
+ *
+ *
+ *
+ *
+ * NFS Operation Mix
+ *
+ * The operation mix is described assigning a percentage to each type
+ * of NFS operation. The default mix of operations is:
+ *
+ * operation percent
+ * --------- -------
+ * null 0
+ * getattr 13
+ * setattr 1
+ * root 0
+ * lookup 34
+ * readlink 8
+ * read 22
+ * wrcache 0
+ * write 15
+ * create 2
+ * remove 1
+ * rename 0
+ * link 0
+ * symlink 0
+ * mkdir 0
+ * rmdir 0
+ * readdir 3
+ * fsstat 1
+ *
+ * The user may specify a a different operation mix by using the '-m' option.
+ * The format for the mix file consists of the output from an nfsstat(1)
+ * command, which lists an operation count and percentage for each NFS
+ * operation.
+ *
+ *
+ * File Set Size
+ *
+ * !!! needs to be re-written - mew 8/24
+ * This still needs to be rewritten
+ * - The default number of i/o files is based on load level.
+ * - For non I/O files, the number of initialized versus empty
+ * slots is hardcoded
+ * - dr 2/8/94
+ *
+ * The file set used by SFS is determined by 2 factors. First,
+ * SFS creates a base set of files. By default this consists of
+ * 100 files for i/o operations, 100 files for non-i/o operations, 20
+ * directories, and 20 symbolic links. These default values can be
+ * changed from the command line by using the "-F", "-D", and "-S" options.
+ * This file set is divided evenly among the set of processes used to
+ * generate load.
+ *
+ * Then, enough resources are allocated to allow for the eventual creation
+ * of new files, directories and symlinks by the creat, link, mkdir,
+ * and symlink operations. The number of extra slots allocated depends
+ * on the mix percentages assigned to each of the create and deletion
+ * operations, multiplied by an estimate of the total number of operations
+ * to be performed. For instance, the default number of extra files names
+ * allocated for non-i/o operations is computed as follows:
+ * 300 secs * 60 ops/sec = 18000 total operations
+ * 2% create * 18000 ops = 360 create ops
+ * 1% remove * 18000 ops = 180 remove ops
+ * 260 creates ops - 180 remove ops = 180 extra files to be created.
+ * These 90 files are distributed evenly among the processes used to
+ * generate load. With the default settings, no extra directories are
+ * created or removed, so no extra allocation is done for directories
+ * beyond the base file set. The same is true for symbolic links.
+ *
+ * Thus, the total default file set size is:
+ * 100 files for i/o operations
+ * 280 files for non-i/o operations
+ * 20 directories
+ * 20 symlinks
+ *
+ * By allocating all required space before the test begins, any space
+ * allocation problems encountered by SFS are discovered before the
+ * test is started.
+ *
+ *
+ * Program Control
+ *
+ * Strategy: loop for some number of NFS calls doing a random sleep
+ * followed by a call to one of the op generator routines. The routines
+ * are called based on a weighting factor determined by the set of
+ * default percentages or a mix supplied by the user.
+ *
+ * The generator routines are able to keep an accurate count of the
+ * NFS operations they are generating by using the NFS protocol
+ * directly and not going through the kernel. This eliminates the
+ * effects of kernel name caches and retry mechanisms that
+ * complicate control of what actually hits the wire. The calling
+ * routine benefits by avoiding having to get the NFS statistics
+ * from the kernel because they KNOW what calls they've made.
+ *
+ * By using the NFS protocol directly :
+ * "lookup" operations sidestep the client kernel name cache,
+ * "getattr" operations avoid the client kernel attribute cache,
+ * "read" operations avoid the client kernel buffer cache,
+ * and so on.
+ *
+ * A consequence of not going thru the client kernel is that the sfs
+ * program must maintain a table of file handles rather than open
+ * file descriptors.
+ *
+ * The parent process starts children to do the real work of generating load.
+ * The parent coordinates them so that they all start at the same time, and
+ * collects statistics from them when they are done. To coordinate the
+ * start up, the parent waits for each child to write one byte into
+ * a common log file (opened in append mode to avoid overwriting).
+ * After they write a byte the children pause, and the parent send SIGUSR1
+ * when it has heard from all of the kids. The children write their statistics
+ * into the same common log file and the parent reads and accumulates the
+ * statistics and prints them out.
+ *
+ *
+ *.Exported_Routines
+ * int main(int, char*)
+ *
+ *.Local_Routines
+ * void init_logfile(void)
+ * void usage(void)
+ * int setmix(char *)
+ * int setiodist(FILE *)
+ * int parseiodist(FILE *, int)
+ * void init_iodist(sfs_io_dist_type *)
+ * void init_fss(void)
+ * void init_filedist(void)
+ * int lad_substr(char *, char *)
+ *
+ *.Revision_History
+ * 21-Aug-92 0.1.11 Wittle File set access code.
+ * 14-Jul-92 0.1.9 Teelucksingh
+ * Implemented Mark Wittle's proposal to
+ * base File Set Size on peak load value,
+ * added "-L peak_load" option.
+ * 10-Jan-92 0.0.0.19 Teelucksingh
+ * Reworked setpgrp() usage to
+ * better handle BSD vs SYSV variations.
+ * 04-Jan-92 0.0.0.18 Pawlowski
+ * Added raw data dump code.
+ * 04-Dec-91 0.0.0.15 Keith
+ * Include string.h if SVR4.
+ * 28-Nov-91 0.0.0.13 Teelucksingh
+ * Modified code to use unique sfs /tmp
+ * logfiles; sfs can now be used on
+ * clients that have a shared /tmp area.
+ * Added ANSI C features. Fixed 'multiple
+ * signals from the Prime-Client' problem.
+ * Added code to allow clients to
+ * check for and create client specific
+ * directories under each mount point -
+ * clients share partitions. (code from
+ * M.Molloy).
+ * 22-Nov-91 Wittle Updated program description comment.
+ * Added new op generation code.
+ * Added block_dist_table and block_size
+ * options, removed 8KB packet assumptions.
+ * 04-Oct-91 0.0.0.12 Teelucksingh
+ * Changed SFS sources and executables
+ * to use the "prelad" prefix.
+ * 23-Sep-91 0.0.0.11 Teelucksingh
+ * Changed format of sfs output.
+ * 01-Aug-91 0.0.9 Wiryaman Use the SPEC random number generator.
+ * Since this RNG cannot take seed = 0,
+ * use child_num+1 instead.
+ * 17-Jul-91 0.0.8 Teelucksingh
+ * Enhance multi-client code and
+ * documentation.
+ * Keith Map "nhfsstone" to "laddis" in
+ * README, nhfsstone_mgr.c. Create
+ * initial DESCR.SFS for SPEC
+ * submission.
+ * 15-Jul-91 0.0.8 Wiryaman Add getmnt() for ULTRIX
+ * 25-Jun-91 0.0.7 Wiryaman Added validation option to test all
+ * of the NFS operations.
+ * 17-Jun-91 0.0.7 Teelucksingh
+ * Added multi-client synchronization
+ * support. By designating a client as
+ * "Prime client" you can synchronize
+ * multi-client SFS execution.
+ * 12-May-91 0.0.6 Wittle Fix standard deviation.
+ * 02-May-91 0.0.5 Wittle Fix SunOS signal bug; use default
+ * warmuptime; add local time routine;
+ * check for calls that underflow elapsed
+ * time measurement; add std deviation
+ * statistics; rework verbose output.
+ * fix init invalid protocol rmdir calls;
+ * 15-Apr-91 0.0.4 Wittle Test can be repeated without removing
+ * test directories - initialization
+ * restores base file set count and sizes.
+ * Fix lack of call rate & mix accuracy -
+ * set op_check before artificially
+ * increasing call_targets.
+ * Don't pre-create files/dirs that are
+ * meant to be created during the test.
+ * 10-Mar-91 0.0.3 Wittle Longer RPC timeout while populating
+ * testdir; strstr() bug fix;
+ * '-i' and '-e' options
+ * 06-Mar-91 0.0.2 Wittle Loop forever pre-filling files.
+ * 22-Feb-91 0.0.1 Wittle Use signal(2) instead of sigset(2).
+ * 18-Feb-91 0.0.0 Wittle Change algorythm for determining i/o
+ * sizes, preserve i/o file working set
+ * by using separate files; bugs fixes.
+ *
+ * nhfsstone renamed to laddis
+ *
+ * 31-Oct-90 2.0.4 Wittle Many bug fixes.
+ * 24-Aug-90 2.0.3 Wittle Output compatible w/graphing tools.
+ * 24-July-90 2.0.2 Wittle Handle mounting below symlinks.
+ * 24-June-90 2.0.1 Wittle Prefill files with data.
+ * 17-May-90 2.0.0 Bean Rewrote the guts to use NFS
+ * protocol directly.
+ * Cleaned up self-pacing mechanism.
+ * 08-Nov-89 Guillermo Roa Ported original version to DG/UX.
+ * 07-Jul-89 Legato Systems Created.
+ */
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <sys/signal.h>
+
+#include <sys/file.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+extern getmyhostname(char *, int);
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+/*
+ * ------------------------- External Definitions -------------------------
+ */
+
+/* external routines from RPC and system libraries */
+#if defined(SETPGRP_BSD)
+extern int setpgrp(int, int);
+#endif /* SETPGRP_BSD */
+#if defined(SETPGRP_SYSV)
+extern pid_t setpgrp(void);
+#endif /* SETPGRP_SYSV */
+
+/* forward definitions for local routines */
+static void init_logfile(void);
+static void usage(void);
+static int setmix(char *);
+static int setiodist(FILE *);
+static int parseiodist(FILE *, int);
+static void init_iodist(sfs_io_dist_type *);
+static void init_fss(void);
+static void init_filedist(void);
+static int lad_substr(char *, char *);
+static double time_so_far1(void);
+static double get_resolution(void);
+static void check_clock(void);
+
+int Tot_client_num_io_files = 0; /* # of files used for i/o per client */
+int Tot_client_num_non_io_files = /* # of files used for i/o per client */
+ DEFAULT_NFILES;
+int Files_per_dir = /* # of pre-created dirs */
+ DEFAULT_FILES_PER_DIR;
+int Tot_client_num_symlinks = /* # of pre-created symlinks/client */
+ DEFAULT_NSYMLINKS;
+int Child_num;
+char * Prime_client = NULL; /* Prime client hostname */
+int Client_num = 1; /* My client number */
+int Tcp = 0; /* Flag set on command line */
+char *sfs_Myname; /* name program invoked under */
+int Log_fd; /* log fd */
+char Logname[NFS_MAXNAMLEN]; /* child processes sync logfile */
+uid_t Real_uid; /* real uid */
+uid_t Cur_uid; /* my uid */
+gid_t Cur_gid; /* my gid list */
+
+static char Client_logname[SFS_MAXNAMLEN];
+
+/*
+ * ----------------- SFS Main and Initialization Code -----------------
+ */
+
+/*
+ * Read the command line arguments, fork off child processes to
+ * generate NFS load, and perform the local (ie, on this client)
+ * book-keeping for the test and the results.
+ */
+int
+main(
+ int argc,
+ char *argv[])
+{
+
+ char *mix_file; /* name of mix file */
+ char *iodist_file; /* name of block i/o dist table file */
+ int children; /* number of children */
+ int child_num; /* child index */
+ int total_load; /* total client load factor */
+ float child_load; /* per child load factor */
+ int pid; /* process id */
+ FILE *pid_fp;
+ FILE *iodist_fp; /* block io dist table file */
+ int i;
+ int c;
+ int Saveerrno;
+ int ret;
+ int nsigs = 32; /* reasonable default */
+ extern char *optarg;
+ extern int optind;
+
+ /*
+ * Place pid in pid log file
+ */
+ if ((pid_fp = fopen(SFS_PNT_PID, "a+")) == NULL) {
+ perror(SFS_PNT_PID);
+ exit(1);
+ }
+
+ (void) fprintf(pid_fp, "%d\n", getpid());
+
+ /* Get program name for stderr printing */
+ sfs_Myname = argv[0];
+
+ check_clock();
+ getmyhostname(lad_hostname, HOSTNAME_LEN);
+
+ init_ops();
+
+/*
+ * Get the uid and gid information.
+ */
+ Real_uid = getuid();
+ Cur_gid = getgid();
+
+/*
+ * Form a new process group so our syncrhonization signals don't
+ * cause our parent shell to exit. Clear the umask.
+ * Default is to use the standard setsid
+ */
+#ifdef SETPGRP3
+ ret = setpgrp3(); /* Work around HP-UX bug */
+#else
+#ifdef SETPGRP_SYSV
+ ret = setpgrp();
+#else
+#ifdef SETPGRP_BSD
+ ret = setpgrp(0, getpid());
+#else
+ ret = setsid();
+#endif /* SETPGRP_BSD */
+#endif /* SETPGRP_SYSV */
+#endif /* SETPGRP3 */
+
+ if (ret == -1) {
+ (void) fprintf(stderr, "%s: failed on setsid/setpgrp\n",
+ sfs_Myname);
+ exit(95);
+ }
+
+ (void) umask(0);
+
+/* Set up default parameters */
+ Validate = 0;
+
+ children = DEFAULT_NPROCS;
+ total_load = DEFAULT_LOAD;
+ mix_file = 0;
+ iodist_file = 0;
+ Nfs_timers = Nfs_udp_timers;
+
+ /* Parse command line arguments */
+ while ((c = getopt(argc, argv, "a:A:b:B:cd:D:f:F:il:m:M:N:p:PQR:S:T:t:V:W:w:z")) != EOF)
+ switch (c) {
+
+ case 'a': /* Percent of files to access */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal access value %s\n",
+ sfs_Myname, optarg);
+ exit(96);
+ }
+ Access_percent = atoi(optarg);
+ if (Access_percent < 0 || Access_percent > 100) {
+ (void) fprintf(stderr,
+ "%s: %% access must be between 0 and 100\n",
+ sfs_Myname);
+ exit(97);
+ }
+ break;
+
+ case 'A': /* Percent of writes that append */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal append value %s\n",
+ sfs_Myname, optarg);
+ exit(98);
+ }
+ Append_percent = atoi(optarg);
+ if (Append_percent < 0 || Append_percent > 100) {
+ (void) fprintf(stderr,
+ "%s: %% append must be between 0 and 100\n",
+ sfs_Myname);
+ exit(99);
+ }
+ break;
+
+ case 'b': /* Set block size distribution table from file */
+ if ((iodist_fp = fopen(optarg, "r")) == NULL) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: bad block size file",
+ sfs_Myname);
+ errno = Saveerrno;
+ perror(optarg);
+ exit(100);
+ }
+ if (setiodist(iodist_fp) < 0) {
+ exit(101);
+ }
+ iodist_file = optarg;
+ (void) fclose(iodist_fp);
+ break;
+
+ case 'B': /* Set the per packet maximum block size */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr,
+ "%s: illegal block size value %s\n",
+ sfs_Myname, optarg);
+ exit(102);
+ }
+ Kb_per_block = atoi(optarg);
+ if ((Kb_per_block < 1) ||
+ (Kb_per_block > (DEFAULT_MAX_BUFSIZE/1024))) {
+ (void) fprintf(stderr,
+ "%s: illegal block size value %s\n",
+ sfs_Myname, optarg);
+ exit(103);
+ }
+ Bytes_per_block = Kb_per_block * 1024;
+ break;
+
+
+ case 'c': /* Set number of calls */
+ (void) fprintf(stderr, "%s: '-c option no longer supported\n",
+ sfs_Myname);
+ exit(104);
+ break;
+
+ case 'd': /* Set debugging level */
+ Debug_level = set_debug_level(optarg);
+ break;
+
+ case 'D': /* Set number of directories */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal dirs value %s\n",
+ sfs_Myname, optarg);
+ exit(105);
+ }
+ Files_per_dir = atoi(optarg);
+ break;
+
+ case 'f': /* Percent change in file set size */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal file set delta value %s\n",
+ sfs_Myname, optarg);
+ exit(106);
+ }
+ Fss_delta_percent = atoi(optarg);
+ if (Fss_delta_percent < 0 || Fss_delta_percent > 100) {
+ (void) fprintf(stderr,
+ "%s: %% file set delta must be between 0 and 100\n",
+ sfs_Myname);
+ exit(107);
+ }
+ break;
+
+ case 'F': /* Set number of io files */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal files value %s\n",
+ sfs_Myname, optarg);
+ exit(108);
+ }
+ Tot_client_num_io_files = atoi(optarg);
+ break;
+
+ case 'i': /* Set interactive mode */
+ if (Prime_client != NULL) {
+ (void) fprintf(stderr,
+ "%s: -i and -M options are incompatible\n",
+ sfs_Myname);
+ exit(109);
+ }
+ Interactive++;
+ break;
+
+ case 'l': /* Set load */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal load value %s\n",
+ sfs_Myname, optarg);
+ exit(110);
+ }
+ total_load = atoi(optarg);
+ if (total_load < 0) {
+ (void) fprintf(stderr, "%s: load must be > 0\n",
+ sfs_Myname);
+ exit(111);
+ }
+ break;
+
+ case 'm': /* Set mix from a file */
+ mix_file = optarg;
+ if (setmix(mix_file) < 0) {
+ exit(112);
+ }
+ break;
+
+ case 'M': /* Set prime_client host name for multi-client sync */
+ if (Interactive) {
+ (void) fprintf(stderr,
+ "%s: -M and -i options are incompatible\n",
+ sfs_Myname);
+ exit(113);
+ }
+ Prime_client = optarg;
+ break;
+
+ case 'N': /* Set client number in multi-client run */
+ Client_num = atoi(optarg);
+ if (Client_num <= 0) {
+ (void) fprintf(stderr,
+ "%s: client number must be > 0\n",
+ sfs_Myname);
+ exit(114);
+ }
+ break;
+
+ case 'p': /* Set number of child processes */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal procs value %s\n",
+ sfs_Myname, optarg);
+ exit(115);
+ }
+ children = atoi(optarg);
+ if (children < 0) {
+ (void) fprintf(stderr, "%s: number of children must be > 0\n",
+ sfs_Myname);
+ exit(116);
+ }
+ break;
+
+ case 'P': /* Populate only */
+ Populate_only++;
+ break;
+
+ case 'Q': /* Set NFS/TCP behaviour */
+ Tcp = 1;
+ Nfs_timers = Nfs_tcp_timers;
+ break;
+
+ case 'R': /* set maximum async read concurrency level */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal read count value %s\n",
+ sfs_Myname, optarg);
+ exit(117);
+ }
+ Biod_max_outstanding_reads = atoi(optarg);
+ if (Biod_max_outstanding_reads < 0 ||
+ Biod_max_outstanding_reads > MAX_BIODS) {
+ (void) fprintf(stderr,
+ "%s: read count must be >= 0 and <= %d\n",
+ sfs_Myname, MAX_BIODS);
+ exit(118);
+ }
+ break;
+
+ case 'S': /* Set number of symlinks */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr,
+ "%s: illegal symlinks value %s\n",
+ sfs_Myname, optarg);
+ exit(119);
+ }
+ Tot_client_num_symlinks = atoi(optarg);
+ break;
+
+ case 'T': /* Set test mode, number following is opnum */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal test value %s\n",
+ sfs_Myname, optarg);
+ exit(120);
+ }
+ Testop = atoi(optarg);
+ if (Testop >= NOPS) {
+ (void) fprintf(stderr, "%s: illegal test value %d\n",
+ sfs_Myname, Testop);
+ exit(121);
+ }
+ break;
+
+ case 't': /* Set run time */
+ if (Ops[TOTAL].target_calls > 0) {
+ (void) fprintf(stderr,
+ "%s: -t and -c options are incompatible\n",
+ sfs_Myname);
+ exit(122);
+ }
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal time value %s\n",
+ sfs_Myname, optarg);
+ exit(123);
+ }
+ Runtime = atoi(optarg);
+ if (Runtime < 0) {
+ (void) fprintf(stderr, "%s: run time must be >= 0\n",
+ sfs_Myname);
+ exit(124);
+ }
+ break;
+
+ case 'V': /* Set Validate Level */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal validate value %s\n",
+ sfs_Myname, optarg);
+ exit(125);
+ }
+ Validate = atoi(optarg);
+ if (Validate < 1 || Validate > 3) {
+ (void) fprintf(stderr, "%s: validate must be between 1 and 3\n",
+ sfs_Myname);
+ exit(126);
+ }
+ break;
+
+ case 'W': /* set maximum async write concurrency level */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal write count value %s\n",
+ sfs_Myname, optarg);
+ exit(127);
+ }
+ Biod_max_outstanding_writes = atoi(optarg);
+ if (Biod_max_outstanding_writes < 0 ||
+ Biod_max_outstanding_writes > MAX_BIODS) {
+ (void) fprintf(stderr,
+ "%s: write count must be >= 0 and <= %d\n",
+ sfs_Myname, MAX_BIODS);
+ exit(128);
+ }
+ break;
+
+ case 'w': /* Set warmup time */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal warmup value %s\n",
+ sfs_Myname, optarg);
+ exit(129);
+ }
+ Warmuptime = atoi(optarg);
+ if (Warmuptime < 0) {
+ (void) fprintf(stderr, "%s: warmup time must be >= 0\n",
+ sfs_Myname);
+ exit(130);
+ }
+ break;
+
+ case 'z': /* Do raw data dumps */
+ Dump_data++;
+ break;
+
+ case '?':
+ default:
+ usage();
+ exit(131);
+
+ } /* end switch on arg */
+
+
+ /* compute ops/request for i/o operations */
+ init_iodist(Io_dist_ptr);
+
+ /* compute bytes/file and number of files */
+ init_filedist();
+
+ /* validate all the NFS operations that sfs will use */
+ if (Validate > 0) {
+ /*
+ * -F <number of files > or else
+ * DEFAULT_NFILES
+ */
+ if (Tot_client_num_io_files == 0) {
+ Tot_client_num_io_files = DEFAULT_NFILES;
+ }
+ Num_io_files = Tot_client_num_io_files/children + 1;
+ /* number of non-io files, dir and symlinks base on constants */
+ Num_non_io_files = Tot_client_num_non_io_files/children + 1;
+ Num_dirs = Num_io_files/Files_per_dir + 1;
+ Num_symlinks = Tot_client_num_symlinks/children + 1;
+
+ /* io operations access a subset of the files */
+ Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1;
+ /* non-io and other operations access all of the files */
+ Num_working_non_io_files = Num_io_files;
+ Num_working_dirs = Num_dirs;
+ Num_working_symlinks = Num_symlinks;
+
+ Validate_ops(argc - optind, &argv[optind]);
+ exit(133);
+ }
+
+ /*
+ * Initial check on the mount arguments, must be at least an
+ * even multiple of the number of procs.
+ */
+ if ((argc - optind) % children) {
+ (void) fprintf(stderr,
+"%s: Invalid mount point list: Not a multiple of number of procs\n",
+ sfs_Myname);
+ exit(182);
+ }
+
+ /*
+ * -F <number of io files > or else
+ * base files set on load ; this in NON-SPEC though
+ */
+ if (Tot_client_num_io_files == 0) {
+ Tot_client_num_io_files = ((DEFAULT_BYTES_PER_OP / 1024 * total_load)
+ / (1024)) * files_per_megabyte;
+ }
+ Num_io_files = Tot_client_num_io_files/children + 1;
+
+ /*
+ * Number of non-io files scales with load and is set at 2% of all files,
+ * but at least DEFAULT_NFILES worth.
+ */
+ Tot_client_num_non_io_files = Tot_client_num_io_files * 0.02;
+ if (Tot_client_num_non_io_files < DEFAULT_NFILES)
+ Tot_client_num_non_io_files = DEFAULT_NFILES;
+ Num_non_io_files = Tot_client_num_non_io_files/children + 1;
+
+ /* number of dir and symlinks base on constants */
+ Num_dirs = Num_io_files/Files_per_dir + 1;
+ Num_symlinks = Tot_client_num_symlinks/children + 1;
+
+ /* io operations access a subset of the files */
+ Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1;
+
+ /* non-io and other operations access all of the files */
+ Num_working_non_io_files = Num_io_files;
+ Num_working_dirs = Num_dirs;
+ Num_working_symlinks = Num_symlinks;
+
+ /*
+ * If we are doing a timed test, we still need an
+ * estimate of how many calls are needed in order to
+ * judge our progress.
+ * If we are doing a test for a number of calls, we still need an
+ * estimate of how long the test will take in order to
+ * establish the time interval between progress checks.
+ */
+ if (Timed_run) {
+ /*
+ * the total number of calls will be divided between the children
+ * when they are forked off.
+ */
+ Ops[TOTAL].target_calls = Runtime * total_load;
+ } else {
+ Runtime = (int) ((float) Ops[TOTAL].target_calls / (float) total_load);
+ }
+
+ /*
+ * multi-client sync support
+ * offset the Runtime value by MULTICLIENT_OFFSET seconds.
+ * This offset prevents the client from finishing before
+ * the Prime Client tells it to 'STOP'. The MULTICLIENT_OFFSET is larger
+ * than the time_out value on the Prime-Client; so in case the client
+ * does not stop when it's told to, the Prime-client should time_out.
+ */
+ if (Prime_client && Timed_run)
+ Runtime += MULTICLIENT_OFFSET;
+
+ /* compute file set sizes */
+ init_fss();
+
+ /* Set up synchronization and results log file */
+ init_logfile();
+
+ /*
+ * setup value of nsigs
+ */
+#ifdef __NSIG
+ nsigs = __NSIG;
+#endif
+#ifdef _NSIG
+ nsigs = _NSIG;
+#endif
+#ifdef NSIG
+ nsigs = NSIG;
+#endif
+#if defined(SOLARIS2) && !defined(_sys_nsig)
+ nsigs = _sys_siglistn;
+#endif
+
+ /* Set up the signal handlers for all signals */
+
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ {
+ struct sigaction sig_act, old_sig_act;
+
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ if (DEBUG_PARENT_GENERAL) {
+ if (nsigs == 0) {
+ (void) fprintf (stderr,
+ "WARNING: nsigs not defined, no extra signals caught\n");
+ }
+ for (i = 1; i < nsigs; i++) {
+/* attempt to set up signal handler for these signals give an error !! K.T. */
+ if (i!=SIGCHLD && i!=SIGKILL && i!=SIGSTOP && i!=SIGCONT) {
+ if (sigaction(i,&sig_act,&old_sig_act) == -1) {
+ if (errno == EINVAL) {
+ (void) fprintf (stderr,
+ "Skipping invalid signal %d\n", i);
+ } else {
+ perror("sigaction failed");
+ exit(134);
+ }
+ }
+ }
+ }
+ }
+
+ /* signals handlers for signals used by sfs */
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGINT");
+ exit(135);
+ }
+
+ sig_act.sa_handler = sfs_alarm;
+ if (sigaction(SIGALRM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGALRM");
+ exit(136);
+ }
+
+ sig_act.sa_handler = sfs_cleanup;
+ if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGTERM");
+ exit(137);
+ }
+
+ sig_act.sa_handler = sfs_startup;
+ if (sigaction(SIGUSR1,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGUSR1");
+ exit(138);
+ }
+
+ sig_act.sa_handler = sfs_stop;
+ if (sigaction(SIGUSR2,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGUSR2");
+ exit(139);
+ }
+ }
+#else
+ if (DEBUG_PARENT_GENERAL) {
+ if (nsigs == 0) {
+ (void) fprintf (stderr,
+ "WARNING: nsigs not defined, no extra signals caught\n");
+ }
+ for (i = 1; i < nsigs; i++) {
+ if (i!=SIGCHLD)
+ (void) signal(i, generic_catcher);
+ }
+ }
+ /* signals handlers for signals used by sfs */
+ (void) signal(SIGINT, sfs_cleanup);
+ (void) signal(SIGALRM, sfs_alarm);
+ (void) signal(SIGTERM, sfs_cleanup);
+ (void) signal(SIGUSR1, sfs_startup);
+ (void) signal(SIGUSR2, sfs_stop);
+#endif
+
+ /* Fork children */
+ for (child_num = 0; child_num < children; child_num++) {
+ pid = fork();
+ if (pid == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't fork children.", sfs_Myname);
+ errno = Saveerrno;
+ perror("fork");
+ (void) generic_kill(0, SIGINT);
+ exit(140);
+ } else if (pid == 0) {
+ break; /* get out of child creation */
+ }
+ (void) fprintf(pid_fp, "%d\n", pid);
+ } /* end for forking kids */
+ (void) fclose(pid_fp);
+
+ /*
+ * Parent: wait for kids to get ready, start them, wait for them to
+ * finish, read and accumulate results.
+ */
+ if (pid != 0) {
+ if (setuid(Real_uid) != 0) {
+ (void) fprintf(stderr,"%s: %s%s\n",
+ sfs_Myname, "cannot perform setuid operation.\n",
+ "Do `make install` as root.\n");
+ }
+
+ /* I'm the parent - let the common code signal handlers know it */
+ Child_num = -1;
+
+ parent(children, total_load, mix_file, iodist_file);
+
+ /* Clean up and exit. */
+ (void) close(Log_fd);
+ (void) unlink(Logname);
+ exit(0);
+
+ } /* parent */
+
+ /*
+ * Children : initialize, then notify parent through log file,
+ * wait to get signal, beat the snot out of the server, write
+ * stats to the log file, and exit.
+ */
+ if (pid == 0) {
+
+ /* I'm a child - let the common code signal handlers know it */
+ Child_num = child_num;
+
+ /*
+ * Determine my share of the calls and load (including any left over)
+ * The call target for each child differs by at most 1 call.
+ * The load rate for each child differs by at most 1 call/sec.
+ */
+ Ops[TOTAL].target_calls = Ops[TOTAL].target_calls / children;
+ if (child_num <= Ops[TOTAL].target_calls % children) {
+ Ops[TOTAL].target_calls++;
+ }
+ child_load = (float) total_load / (float) children;
+
+ /*
+ * Sleep a bit so the parent can catch up after procreating all us
+ * children.
+ */
+ (void) sleep(10);
+
+ child(child_num, children, child_load, argc - optind, &argv[optind]);
+ exit(0);
+
+ } /* child */
+
+ (void) unlink(SFS_PNT_PID);
+
+ return(0);
+
+} /* main */
+
+
+/*
+ * ----------------- Initalization of Parent/Child ---------------------
+ */
+
+/*
+ * Open the multi-client synchronization file with append mode.
+ */
+static void
+init_logfile(void)
+{
+ FILE *cl_log_fd;
+ int Saveerrno;
+
+ (void) sprintf(Logname, "%s%d", CHILD_SYNC_LOG, Client_num);
+ Log_fd = open(Logname, (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666);
+ if (Log_fd == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't open log file %s ", sfs_Myname, Logname);
+ errno = Saveerrno;
+ perror(Logname);
+ exit(141);
+ }
+ if (chown(Logname, Real_uid, Cur_gid) ==-1) {
+ perror("chown");
+ (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname);
+ }
+
+ /* if multi-client execution then init client sync log */
+ if (Prime_client != NULL) {
+ /* init logfile and write process id */
+ (void) sprintf(Client_logname, "%s%d",
+ SFS_CLIENT_SYNC_LOG, Client_num);
+ cl_log_fd = fopen(Client_logname, "w+");
+ if (chown(Client_logname, Real_uid, Cur_gid) ==-1) {
+ perror("chown");
+ (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname);
+ }
+ if (cl_log_fd == NULL) {
+ Saveerrno = errno;
+ (void) fprintf(stderr,
+ "%s: can't open Client synchronization file %s ",
+ sfs_Myname, Client_logname);
+ errno = Saveerrno;
+ perror(Client_logname);
+ exit(142);
+ } else {
+ /* store parent pid */
+ (void) fprintf(cl_log_fd, "%d", (int)getpid());
+ (void) fclose(cl_log_fd);
+ }
+ } /* init multi-client sync log */
+
+} /* init_logfile */
+
+/*
+ * ------------------------ Utility Routines --------------------------
+ */
+
+
+/*
+ * Print the program's usage message.
+ * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time]
+ * [-m mix_file] [-B block_size] [-b blocksz_file]
+ * [-f file_set_delta] [-a access_pnct]
+ * [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]
+ * [-d debug_level] [-i] [-P] [-T op_num]
+ * [-V validation_level] [-z] [-Q]
+ * [-R biod_reads] [-W biod_writes]
+ * [-M prime_client_hostname] [-N client_cnt]
+ */
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-l load] [-p procs] [-w warmup] [-t time]\n", sfs_Myname);
+ (void) fprintf(stderr,
+ " [-m mix_file] [-B block_size] [-b blocksz_file]\n");
+ (void) fprintf(stderr,
+ " [-f file_set_delta] [-a access_pnct]\n");
+ (void) fprintf(stderr,
+ " [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]\n");
+ (void) fprintf(stderr,
+ " [-d debug_level] [-i] [-P] [-T op_num]\n");
+ (void) fprintf(stderr,
+ " [-V validation_level] [-z] [-Q]\n");
+ (void) fprintf(stderr,
+ " [-R biod_reads] [-W biod_writes]\n");
+ (void) fprintf(stderr,
+ " [-M prime_client_hostname] [-N client_cnt]\n");
+} /* usage */
+
+
+
+/*
+ * -------------- Command Line File Parsing -------------------
+ */
+
+/*
+ * Constants for mix file
+ */
+#define LINELEN 128 /* max bytes/line in mix file */
+#define MIX_START 0
+#define MIX_DATALINE 1
+#define MIX_DONE 2
+#define MIX_FIRSTLINE 3
+
+/*
+ * Parse the operation mix file 'mix_file'.
+ *
+ * ORIGINAL PRE-SFS1.2 format:
+ * Assumes that the input file is in the same format as
+ * the output of the nfsstat(8) command.
+ *
+ * Uses a simple state transition to keep track of what to expect.
+ * Parsing is done a line at a time.
+ *
+ * State Input action New state
+ * MIX_START ".*nfs:.*" skip one line MIX_FIRSTLINE
+ * MIX_FIRSTLINE ".*[0-9]*.*" get calls MIX_DATALINE
+ * MIX_DATALINE "[0-9]* [0-9]*%"X6 get op counts MIX_DATALINE
+ * MIX_DATALINE "[0-9]* [0-9]*%"X4 get op counts MIX_DONE
+ * MIX_DONE EOF return
+ *
+ * We read operation counts from the mix file
+ * and compute our own mix percentages,
+ * rather than using those in the mix file.
+ *
+ * NEW SFS1.2 format version #2:
+ * SFS MIXFILE VERSION 2 Version header (must come first line)
+ * "^#.*" Comment (any line except first)
+ * "%s [0-9]*%" Op name Op percentage
+ */
+static int
+setmix(
+ char * mix_file)
+{
+ int state; /* current state of state machine */
+ int got; /* number of items read from input line */
+ int opnum; /* operation number index */
+ int calls; /* total number of calls in mix */
+ char line[LINELEN]; /* input line buffer */
+ char op_name[LINELEN]; /* name buffer */
+ int mix_pcnt;
+ unsigned int len; /* length of input line */
+ FILE *mix_fp; /* mix file */
+ int vers; /* mix file version number */
+ sfs_op_type *op_ptr;
+
+ if ((mix_fp = fopen(mix_file, "r")) == NULL) {
+ (void) fprintf(stderr, "%s: bad mix file", sfs_Myname);
+ perror(mix_file);
+ return(-1);
+ }
+
+ if (fgets(line, LINELEN, mix_fp) == NULL) {
+ (void) fprintf(stderr, "%s: bad mix format - unexpected empty file\n",
+ sfs_Myname);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+
+ opnum = 0;
+
+ /*
+ * Look for initial version string
+ */
+ got = sscanf(line, "SFS MIXFILE VERSION %d", &vers);
+ if (got != 1) {
+ /*
+ * Check to see if this is old mixfile
+ */
+ len = strlen(line);
+ if (len < 4 || lad_substr(line, "nfs:") == 0) {
+ (void) fprintf(stderr, "%s: bad mix format - initial line '%s'\n",
+ sfs_Myname, line);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ vers = 1;
+ }
+
+ if (vers == 1) {
+ /*
+ * Old style mix file
+ */
+ state = MIX_START;
+ while (state != MIX_DONE && fgets(line, LINELEN, mix_fp)) {
+
+ switch (state) {
+ case MIX_START:
+ /*
+ * Ate first line after nfs:
+ */
+ state = MIX_FIRSTLINE;
+ break;
+
+ case MIX_FIRSTLINE:
+ got = sscanf(line, "%d", &calls);
+ if (got != 1) {
+ (void) fprintf(stderr,
+ "%s: bad mix format - can't find 'calls' value %d\n",
+ sfs_Myname,got);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ if (fgets(line, LINELEN, mix_fp) == NULL) {
+ (void) fprintf(stderr,
+ "%s: bad mix format - unexpected EOF after 'calls'\n",
+ sfs_Myname);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ state = MIX_DATALINE;
+ break;
+
+ case MIX_DATALINE:
+ got = sscanf(line,
+ "%d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%%",
+ &Ops[opnum].mix_pcnt,
+ &Ops[opnum + 1].mix_pcnt,
+ &Ops[opnum + 2].mix_pcnt,
+ &Ops[opnum + 3].mix_pcnt,
+ &Ops[opnum + 4].mix_pcnt,
+ &Ops[opnum + 5].mix_pcnt,
+ &Ops[opnum + 6].mix_pcnt);
+
+ if (got == 4 && opnum == 14) {
+ /* looks like the last line */
+ state = MIX_DONE;
+ } else if (got == 7) {
+ opnum += 7;
+ if (fgets(line, LINELEN, mix_fp) == NULL) {
+ (void) fprintf(stderr,
+ "%s: bad mix format - unexpected EOF after 'calls'\n",
+ sfs_Myname);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ } else {
+ (void) fprintf(stderr,
+ "%s: bad mix format - can't find %d op values\n",
+ sfs_Myname, got);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ "%s: error parsing mix file - bad state %d\n",
+ sfs_Myname, state);
+ (void) fclose(mix_fp);
+ return (-1);
+ } /* end switch on state */
+ } /* end while there are lines to read */
+
+ if (state != MIX_DONE) {
+ (void) fprintf(stderr, "%s: bad mix format - unexpected EOF\n",
+ sfs_Myname);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ for (opnum = 0; opnum < NOPS; opnum++) {
+ Ops[opnum].mix_pcnt = Ops[opnum].mix_pcnt * 100 / calls
+ + ((Ops[opnum].mix_pcnt * 1000 / calls % 10) >= 5);
+ }
+ (void) fclose(mix_fp);
+ return (0);
+ }
+ if (vers == 2) {
+ /*
+ * New style mix file
+ */
+ while (fgets(line, LINELEN, mix_fp) != NULL) {
+ if (line[0] == '#') /* Comment line */
+ continue;
+ got = sscanf(line, "%s %d", op_name, &mix_pcnt);
+ if (got != 2) {
+ (void) fprintf(stderr,
+ "%s: bad mix format - can't find op values: %s\n",
+ sfs_Myname, line);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ op_ptr = Ops;
+ while (strcmp(op_ptr->name, "TOTAL") != 0) {
+ if (strcmp(op_ptr->name, op_name) == 0) {
+ op_ptr->mix_pcnt = mix_pcnt;
+ break;
+ }
+ op_ptr++;
+ }
+ if (strcmp(op_ptr->name, "TOTAL") == 0) {
+ (void) fprintf(stderr,
+ "%s: unknown op name: %s\n",
+ sfs_Myname, op_name);
+ (void) fclose(mix_fp);
+ return (-1);
+ }
+ }
+ /*
+ * Make sure that the total mix percentages == 100
+ */
+ op_ptr = Ops;
+ mix_pcnt = 0;
+ while (strcmp(op_ptr->name, "TOTAL") != 0) {
+ mix_pcnt += op_ptr->mix_pcnt;
+ op_ptr++;
+ }
+ if (mix_pcnt != 100) {
+ (void) fprintf(stderr,
+ "%s: WARNING total mix percentage %d != 100\n",
+ sfs_Myname, mix_pcnt);
+ }
+ (void) fclose(mix_fp);
+ return (0);
+ }
+
+ (void) fprintf(stderr, "%s: Unknown mix file version number %d\n",
+ sfs_Myname, vers);
+ (void) fclose(mix_fp);
+ return (-1);
+} /* setmix */
+
+
+/*
+ * Parse the block I/O distribution file 'fp'.
+ */
+static int
+setiodist(
+ FILE * fp)
+{
+ int i;
+
+ /* first, read and parse the i/o distribution file for syntax and size */
+ if (parseiodist(fp, 1) == -1) {
+ exit(143);
+ }
+ (void) fseek(fp, 0, SEEK_SET);
+
+ /* read the i/o distribution file into the i/o dist table */
+ if (parseiodist(fp, 2) == -1) {
+ exit(144);
+ }
+
+ if (DEBUG_PARENT_GENERAL) {
+ (void) fprintf(stdout, "I/o Distribution Table\n");
+ (void) fprintf(stdout, "Read:\n");
+ (void) fprintf(stdout, "\tpcnt bufs frags\n");
+ for (i = 0 ; ; i++) {
+ (void) fprintf(stdout, "\t%4d %4d %5d\n", Io_dist_ptr->read[i].pcnt,
+ Io_dist_ptr->read[i].bufs, Io_dist_ptr->read[i].frags);
+ if (Io_dist_ptr->read[i].pcnt == 100)
+ break;
+ }
+
+ (void) fprintf(stdout, "Write:\n");
+ (void) fprintf(stdout, "\tpcnt bufs frags\n");
+ for (i = 0; ; i++) {
+ (void) fprintf(stdout, "\t%4d %4d %5d\n",
+ Io_dist_ptr->write[i].pcnt,
+ Io_dist_ptr->write[i].bufs,
+ Io_dist_ptr->write[i].frags);
+ if (Io_dist_ptr->write[i].pcnt == 100)
+ break;
+ }
+ (void) fprintf(stdout, "Maximum file size: %d KB (%d * %d KB)\n",
+ Io_dist_ptr->max_bufs * Kb_per_block,
+ Io_dist_ptr->max_bufs, Kb_per_block);
+ }
+ return(0);
+} /* setiodist */
+
+
+/*
+ * Block/File Distribution file parser.
+ * Assumes that the input file is in the following format:
+ *
+ *
+ * READ_KEY_WORD
+ * percent block_cnt fragment_flag
+ * . . .
+ * . . .
+ * . . .
+ * WRITE_KEY_WORD
+ * percent block_cnt fragment_flag
+ * . . .
+ * . . .
+ * . . .
+ *
+ *
+ * Notes:
+ * - The READ_KEY_WORD is "Read", the WRITE_KEY_WORD is "Write".
+ * - For each key word, the percent fields must sum to 100.
+ * - Fragment is either true (1) or false (0)
+ * - Maximum file size (and transfer size) is the largest
+ * eight_k_cnt * 8KB plus 7 Kb for fragments
+ *
+ *
+ * Uses a simple state transition to keep track of what to expect.
+ * Parsing is done a line at a time.
+ *
+ * State Input action New state
+ * ----- -------------------- ------------- ---------
+ * START "Read" skip one line READ
+ * START "Write" skip one line WRITE
+ * READ "[0-9]* [0-9]* [01]" get values READ
+ * READ "Write" skip one line WRITE
+ * WRITE "[0-9]* [0-9]* [01]" get values WRITE
+ * WRITE "Read" skip one line READ
+ * DONE EOF return
+ *
+ * Pass 1 reads the file and allocates table space.
+ * Pass 2 reads the file data into the tables.
+ */
+static int
+parseiodist(
+ FILE * fp,
+ int pass)
+{
+ int state; /* current state of state machine */
+ int got; /* number of items read from input line */
+ int pcnt; /* percent read from input line */
+ int bufs; /* eight_kb_buffer_cnt read from input line */
+ int frags; /* fragment flag read from input line */
+ int rbucket; /* current read distribution table bucket */
+ int wbucket; /* current write distribution table bucket */
+ int rpcnt; /* cumulative percent for read buckets */
+ int wpcnt; /* cumulative percent for write buckets */
+ char key[5]; /* keyword buffer */
+ char line[LINELEN]; /* input line buffer */
+ int nextline;
+
+ /*
+ * Pass 1 reads, sizes, and error checks the input
+ * and then allocates space for the distribution tables.
+ * Pass 2 reads the input into the allocated tables.
+ */
+
+ rbucket = 0;
+ wbucket = 0;
+ rpcnt = 0;
+ wpcnt = 0;
+ state = IO_DIST_START;
+
+ while (fgets(line, LINELEN, fp)) {
+
+ nextline = 0;
+ while (nextline == 0) {
+
+ if (state == IO_DIST_READ) {
+ got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags);
+ if (got != 3) {
+ state = IO_DIST_START;
+ continue; /* same line, but goto new state */
+ }
+ if (pass == 1) {
+ rbucket++;
+ rpcnt += pcnt;
+ if (frags != 0 && frags != 1) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - bad fragment value\n",
+ sfs_Myname);
+ return(-1);
+ }
+ } else {
+ rpcnt += pcnt;
+ Io_dist_ptr->read[rbucket].pcnt = rpcnt;
+ Io_dist_ptr->read[rbucket].bufs = bufs;
+ Io_dist_ptr->read[rbucket].frags = frags;
+ rbucket++;
+ }
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stdout, "p=%d b=%d f=%d rpcnt=%d\n",
+ pcnt, bufs, frags, rpcnt);
+ (void) fflush(stdout);
+ }
+
+ /* read next line in file */
+ nextline++;
+ break;
+ }
+
+ if (state == IO_DIST_WRITE) {
+ got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags);
+ if (got != 3) {
+ state = IO_DIST_START;
+ continue; /* same line, but goto new state */
+ }
+ if (pass == 1) {
+ wbucket++;
+ wpcnt += pcnt;
+ if (frags != 0 && frags != 1) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - bad fragment value\n",
+ sfs_Myname);
+ return(-1);
+ }
+ } else {
+ wpcnt += pcnt;
+ Io_dist_ptr->write[wbucket].pcnt = wpcnt;
+ Io_dist_ptr->write[wbucket].bufs = bufs;
+ Io_dist_ptr->write[wbucket].frags = frags;
+ wbucket++;
+ }
+ if (DEBUG_CHILD_FILES) {
+ (void) fprintf(stdout, "p=%d b=%d f=%d wpcnt=%d\n",
+ pcnt, bufs, frags, wpcnt);
+ (void) fflush(stdout);
+ }
+ /* read next line in file */
+ nextline++;
+ break;
+ }
+
+ if (state == IO_DIST_START) {
+ got = sscanf(line, "%s", key);
+ if (got != 1 || (strlen(key) != 5)){
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - invalid keyword %s\n",
+ sfs_Myname, key);
+ return(-1);
+ }
+ if (!strcmp(key, "Read") || !strcmp(key, "read")) {
+ if (rbucket != 0) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - too many read keywords\n",
+ sfs_Myname);
+ return(-1);
+ }
+ rpcnt = 0;
+ state = IO_DIST_READ;
+
+ /* read next line in file */
+ nextline++;
+ break;
+ }
+ if (!strcmp(key, "Write") || !strcmp(key, "write")) {
+ if (wbucket != 0) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - too many write keywords\n",
+ sfs_Myname);
+ return(-1);
+ }
+ wpcnt = 0;
+ state = IO_DIST_WRITE;
+
+ /* read next line in file */
+ nextline++;
+ break;
+ }
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - unknown keyword %s\n",
+ sfs_Myname, key);
+ return(-1);
+ }
+
+ } /* end while processing this line */
+ } /* end while more lines */
+
+ if (pass == 1) {
+
+ /* error check the input */
+ if (rbucket == 0) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - no read distribution data\n",
+ sfs_Myname);
+ return(-1);
+ }
+ if (rpcnt != 100) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - read total percent != 100\n",
+ sfs_Myname);
+ return(-1);
+ }
+ if (wbucket == 0) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - no write distribution data\n",
+ sfs_Myname);
+ return(-1);
+ }
+ if (wpcnt != 100) {
+ (void) fprintf(stderr,
+ "%s: bad i/o dist format - write percent total != 100\n",
+ sfs_Myname);
+ return(-1);
+ }
+
+ /* allocate space for the table */
+ if ((Io_dist_ptr = (sfs_io_dist_type *)
+ malloc(sizeof(sfs_io_dist_type))) == NULL) {
+ (void) fprintf(stderr,
+ "%s: block i/o distribution table allocation failed\n",
+ sfs_Myname);
+ return(-1);
+ }
+ if ((Io_dist_ptr->read = (sfs_io_op_dist_type *)
+ malloc(rbucket*sizeof(sfs_io_op_dist_type))) == NULL) {
+ (void) fprintf(stderr,
+ "%s: read distribution table allocation failed\n", sfs_Myname);
+ return(-1);
+ }
+ if ((Io_dist_ptr->write = (sfs_io_op_dist_type *)
+ malloc(wbucket*sizeof(sfs_io_op_dist_type)))==NULL) {
+ (void) fprintf(stderr,
+ "%s: write distribution table allocation failed\n", sfs_Myname);
+ return(-1);
+ }
+
+ }
+ return(0);
+
+} /* parseiodist */
+
+
+/*
+ * Compute the max i/o transfer size and average ops per request
+ * for the block transfer distribution table.
+ */
+static void
+init_iodist(
+ sfs_io_dist_type * io_dist_ptr)
+{
+ int max_bufs;
+ double weighted_ops;
+ double previous_pcnt;
+ int i;
+
+ /*
+ * compute expected number of ops for multi-op requests.
+ * the calculation assumes that if a i/o distribution table
+ * entry specifies that a fragment is to be generated, then
+ * exactly one OTW operation will result.
+ */
+ max_bufs = 0;
+
+ weighted_ops = 0.0;
+ previous_pcnt = 0.0;
+ for (i = 0; ; i++) {
+ weighted_ops += (io_dist_ptr->read[i].pcnt - previous_pcnt) *
+ (io_dist_ptr->read[i].bufs + io_dist_ptr->read[i].frags);
+ previous_pcnt = io_dist_ptr->read[i].pcnt;
+ if (io_dist_ptr->read[i].bufs > max_bufs)
+ max_bufs = io_dist_ptr->read[i].bufs;
+ if (io_dist_ptr->read[i].pcnt == 100)
+ break;
+ }
+ io_dist_ptr->avg_ops_per_read_req = weighted_ops / 100.0;
+
+ weighted_ops = 0.0;
+ previous_pcnt = 0.0;
+ for (i = 0; ; i++) {
+ weighted_ops += (io_dist_ptr->write[i].pcnt - previous_pcnt) *
+ (io_dist_ptr->write[i].bufs + io_dist_ptr->write[i].frags);
+ previous_pcnt = io_dist_ptr->write[i].pcnt;
+ if (io_dist_ptr->write[i].bufs > max_bufs)
+ max_bufs = io_dist_ptr->write[i].bufs;
+ if (io_dist_ptr->write[i].pcnt == 100)
+ break;
+ }
+ io_dist_ptr->avg_ops_per_write_req = weighted_ops / 100.0;
+
+ io_dist_ptr->max_bufs = max_bufs + 1;
+
+} /* init_iodist */
+
+static void
+init_filedist()
+{
+ int i;
+ int cur_pcnt;
+ int num_files = 0;
+ int prev_pcnt = 0;
+ int tot_size = 0;
+
+ /*
+ * Calculate the average number of bytes per file
+ */
+ for (i = 0; Default_file_size_dist[i].size != 0; i++) {
+ cur_pcnt = Default_file_size_dist[i].pcnt - prev_pcnt;
+ num_files += cur_pcnt;
+ tot_size += (Default_file_size_dist[i].size * 1024) * cur_pcnt;
+ prev_pcnt = Default_file_size_dist[i].pcnt;
+ }
+
+ avg_bytes_per_file = tot_size / num_files;
+ files_per_megabyte = (((1024*1024) + avg_bytes_per_file) \
+ / avg_bytes_per_file);
+}
+
+static void
+init_fss()
+{
+ int Delta_fss_bytes;
+
+ Base_fss_bytes = Num_working_io_files * (avg_bytes_per_file / 1024);
+ Total_fss_bytes = Num_io_files * (avg_bytes_per_file / 1024);
+ Cur_fss_bytes = Base_fss_bytes;
+ Delta_fss_bytes = (Base_fss_bytes * Fss_delta_percent) / 100;
+ Limit_fss_bytes = Base_fss_bytes + Delta_fss_bytes;
+ Most_fss_bytes = Base_fss_bytes;
+ Least_fss_bytes = Base_fss_bytes;
+}
+
+/*
+ * return true if 'sp' contains the substring 'subsp', false otherwise
+ */
+static int
+lad_substr(
+ char * sp,
+ char * subsp)
+{
+ unsigned int found;
+ int want;
+ char * s2;
+
+ if (sp == NULL || subsp == NULL) {
+ return (0);
+ }
+
+ want = strlen(subsp);
+
+ while (*sp != '\0') {
+ while (*sp != *subsp && *sp != '\0') {
+ sp++;
+ }
+ found = 0;
+ s2 = subsp;
+ while (*sp == *s2 && *sp != '\0') {
+ sp++;
+ s2++;
+ found++;
+ }
+ if (found == want) {
+ return (1);
+ }
+ }
+ return (0);
+
+} /* lad_substr */
+
+/*
+ * Check the gettimeofday() resolution. If the resolution
+ * is in chunks bigger than SFS_MIN_RES then the client
+ * does not have a usable resolution for running the
+ * benchmark.
+ */
+static void
+check_clock(void)
+{
+ double time_res;
+ char tmp_hostname[HOSTNAME_LEN];
+
+ time_res = get_resolution();
+ getmyhostname(tmp_hostname, HOSTNAME_LEN);
+ if( time_res > (double)SFS_MIN_RES )
+ {
+ (void) fprintf(stderr,
+ "\n%s: Clock resolution too poor to obtain valid results.\n",
+ tmp_hostname);
+ (void) fprintf(stderr,
+ "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
+ time_res);
+ exit(175);
+ }
+ else
+ {
+ (void) fprintf(stderr,
+ "\n%s: Good clock resolution [ %f ] Micro seconds.\n",
+ tmp_hostname, time_res);
+ }
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns the resolution of the gettimeofday() function
+ * in microseconds.
+ */
+static double
+get_resolution(void)
+{
+ double starttime, finishtime, besttime;
+ long j,delay;
+ int k;
+
+ finishtime=time_so_far1(); /* Warm up the instruction cache */
+ starttime=time_so_far1(); /* Warm up the instruction cache */
+ delay=j=0; /* Warm up the data cache */
+ for(k=0;k<10;k++)
+ {
+ while(1)
+ {
+ starttime=time_so_far1();
+ for(j=0;j< delay;j++)
+ ;
+ finishtime=time_so_far1();
+ if(starttime==finishtime)
+ delay++;
+ else
+ {
+ if(k==0)
+ besttime=(finishtime-starttime);
+ if((finishtime-starttime) < besttime)
+ besttime=(finishtime-starttime);
+ break;
+ }
+ }
+ }
+ return(besttime);
+}
+
+/*
+ * Lifted code from Iozone with permission from author. (Don Capps)
+ * Returns current result of gettimeofday() in microseconds.
+ */
+/************************************************************************/
+/* Time measurement routines. */
+/* Return time in microseconds */
+/************************************************************************/
+
+static double
+time_so_far1(void)
+{
+ /* For Windows the time_of_day() is useless. It increments in 55 */
+ /* milli second increments. By using the Win32api one can get */
+ /* access to the high performance measurement interfaces. */
+ /* With this one can get back into the 8 to 9 microsecond */
+ /* resolution. */
+#ifdef Windows
+ LARGE_INTEGER freq,counter;
+ double wintime;
+ double bigcounter;
+
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&counter);
+ bigcounter=(double)counter.HighPart *(double)0xffffffff +
+ (double)counter.LowPart;
+ wintime = (double)(bigcounter/(double)freq.LowPart);
+ return((double)wintime*1000000.0);
+#else
+#if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
+ struct timespec gp;
+
+ if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
+ perror("getclock");
+ return (( (double) (gp.tv_sec)*1000000.0) +
+ ( ((float)(gp.tv_nsec)) * 0.001 ));
+#else
+ struct timeval tp;
+
+ if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
+ perror("gettimeofday");
+ return ((double) (tp.tv_sec)*1000000.0) +
+ (((double) tp.tv_usec) );
+#endif
+#endif
+}
+
+/* sfs_c_chd.c */
+/* sfs_c_man.c */
+
--- /dev/null
+#ifndef lint
+static char sfs_c_mntSid[] = "@(#)sfs_c_mnt.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_mnt.c ---------------------
+ *
+ * The sfs child. Routines to handle mount points.
+ *
+ *.Exported_Routines
+ * void init_mount_point(int, char *, CLIENT *)
+ *
+ *.Local_Routines
+ * int pseudo_mount(char *, int, char *, CLIENT *)
+ *
+ *.Revision_History
+ * 2-Jul-92 Teelucksingh Added code for OSF/1
+ * use of getmntinfo().
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+struct hostent *Server_hostent;
+
+/*
+ * ------------------------- Constants -------------------------
+ */
+
+/*
+ * Number of times a load generating process will retry amount.
+ * Each load generating process also picks a client shifted
+ * mount start time, and executes a backoff on retry time on
+ * failure.
+ */
+#define NUMBER_MOUNT_RETRIES 10
+
+/*
+ * ------------------------- External Definitions -------------------------
+ */
+
+/* forward definitions for local routines */
+CLIENT * lad_getmnt_hand(char *);
+static int pseudo_mount(char *, int, char *, CLIENT *);
+
+/*
+ * Mounts are retried when an RPC timeout occurs, in the mainline
+ * code. They are not retried by the RPC clnt_call routine, as the
+ * timeout values are set now.
+ */
+static struct timeval Mount_timer = { 10, 0 };
+
+
+/*
+ * ------------------------- Mount Point Routines -------------------------
+ */
+
+
+/*
+ * mount the testdir 'dirnum' under the parent directory 'parentdir'.
+ */
+void
+init_mount_point(
+ int dirnum,
+ char * parentdir,
+ CLIENT * mount_client_ptr)
+{
+ char pnt_dir[SFS_MAXPATHLEN]; /* test dir component name */
+ char testdirname[SFS_MAXPATHLEN]; /* test dir component name */
+ char export_fsname[SFS_MAXPATHLEN]; /* "host:path" exported fs */
+ sfs_fh_type file_handle;
+ char *fh_ptr;
+ char *cp;
+ int ret;
+ sfs_fh_data *fh_datap, *Ex_fh_datap;
+
+ fh_datap= calloc(1,sizeof(sfs_fh_data));
+ (void) memset((char *)fh_datap, 0, sizeof(sfs_fh_data));
+ (void) memset((char *)&file_handle, 0, sizeof(file_handle));
+ file_handle.fh_data = fh_datap;
+ file_handle.dir = &Export_dir;
+
+ Ex_fh_datap = (sfs_fh_data *) calloc(1,sizeof(sfs_fh_data));
+ Export_dir.fh_data = Ex_fh_datap;
+
+ (void) strcpy(pnt_dir, parentdir);
+
+ cp = strchr(pnt_dir, ':');
+ if (cp == NULL) {
+ (void) fprintf(stderr, "%s: malformed fsname %s\n",
+ sfs_Myname, parentdir);
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(86);
+ }
+
+ *cp++ = '\0';
+
+ /*
+ * Now we have the parent directory in the form:
+ * host:host_path
+ *
+ * First we get the file handle for parent directory
+ *
+ * Verify that the server is running the correct version of
+ * the NFS protocol specification and then proceed to get
+ * the exported fh from the server.
+ */
+ (void) strcpy(testdirname, pnt_dir);
+ (void) strcat(testdirname, ":");
+ (void) strcat(testdirname, cp);
+ (void) strcpy(export_fsname, testdirname);
+
+ if (nfs_version == NFS_VERSION) {
+ (void) memset((char *) &Export_dir.fh2, '\0', sizeof (Export_dir.fh2));
+ fh_ptr = (char *)&Export_dir.fh2;
+ } else if (nfs_version == NFS_V3) {
+ (void) memset((char *) &Export_dir.fh3, '\0', sizeof (Export_dir.fh3));
+ fh_ptr = (char *)&Export_dir.fh3;
+ }
+
+ ret = pseudo_mount(export_fsname, nfs_version,
+ fh_ptr, mount_client_ptr);
+ if (ret < 0) {
+ if (ret == -2) {
+ (void) fprintf(stderr,
+ "%s: NFS Protocol Version %lu verification failed.\n",
+ sfs_Myname, (uint32_t)nfs_version);
+ }
+ else {
+ (void) fprintf(stderr, "%s: can't pseudo mount %s\n",
+ sfs_Myname, export_fsname);
+ }
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(87);
+ }
+
+ /*
+ * Setup initial state of export directory
+ */
+ Export_dir.state = Exists;
+ (void) strcpy(Export_dir.file_name, testdirname);
+ Export_dir.dir = &Export_dir;
+
+#ifndef RFS
+ /*
+ * Check for and create the client directory. Stat it first, if not
+ * there then mkdir, if that fails with EEXIST we lost the race but
+ * that's OK.
+ */
+ if (Validate) {
+ (void) sprintf(testdirname, "%s", "validatedir");
+ } else {
+ (void) sprintf(testdirname, "CL%d", Client_num);
+ }
+
+ if ((ret = lad_lookup(&file_handle, testdirname)) == -1) {
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(88);
+ }
+
+ if (ret == 1) {
+ /*
+ * Directory doesn't exist so create it
+ * if it already exists thats OK
+ */
+ if ((ret = lad_mkdir(&file_handle, testdirname)) == -1) {
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(89);
+ }
+ /*
+ * If someone else created this out from underneath us simply
+ * lookup the result and continue on.
+ */
+ if (ret != 0 && (ret = lad_lookup(&file_handle, testdirname)) == -1) {
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(90);
+ }
+ }
+
+ /* testdirname now exists, verify it is a directory and writeable */
+ if (!fh_isdir(&file_handle) ||
+ (check_fh_access(&file_handle) == -1)) {
+ (void) fprintf(stderr,
+ "%s: %s is either not a directory or not accessible\n",
+ sfs_Myname, testdirname);
+ if (!Validate)
+ (void) generic_kill(0, SIGINT);
+ exit(91);
+ }
+
+ /*
+ * logically chdir into CL directory
+ */
+ /* Export_dir = file_handle; Implied bcopy here */
+ (void) memmove(&Export_dir,&file_handle,sizeof(sfs_fh_type));
+ Export_dir.fh_data = Ex_fh_datap;
+ (void ) memmove(Export_dir.fh_data, file_handle.fh_data,
+ sizeof(sfs_fh_data));
+ (void) memset((char *)&file_handle, 0, sizeof(file_handle));
+ (void) memset((char *)fh_datap, 0, sizeof(sfs_fh_data));
+ file_handle.fh_data = fh_datap;
+ file_handle.dir = &Export_dir;
+
+ /*
+ * Validation only occurs one directory deep so we can exit early
+ */
+ if (Validate)
+ return;
+
+ (void) sprintf(testdirname, "testdir%d", dirnum);
+
+ if ((ret = lad_lookup(&file_handle, testdirname)) == -1) {
+ (void) generic_kill(0, SIGINT);
+ exit(92);
+ }
+
+ if (ret == 1) {
+ /*
+ * Directory doesn't exist so create it
+ */
+ if (lad_mkdir(&file_handle, testdirname) != 0) {
+ (void) fprintf(stderr, "%s: Unable to create %s\n",
+ sfs_Myname, testdirname);
+ (void) generic_kill(0, SIGINT);
+ exit(93);
+ }
+ }
+
+ /* testdirname now exists, verify it is a directory and writeable */
+ if (!fh_isdir(&file_handle) ||
+ (check_fh_access(&file_handle) == -1)) {
+ (void) fprintf(stderr,
+ "%s: %s is either not a directory or not accessible\n",
+ sfs_Myname, testdirname);
+ (void) generic_kill(0, SIGINT);
+ exit(94);
+ }
+
+ /*
+ * logically chdir into testdir directory
+ */
+ /* Export_dir = file_handle;*/
+ (void) memmove(&Export_dir, &file_handle, sizeof(struct sfs_fh_type));
+ Export_dir.fh_data = Ex_fh_datap; /* Put pointer back */
+ (void) memmove(Export_dir.fh_data, file_handle.fh_data, sizeof
+ (sfs_fh_data));
+#endif
+} /* init_mount_point */
+
+/*
+ * Get the filehandle for 'mount_fsname', and return it
+ * Returns NULL for error ... not mounted || no NFS client.
+ *
+ * Children should only call this routine 1 time.
+ */
+CLIENT *
+lad_getmnt_hand(
+ char * mount_point)
+{
+ char mnt_pnt[SFS_MAXPATHLEN]; /* working buffer */
+ char host[SFS_MAXPATHLEN]; /* host with exported fs */
+ static struct hostent hp;
+ struct hostent *thp;
+ CLIENT *mount_client_ptr; /* Mount client handle */
+ char *cp;
+ int rpc_result; /* rpc call result */
+ uint32_t mount_vers = 0;
+
+ /*
+ * If the mount point is of the form host:path just use the explicit
+ * name instead of grovelling through the mount table.
+ */
+ (void) strcpy(mnt_pnt, mount_point);
+ cp = strchr(mnt_pnt, ':');
+ if (cp == NULL) {
+ (void) fprintf(stderr, "%s: malformed fsname %s\n",
+ sfs_Myname, mount_point);
+ return(NULL);
+ }
+
+ *cp++ = '\0';
+ (void) strcpy(host, mnt_pnt);
+
+ /* Verify NFS Version */
+ rpc_result = callrpc(host,
+ (uint32_t) NFS_PROGRAM,
+ (uint32_t) nfs_version,
+ (uint32_t) NFSPROC_NULL, (xdrproc_t) xdr_void,
+ (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL);
+ if (rpc_result != 0) {
+ clnt_perrno((enum clnt_stat)rpc_result);
+ (void) fprintf(stderr,
+"\nUnable to contact NFS server %s.\n", host);
+ (void) fprintf(stderr,
+"Verify NFS server daemon supporting version %u is running and\n",
+ (uint32_t)nfs_version);
+ (void) fprintf(stderr, "registered with the portmapper.\n");
+ return(NULL);
+ }
+
+ /* Get host's address */
+ if ((thp = gethostbyname(host)) == NULL) {
+ /* Failure may be due to yellow pages, try again */
+ if ((thp = gethostbyname(host)) == NULL) {
+ (void) fprintf(stderr, "%s: %s not in hosts database\n",
+ sfs_Myname, host);
+ return(NULL);
+ }
+ }
+
+ hp = *thp;
+ Server_hostent = &hp;
+
+ if (nfs_version == NFS_VERSION)
+ mount_vers = MOUNTVERS;
+ if (nfs_version == NFS_V3)
+ mount_vers = MOUNTVER3;
+
+ mount_client_ptr = lad_clnt_create(0, Server_hostent,
+ (uint32_t) MOUNTPROG,
+ mount_vers,
+ RPC_ANYSOCK, &Mount_timer);
+
+
+ if (mount_client_ptr == ((CLIENT*) NULL)) {
+ (void) fprintf(stderr,
+ "%s: portmap/mountd %s server not responding",
+ sfs_Myname, mount_point);
+ return(NULL);
+ }
+
+ mount_client_ptr->cl_auth = authunix_create_default();
+ return (mount_client_ptr);
+
+} /* lad_getmnt_hand */
+
+
+/*
+ * Get the filehandle for 'mount_fsname', and return it in 'fh_ptr'.
+ * Returns 0 for OK, -1 for error ... not mounted || no NFS client.
+ *
+ * Children should only call this routine 1 time.
+ */
+static int
+pseudo_mount(
+ char * mount_fsname,
+ int version,
+ char * fh_ptr,
+ CLIENT * mount_client_ptr)
+{
+ char * host_ptr; /* host with exported fs */
+ char * path_ptr; /* ptr to path for RPC */
+
+ struct fhstatus fhs; /* status of mountd call */
+ nfs_fh3 * fh_ptr3;
+ mountres3 mntres3; /* status of mountd call */
+ char * cp;
+ enum clnt_stat rpc_stat;
+ int tries = 0; /* Number of retries */
+ /* Space by 200ms intervals. */
+ int pacesleep = Child_num * 200;
+
+ /* Parse the fsname for host and path strings */
+ cp = strchr(mount_fsname, ':');
+
+ if (cp == NULL) {
+ (void) fprintf(stderr, "%s: malformed fsname %s\n",
+ sfs_Myname, mount_fsname);
+ return(-1);
+ }
+
+ *cp++ = '\0';
+ host_ptr = mount_fsname;
+ path_ptr = cp;
+
+ /* Check host's address */
+ if (gethostbyname(host_ptr) == NULL) {
+ /* Failure may be due to yellow pages, try again */
+ if (gethostbyname(host_ptr) == NULL) {
+ (void) fprintf(stderr, "%s: %s not in hosts database\n",
+ sfs_Myname, host_ptr);
+ return(-1);
+ }
+ }
+
+ if (DEBUG_CHILD_GENERAL) {
+ (void) fprintf(stderr, "%s: mount clnt_call\n", sfs_Myname);
+ }
+
+ /* get fhandle of remote path from host's mountd */
+
+retry_mount:
+ /*
+ * Many children on many clients hammer a server with
+ * mounts. Crude fix is to pace them. Some run rule interpretations
+ * are to have *many* children on each client. This can
+ * cause problems.
+ */
+ (void) msec_sleep(pacesleep);
+
+ if (version == NFS_VERSION) {
+ (void) memset((char *) &fhs, '\0', sizeof (fhs));
+ rpc_stat = clnt_call(mount_client_ptr, MOUNTPROC_MNT, xdr_path,
+ (char *) &path_ptr,xdr_fhstatus,(char *) &fhs,
+ Mount_timer);
+ } else if (version == NFS_V3) {
+ (void) memset((char *) &mntres3, '\0', sizeof (mntres3));
+ rpc_stat = clnt_call(mount_client_ptr, MOUNTPROC_MNT, xdr_dirpath,
+ (char *) &path_ptr, xdr_mntres3, (char *) &mntres3,
+ Mount_timer);
+ } else
+ rpc_stat = RPC_PROGVERSMISMATCH;
+
+ errno = 0;
+ if (rpc_stat != RPC_SUCCESS) {
+
+ switch (rpc_stat) {
+
+ case RPC_TIMEDOUT:
+ errno = ETIMEDOUT;
+ (void) fprintf(stderr,
+ "%s: mounting %s:%s server not responding: %s (%d)\n",
+ sfs_Myname, host_ptr, path_ptr,
+ strerror(errno), errno);
+ if (tries++ < NUMBER_MOUNT_RETRIES) {
+ /* Randomize the backoff on retry */
+ pacesleep = pacesleep + (sfs_random() % 2000);
+ goto retry_mount;
+ }
+ break;
+
+
+ case RPC_PMAPFAILURE:
+ errno = ENETDOWN; /* reasonable error */
+ (void) fprintf(stderr,
+ "%s: mounting %s portmap call failed: %s (%d)\n",
+ sfs_Myname, host_ptr, strerror(errno), errno);
+ break;
+
+ case RPC_PROGNOTREGISTERED:
+ errno = ENETDOWN; /* reasonable error */
+ (void) fprintf(stderr,
+ "%s: mounting %s nfsd not registered: %s (%d)\n",
+ sfs_Myname, host_ptr, strerror(errno), errno);
+ break;
+
+ case RPC_AUTHERROR:
+ errno = EACCES;
+ (void) fprintf(stderr,
+ "%s: mounting %s authentication failed: %s (%d)\n",
+ sfs_Myname, host_ptr, strerror(errno), errno);
+ break;
+
+ default:
+ errno = ENETDOWN; /* reasonable error */
+ (void) fprintf(stderr,
+ "%s: mounting %s:%s failed: %s (%d)\n",
+ sfs_Myname, host_ptr, path_ptr,
+ strerror(errno), errno);
+ break;
+ }
+
+ clnt_perror(mount_client_ptr, "");
+ return(-1);
+
+ } /* MOUNTPROC_MNT call failed */
+
+ if (version == NFS_VERSION) {
+ if (fhs.fhs_status != 0) {
+ if (fhs.fhs_status == EACCES) {
+ (void) fprintf(stderr, "%s: mounting %s:%s - access denied\n",
+ sfs_Myname, host_ptr, path_ptr);
+ } else {
+ (void) fprintf(stderr,
+ "%s: mounting %s:%s - bad fh status %d\n ",
+ sfs_Myname, host_ptr, path_ptr, fhs.fhs_status);
+ }
+ return(-1);
+ } /* bad fhs status */
+
+ /*
+ * fill in the caller's file handle
+ */
+ (void) memmove(fh_ptr, (char *) &fhs.fhs_fh, NFS_FHSIZE);
+
+ } else if (version == NFS_V3) {
+
+ if (mntres3.fhs_status != MNT_OK) {
+ if (mntres3.fhs_status == MNT3ERR_ACCES) {
+ (void) fprintf(stderr, "%s: mounting %s:%s - access denied\n",
+ sfs_Myname, host_ptr, path_ptr);
+ } else {
+ (void) fprintf(stderr,
+ "%s: mounting %s:%s - bad fh status %d\n ",
+ sfs_Myname, host_ptr, path_ptr, mntres3.fhs_status);
+ }
+ return(-1);
+ } /* bad fhs status */
+
+ /*
+ * fill in the caller's file handle
+ * space pointed by fhandle3_val is allocated through xdr_mntres3
+ */
+ fh_ptr3 = (nfs_fh3 *)fh_ptr;
+ fh_ptr3->fh3_length = mntres3.mntres3_u.mntinfo.fhandle.fhandle3_len;
+ (void) memmove((char *) fh_ptr3->fh3_u.data,
+ (char *) mntres3.mntres3_u.mntinfo.fhandle.fhandle3_val,
+ fh_ptr3->fh3_length);
+ }
+
+ return(0);
+
+} /* pseudo_mount */
+
+
+/* sfs_c_mnt.c */
--- /dev/null
+#ifndef __sfs_c_nfs_h
+#define __sfs_c_nfs_h
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * @(#)sfs_c_nfs.h 2.1 97/10/23
+ *
+ * -------------------------- sfs_c_nfs.h -------------------------
+ *
+ * Literals and types for the NFS protocol, Version 2.
+ *
+ *.Revision_History
+ *
+ * Richard Bean 17-May-90 Created.
+ */
+
+
+/*
+ * for RPC calls
+ */
+/* #define NFS_PORT 2049 */
+#define NFS_PROGRAM ((uint32_t)100003)
+#define NFS_VERSION ((uint32_t)2)
+#define NFS_V3 ((uint32_t)3)
+
+/*
+ * fixed sizes
+ */
+#define NFS_MAXDATA 8192
+//#define NFS_MAXDATA 16384
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_COOKIESIZE 4
+#define NFS_FHSIZE 32
+
+
+#if !defined(AIX)
+typedef struct {
+ char data[NFS_FHSIZE];
+} fhandle_t;
+#endif /* AIX */
+
+/*
+ * Definition of "standard" mount parameters
+ */
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+#define MOUNTPROC_MNT 1
+
+struct fhstatus {
+ int fhs_status;
+ fhandle_t fhs_fh;
+};
+
+typedef fhandle_t nfs_fh;
+
+/*
+ * fattr modes
+ */
+/*
+ * The mode mask is used to mask off server vendor specific mode bits
+ * from the mode field. This allows clients from different vendors to
+ * validate servers from different vendors.
+ */
+#define NFSMODE_MASK 0177777
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+
+/*
+ * NFS Procedures
+ */
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_ROOT 3
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE 7
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+#define NFS_PROCEDURE_COUNT (NFSPROC_STATFS + 1)
+
+
+/*
+ * call and return types
+ */
+enum nfsstat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22,
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_OPNOTSUPP = 45,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_REMOTE = 71,
+ NFSERR_WFLUSH = 99
+};
+typedef enum nfsstat nfsstat;
+
+
+enum ftype {
+ NFNON = 0,
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NFBAD = 7,
+ NFFIFO = 8
+};
+typedef enum ftype ftype;
+#define NUM_TYPES 5
+
+
+struct nfstime {
+ unsigned int seconds;
+ unsigned int useconds;
+};
+typedef struct nfstime nfstime;
+
+
+struct fattr {
+ ftype type;
+ unsigned int mode;
+ unsigned int nlink;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int size;
+ unsigned int blocksize;
+ unsigned int rdev;
+ unsigned int blocks;
+ unsigned int fsid;
+ unsigned int fileid;
+ nfstime atime;
+ nfstime mtime;
+ nfstime ctime;
+};
+typedef struct fattr fattr;
+
+
+struct sattr {
+ unsigned int mode;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int size;
+ nfstime atime;
+ nfstime mtime;
+};
+typedef struct sattr sattr;
+
+
+typedef char *filename;
+
+
+typedef char *nfspath;
+
+
+struct attrstat {
+ nfsstat status;
+ union {
+ fattr attributes;
+ } attrstat_u;
+};
+typedef struct attrstat attrstat;
+
+
+struct sattrargs {
+ nfs_fh file;
+ sattr attributes;
+};
+typedef struct sattrargs sattrargs;
+
+
+struct diropargs {
+ nfs_fh dir;
+ filename name;
+};
+typedef struct diropargs diropargs;
+
+
+struct diropokres {
+ nfs_fh file;
+ fattr attributes;
+};
+typedef struct diropokres diropokres;
+
+
+struct diropres {
+ nfsstat status;
+ union {
+ diropokres diropres;
+ } diropres_u;
+};
+typedef struct diropres diropres;
+
+
+struct readlinkres {
+ nfsstat status;
+ struct {
+ nfspath data;
+ int len; /* for convenience only, not in the protocol */
+ } readlinkres_u;
+};
+typedef struct readlinkres readlinkres;
+
+
+struct readargs {
+ nfs_fh file;
+ unsigned int offset;
+ unsigned int count;
+ unsigned int totalcount; /* unused field, but in the protocol */
+};
+typedef struct readargs readargs;
+
+
+struct readokres {
+ fattr attributes;
+ struct {
+ unsigned int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct readokres readokres;
+
+
+struct readres {
+ nfsstat status;
+ union {
+ readokres reply;
+ } readres_u;
+};
+typedef struct readres readres;
+
+
+struct writeargs {
+ nfs_fh file;
+ unsigned int beginoffset; /* unused field, but in the protocol */
+ unsigned int offset;
+ unsigned int totalcount; /* unused field, but in the protocol */
+ struct {
+ unsigned int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct writeargs writeargs;
+
+
+struct createargs {
+ diropargs where;
+ sattr attributes;
+};
+typedef struct createargs createargs;
+
+
+struct renameargs {
+ diropargs from;
+ diropargs to;
+};
+typedef struct renameargs renameargs;
+
+
+struct linkargs {
+ nfs_fh from;
+ diropargs to;
+};
+typedef struct linkargs linkargs;
+
+
+struct symlinkargs {
+ diropargs from;
+ nfspath to;
+ sattr attributes;
+};
+typedef struct symlinkargs symlinkargs;
+
+
+struct mkdirargs {
+ diropargs where;
+ sattr attributes;
+};
+typedef struct mkdirargs mkdirargs;
+
+
+typedef char nfscookie[NFS_COOKIESIZE];
+
+
+struct readdirargs {
+ nfs_fh dir;
+ nfscookie cookie;
+ unsigned int count;
+};
+typedef struct readdirargs readdirargs;
+
+
+struct entry {
+ bool_t valid; /* bool for entry is present */
+ unsigned int fileid;
+ uint16_t name_len;
+ filename name;
+ nfscookie cookie;
+};
+typedef struct entry entry;
+
+
+struct dirlist {
+ int max_entries; /* for convenience only, not in the protocol */
+ entry *entries; /* a stream of consecutive entry's */
+ bool_t eof;
+};
+typedef struct dirlist dirlist;
+
+
+struct readdirres {
+ nfsstat status;
+ union {
+ dirlist reply;
+ } readdirres_u;
+};
+typedef struct readdirres readdirres;
+
+
+struct statfsokres {
+ unsigned int tsize;
+ unsigned int bsize;
+ unsigned int blocks;
+ unsigned int bfree;
+ unsigned int bavail;
+};
+typedef struct statfsokres statfsokres;
+
+
+struct statfsres {
+ nfsstat status;
+ union {
+ statfsokres reply;
+ } statfsres_u;
+};
+typedef struct statfsres statfsres;
+
+
+/*
+ * Literals and types for the NFS protocol, Version 3.
+ */
+
+
+/*
+ * for RPC calls
+ */
+
+/*
+ * fixed sizes
+ */
+#define NFS3_FHSIZE 64
+#define NFS3_COOKIEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_WRITEVERFSIZE 8
+
+#define nfs3nametoolong ((char *)-1)
+
+/*
+ * Not all systems have a built in long long type so we define a
+ * special version for sfs to use.
+ */
+typedef union {
+ struct {
+ uint32_t _u;
+ uint32_t _l;
+ } _p;
+ char _f[8];
+} nfs_uint64_t;
+
+typedef char *filename3;
+
+typedef char *nfspath3;
+
+typedef char cookieverf3[NFS3_COOKIEVERFSIZE];
+
+typedef char createverf3[NFS3_CREATEVERFSIZE];
+
+typedef char writeverf3[NFS3_WRITEVERFSIZE];
+
+struct nfs_fh3 {
+ unsigned int fh3_length;
+ union nfs_fh3_u {
+ struct {
+ char _u[NFS_FHSIZE];
+ char _l[NFS_FHSIZE];
+ } _p;
+ char data[NFS3_FHSIZE];
+ } fh3_u;
+};
+#define fh3_fsid fh3_u.nfs_fh3_i.fh3_i.fh_fsid
+#define fh3_len fh3_u.nfs_fh3_i.fh3_i.fh_len /* fid length */
+#define fh3_data fh3_u.nfs_fh3_i.fh3_i.fh_data /* fid bytes */
+#define fh3_xlen fh3_u.nfs_fh3_i.fh3_i.fh_xlen
+#define fh3_xdata fh3_u.nfs_fh3_i.fh3_i.fh_xdata
+typedef struct nfs_fh3 nfs_fh3;
+
+struct diropargs3 {
+ nfs_fh3 dir;
+ filename3 name;
+};
+typedef struct diropargs3 diropargs3;
+
+struct nfstime3 {
+ uint32_t seconds;
+ uint32_t nseconds;
+};
+typedef struct nfstime3 nfstime3;
+
+struct specdata3 {
+ uint32_t specdata1;
+ uint32_t specdata2;
+};
+typedef struct specdata3 specdata3;
+
+
+/*
+ * call and return types
+ */
+enum nfsstat3 {
+ NFS3_OK = 0,
+ NFS3ERR_PERM = 1,
+ NFS3ERR_NOENT = 2,
+ NFS3ERR_IO = 5,
+ NFS3ERR_NXIO = 6,
+ NFS3ERR_ACCES = 13,
+ NFS3ERR_EXIST = 17,
+ NFS3ERR_XDEV = 18,
+ NFS3ERR_NODEV = 19,
+ NFS3ERR_NOTDIR = 20,
+ NFS3ERR_ISDIR = 21,
+ NFS3ERR_INVAL = 22,
+ NFS3ERR_FBIG = 27,
+ NFS3ERR_NOSPC = 28,
+ NFS3ERR_ROFS = 30,
+ NFS3ERR_MLINK = 31,
+ NFS3ERR_NAMETOOLONG = 63,
+ NFS3ERR_NOTEMPTY = 66,
+ NFS3ERR_DQUOT = 69,
+ NFS3ERR_STALE = 70,
+ NFS3ERR_REMOTE = 71,
+ NFS3ERR_BADHANDLE = 10001,
+ NFS3ERR_NOT_SYNC = 10002,
+ NFS3ERR_BAD_COOKIE = 10003,
+ NFS3ERR_NOTSUPP = 10004,
+ NFS3ERR_TOOSMALL = 10005,
+ NFS3ERR_SERVERFAULT = 10006,
+ NFS3ERR_BADTYPE = 10007,
+ NFS3ERR_JUKEBOX = 10008,
+ NFS3ERR_RFS_TIMEOUT = 10009, // RFS
+ NFS3ERR_RFS_MISS = 10010 // RFS reply missed in trace
+};
+typedef enum nfsstat3 nfsstat3;
+
+enum ftype3 {
+ NF3NON = 0,
+ NF3REG = 1,
+ NF3DIR = 2,
+ NF3BLK = 3,
+ NF3CHR = 4,
+ NF3LNK = 5,
+ NF3SOCK = 6,
+ NF3FIFO = 7
+};
+typedef enum ftype3 ftype3;
+#define NUM_TYPES 5
+
+
+struct fattr3 {
+ ftype3 type;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ nfs_uint64_t size;
+ nfs_uint64_t used;
+ specdata3 rdev;
+ nfs_uint64_t fsid;
+ nfs_uint64_t fileid;
+ nfstime3 atime;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+typedef struct fattr3 fattr3;
+
+struct post_op_attr {
+ bool_t attributes;
+ fattr3 attr;
+};
+typedef struct post_op_attr post_op_attr;
+
+struct wcc_attr {
+ nfs_uint64_t size;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+typedef struct wcc_attr wcc_attr;
+
+struct pre_op_attr {
+ bool_t attributes;
+ wcc_attr attr;
+};
+typedef struct pre_op_attr pre_op_attr;
+
+struct wcc_data {
+ pre_op_attr before;
+ post_op_attr after;
+};
+typedef struct wcc_data wcc_data;
+
+struct post_op_fh3 {
+ bool_t handle_follows;
+ nfs_fh3 handle;
+};
+typedef struct post_op_fh3 post_op_fh3;
+
+enum time_how {
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2
+};
+typedef enum time_how time_how;
+
+struct set_mode3 {
+ bool_t set_it;
+ uint32_t mode;
+};
+typedef struct set_mode3 set_mode3;
+
+struct set_uid3 {
+ bool_t set_it;
+ uint32_t uid;
+};
+typedef struct set_uid3 set_uid3;
+
+struct set_gid3 {
+ bool_t set_it;
+ uint32_t gid;
+};
+typedef struct set_gid3 set_gid3;
+
+struct set_size3 {
+ bool_t set_it;
+ nfs_uint64_t size;
+};
+typedef struct set_size3 set_size3;
+
+struct set_atime {
+ time_how set_it;
+ nfstime3 atime;
+};
+typedef struct set_atime set_atime;
+
+struct set_mtime {
+ time_how set_it;
+ nfstime3 mtime;
+};
+typedef struct set_mtime set_mtime;
+
+struct sattr3 {
+ set_mode3 mode;
+ set_uid3 uid;
+ set_gid3 gid;
+ set_size3 size;
+ set_atime atime;
+ set_mtime mtime;
+};
+typedef struct sattr3 sattr3;
+
+
+#define resok res_u.ok
+#define resfail res_u.fail
+
+struct GETATTR3args {
+ nfs_fh3 object;
+};
+typedef struct GETATTR3args GETATTR3args;
+
+struct GETATTR3resok {
+ fattr3 obj_attributes;
+};
+typedef struct GETATTR3resok GETATTR3resok;
+
+struct GETATTR3res {
+ nfsstat3 status;
+ union {
+ GETATTR3resok ok;
+ } res_u;
+};
+typedef struct GETATTR3res GETATTR3res;
+
+struct sattrguard3 {
+ bool_t check;
+ nfstime3 obj_ctime;
+};
+typedef struct sattrguard3 sattrguard3;
+
+struct SETATTR3args {
+ nfs_fh3 object;
+ sattr3 new_attributes;
+ sattrguard3 guard;
+};
+typedef struct SETATTR3args SETATTR3args;
+
+struct SETATTR3resok {
+ wcc_data obj_wcc;
+};
+typedef struct SETATTR3resok SETATTR3resok;
+
+struct SETATTR3resfail {
+ wcc_data obj_wcc;
+};
+typedef struct SETATTR3resfail SETATTR3resfail;
+
+struct SETATTR3res {
+ nfsstat3 status;
+ union {
+ SETATTR3resok ok;
+ SETATTR3resfail fail;
+ } res_u;
+};
+typedef struct SETATTR3res SETATTR3res;
+
+struct LOOKUP3args {
+ diropargs3 what;
+};
+typedef struct LOOKUP3args LOOKUP3args;
+
+struct LOOKUP3resok {
+ nfs_fh3 object;
+ post_op_attr obj_attributes;
+ post_op_attr dir_attributes;
+};
+typedef struct LOOKUP3resok LOOKUP3resok;
+
+struct LOOKUP3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct LOOKUP3resfail LOOKUP3resfail;
+
+struct LOOKUP3res {
+ nfsstat3 status;
+ union {
+ LOOKUP3resok ok;
+ LOOKUP3resfail fail;
+ } res_u;
+};
+typedef struct LOOKUP3res LOOKUP3res;
+
+struct ACCESS3args {
+ nfs_fh3 object;
+ uint32_t access;
+};
+typedef struct ACCESS3args ACCESS3args;
+#define ACCESS3_READ 0x1
+#define ACCESS3_LOOKUP 0x2
+#define ACCESS3_MODIFY 0x4
+#define ACCESS3_EXTEND 0x8
+#define ACCESS3_DELETE 0x10
+#define ACCESS3_EXECUTE 0x20
+
+struct ACCESS3resok {
+ post_op_attr obj_attributes;
+ uint32_t access;
+};
+typedef struct ACCESS3resok ACCESS3resok;
+
+struct ACCESS3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct ACCESS3resfail ACCESS3resfail;
+
+struct ACCESS3res {
+ nfsstat3 status;
+ union {
+ ACCESS3resok ok;
+ ACCESS3resfail fail;
+ } res_u;
+};
+typedef struct ACCESS3res ACCESS3res;
+
+struct READLINK3args {
+ nfs_fh3 symlink;
+};
+typedef struct READLINK3args READLINK3args;
+
+struct READLINK3resok {
+ post_op_attr symlink_attributes;
+ nfspath3 data;
+};
+typedef struct READLINK3resok READLINK3resok;
+
+struct READLINK3resfail {
+ post_op_attr symlink_attributes;
+};
+typedef struct READLINK3resfail READLINK3resfail;
+
+struct READLINK3res {
+ nfsstat3 status;
+ union {
+ READLINK3resok ok;
+ READLINK3resfail fail;
+ } res_u;
+};
+typedef struct READLINK3res READLINK3res;
+
+struct READ3args {
+ nfs_fh3 file;
+ nfs_uint64_t offset;
+ uint32_t count;
+};
+typedef struct READ3args READ3args;
+
+struct READ3resok {
+ post_op_attr file_attributes;
+ uint32_t count;
+ bool_t eof;
+ struct {
+ unsigned int data_len;
+ char *data_val;
+ } data;
+ unsigned int size;
+};
+typedef struct READ3resok READ3resok;
+
+struct READ3resfail {
+ post_op_attr file_attributes;
+};
+typedef struct READ3resfail READ3resfail;
+
+struct READ3res {
+ nfsstat3 status;
+ union {
+ READ3resok ok;
+ READ3resfail fail;
+ } res_u;
+};
+typedef struct READ3res READ3res;
+
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2
+};
+typedef enum stable_how stable_how;
+
+struct WRITE3args {
+ nfs_fh3 file;
+ nfs_uint64_t offset;
+ uint32_t count;
+ stable_how stable;
+ struct {
+ unsigned int data_len;
+ char *data_val;
+ } data;
+};
+typedef struct WRITE3args WRITE3args;
+
+struct WRITE3resok {
+ wcc_data file_wcc;
+ uint32_t count;
+ stable_how committed;
+ writeverf3 verf;
+};
+typedef struct WRITE3resok WRITE3resok;
+
+struct WRITE3resfail {
+ wcc_data file_wcc;
+};
+typedef struct WRITE3resfail WRITE3resfail;
+
+struct WRITE3res {
+ nfsstat3 status;
+ union {
+ WRITE3resok ok;
+ WRITE3resfail fail;
+ } res_u;
+};
+typedef struct WRITE3res WRITE3res;
+
+enum createmode3 {
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2
+};
+typedef enum createmode3 createmode3;
+
+struct createhow3 {
+ createmode3 mode;
+ union {
+ sattr3 obj_attributes;
+ createverf3 verf;
+ } createhow3_u;
+};
+typedef struct createhow3 createhow3;
+
+struct CREATE3args {
+ diropargs3 where;
+ createhow3 how;
+};
+typedef struct CREATE3args CREATE3args;
+
+struct CREATE3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct CREATE3resok CREATE3resok;
+
+struct CREATE3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct CREATE3resfail CREATE3resfail;
+
+struct CREATE3res {
+ nfsstat3 status;
+ union {
+ CREATE3resok ok;
+ CREATE3resfail fail;
+ } res_u;
+};
+typedef struct CREATE3res CREATE3res;
+
+struct MKDIR3args {
+ diropargs3 where;
+ sattr3 attributes;
+};
+typedef struct MKDIR3args MKDIR3args;
+
+struct MKDIR3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct MKDIR3resok MKDIR3resok;
+
+struct MKDIR3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct MKDIR3resfail MKDIR3resfail;
+
+struct MKDIR3res {
+ nfsstat3 status;
+ union {
+ MKDIR3resok ok;
+ MKDIR3resfail fail;
+ } res_u;
+};
+typedef struct MKDIR3res MKDIR3res;
+
+struct symlinkdata3 {
+ sattr3 symlink_attributes;
+ nfspath3 symlink_data;
+};
+typedef struct symlinkdata3 symlinkdata3;
+
+struct SYMLINK3args {
+ diropargs3 where;
+ symlinkdata3 symlink;
+};
+typedef struct SYMLINK3args SYMLINK3args;
+
+struct SYMLINK3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct SYMLINK3resok SYMLINK3resok;
+
+struct SYMLINK3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct SYMLINK3resfail SYMLINK3resfail;
+
+struct SYMLINK3res {
+ nfsstat3 status;
+ union {
+ SYMLINK3resok ok;
+ SYMLINK3resfail fail;
+ } res_u;
+};
+typedef struct SYMLINK3res SYMLINK3res;
+
+struct devicedata3 {
+ sattr3 dev_attributes;
+ specdata3 spec;
+};
+typedef struct devicedata3 devicedata3;
+
+struct mknoddata3 {
+ ftype3 type;
+ union {
+ devicedata3 device;
+ sattr3 pipe_attributes;
+ } mknoddata3_u;
+};
+typedef struct mknoddata3 mknoddata3;
+
+struct MKNOD3args {
+ diropargs3 where;
+ mknoddata3 what;
+};
+typedef struct MKNOD3args MKNOD3args;
+
+struct MKNOD3resok {
+ post_op_fh3 obj;
+ post_op_attr obj_attributes;
+ wcc_data dir_wcc;
+};
+typedef struct MKNOD3resok MKNOD3resok;
+
+struct MKNOD3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct MKNOD3resfail MKNOD3resfail;
+
+struct MKNOD3res {
+ nfsstat3 status;
+ union {
+ MKNOD3resok ok;
+ MKNOD3resfail fail;
+ } res_u;
+};
+typedef struct MKNOD3res MKNOD3res;
+
+struct REMOVE3args {
+ diropargs3 object;
+};
+typedef struct REMOVE3args REMOVE3args;
+
+struct REMOVE3resok {
+ wcc_data dir_wcc;
+};
+typedef struct REMOVE3resok REMOVE3resok;
+
+struct REMOVE3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct REMOVE3resfail REMOVE3resfail;
+
+struct REMOVE3res {
+ nfsstat3 status;
+ union {
+ REMOVE3resok ok;
+ REMOVE3resfail fail;
+ } res_u;
+};
+typedef struct REMOVE3res REMOVE3res;
+
+struct RMDIR3args {
+ diropargs3 object;
+};
+typedef struct RMDIR3args RMDIR3args;
+
+struct RMDIR3resok {
+ wcc_data dir_wcc;
+};
+typedef struct RMDIR3resok RMDIR3resok;
+
+struct RMDIR3resfail {
+ wcc_data dir_wcc;
+};
+typedef struct RMDIR3resfail RMDIR3resfail;
+
+struct RMDIR3res {
+ nfsstat3 status;
+ union {
+ RMDIR3resok ok;
+ RMDIR3resfail fail;
+ } res_u;
+};
+typedef struct RMDIR3res RMDIR3res;
+
+struct RENAME3args {
+ diropargs3 from;
+ diropargs3 to;
+};
+typedef struct RENAME3args RENAME3args;
+
+struct RENAME3resok {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+typedef struct RENAME3resok RENAME3resok;
+
+struct RENAME3resfail {
+ wcc_data fromdir_wcc;
+ wcc_data todir_wcc;
+};
+typedef struct RENAME3resfail RENAME3resfail;
+
+struct RENAME3res {
+ nfsstat3 status;
+ union {
+ RENAME3resok ok;
+ RENAME3resfail fail;
+ } res_u;
+};
+typedef struct RENAME3res RENAME3res;
+
+struct LINK3args {
+ nfs_fh3 file;
+ diropargs3 link;
+};
+typedef struct LINK3args LINK3args;
+
+struct LINK3resok {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+typedef struct LINK3resok LINK3resok;
+
+struct LINK3resfail {
+ post_op_attr file_attributes;
+ wcc_data linkdir_wcc;
+};
+typedef struct LINK3resfail LINK3resfail;
+
+struct LINK3res {
+ nfsstat3 status;
+ union {
+ LINK3resok ok;
+ LINK3resfail fail;
+ } res_u;
+};
+typedef struct LINK3res LINK3res;
+
+struct READDIR3args {
+ nfs_fh3 dir;
+ nfs_uint64_t cookie;
+ cookieverf3 cookieverf;
+ uint32_t count;
+};
+typedef struct READDIR3args READDIR3args;
+
+struct entry3 {
+ nfs_uint64_t fileid;
+ char name[SFS_MAXNAMLEN];
+ nfs_uint64_t cookie;
+};
+typedef struct entry3 entry3;
+
+struct dirlist3 {
+ entry3 *entries;
+ bool_t eof;
+};
+typedef struct dirlist3 dirlist3;
+
+struct READDIR3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlist3 reply;
+ unsigned int size;
+ unsigned int count;
+ nfs_uint64_t cookie;
+};
+typedef struct READDIR3resok READDIR3resok;
+
+struct READDIR3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct READDIR3resfail READDIR3resfail;
+
+struct READDIR3res {
+ nfsstat3 status;
+ union {
+ READDIR3resok ok;
+ READDIR3resfail fail;
+ } res_u;
+};
+typedef struct READDIR3res READDIR3res;
+
+struct READDIRPLUS3args {
+ nfs_fh3 dir;
+ nfs_uint64_t cookie;
+ cookieverf3 cookieverf;
+ uint32_t dircount;
+ uint32_t maxcount;
+};
+typedef struct READDIRPLUS3args READDIRPLUS3args;
+
+struct entryplus3 {
+ nfs_uint64_t fileid;
+ char name[SFS_MAXNAMLEN];
+ nfs_uint64_t cookie;
+ post_op_attr name_attributes;
+ post_op_fh3 name_handle;
+};
+typedef struct entryplus3 entryplus3;
+
+struct dirlistplus3 {
+ entryplus3 *entries;
+ bool_t eof;
+};
+typedef struct dirlistplus3 dirlistplus3;
+
+struct READDIRPLUS3resok {
+ post_op_attr dir_attributes;
+ cookieverf3 cookieverf;
+ dirlistplus3 reply;
+ unsigned int size;
+ unsigned int count;
+ unsigned int maxcount;
+ post_op_attr *attributes;
+ post_op_fh3 *handles;
+};
+typedef struct READDIRPLUS3resok READDIRPLUS3resok;
+
+struct READDIRPLUS3resfail {
+ post_op_attr dir_attributes;
+};
+typedef struct READDIRPLUS3resfail READDIRPLUS3resfail;
+
+struct READDIRPLUS3res {
+ nfsstat3 status;
+ union {
+ READDIRPLUS3resok ok;
+ READDIRPLUS3resfail fail;
+ } res_u;
+};
+typedef struct READDIRPLUS3res READDIRPLUS3res;
+
+struct FSSTAT3args {
+ nfs_fh3 fsroot;
+};
+typedef struct FSSTAT3args FSSTAT3args;
+
+struct FSSTAT3resok {
+ post_op_attr obj_attributes;
+ nfs_uint64_t tbytes;
+ nfs_uint64_t fbytes;
+ nfs_uint64_t abytes;
+ nfs_uint64_t tfiles;
+ nfs_uint64_t ffiles;
+ nfs_uint64_t afiles;
+ uint32_t invarsec;
+};
+typedef struct FSSTAT3resok FSSTAT3resok;
+
+struct FSSTAT3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct FSSTAT3resfail FSSTAT3resfail;
+
+struct FSSTAT3res {
+ nfsstat3 status;
+ union {
+ FSSTAT3resok ok;
+ FSSTAT3resfail fail;
+ } res_u;
+};
+typedef struct FSSTAT3res FSSTAT3res;
+
+struct FSINFO3args {
+ nfs_fh3 fsroot;
+};
+typedef struct FSINFO3args FSINFO3args;
+
+struct FSINFO3resok {
+ post_op_attr obj_attributes;
+ uint32_t rtmax;
+ uint32_t rtpref;
+ uint32_t rtmult;
+ uint32_t wtmax;
+ uint32_t wtpref;
+ uint32_t wtmult;
+ uint32_t dtpref;
+ nfs_uint64_t maxfilesize;
+ nfstime3 time_delta;
+ uint32_t properties;
+};
+typedef struct FSINFO3resok FSINFO3resok;
+
+struct FSINFO3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct FSINFO3resfail FSINFO3resfail;
+#define FSF3_LINK 0x1
+#define FSF3_SYMLINK 0x2
+#define FSF3_HOMOGENEOUS 0x8
+#define FSF3_CANSETTIME 0x10
+
+struct FSINFO3res {
+ nfsstat3 status;
+ union {
+ FSINFO3resok ok;
+ FSINFO3resfail fail;
+ } res_u;
+};
+typedef struct FSINFO3res FSINFO3res;
+
+struct PATHCONF3args {
+ nfs_fh3 object;
+};
+typedef struct PATHCONF3args PATHCONF3args;
+
+struct PATHCONF3resok {
+ post_op_attr obj_attributes;
+ uint32_t link_max;
+ uint32_t name_max;
+ bool_t no_trunc;
+ bool_t chown_restricted;
+ bool_t case_insensitive;
+ bool_t case_preserving;
+};
+typedef struct PATHCONF3resok PATHCONF3resok;
+
+struct PATHCONF3resfail {
+ post_op_attr obj_attributes;
+};
+typedef struct PATHCONF3resfail PATHCONF3resfail;
+
+struct PATHCONF3res {
+ nfsstat3 status;
+ union {
+ PATHCONF3resok ok;
+ PATHCONF3resfail fail;
+ } res_u;
+};
+typedef struct PATHCONF3res PATHCONF3res;
+
+struct COMMIT3args {
+ nfs_fh3 file;
+ nfs_uint64_t offset;
+ uint32_t count;
+};
+typedef struct COMMIT3args COMMIT3args;
+
+struct COMMIT3resok {
+ wcc_data file_wcc;
+ writeverf3 verf;
+};
+typedef struct COMMIT3resok COMMIT3resok;
+
+struct COMMIT3resfail {
+ wcc_data file_wcc;
+};
+typedef struct COMMIT3resfail COMMIT3resfail;
+
+struct COMMIT3res {
+ nfsstat3 status;
+ union {
+ COMMIT3resok ok;
+ COMMIT3resfail fail;
+ } res_u;
+};
+typedef struct COMMIT3res COMMIT3res;
+
+/*
+ * NFS Procedures
+ */
+#define NFSPROC3_NULL 0
+#define NFSPROC3_GETATTR 1
+#define NFSPROC3_SETATTR 2
+#define NFSPROC3_LOOKUP 3
+#define NFSPROC3_ACCESS 4
+#define NFSPROC3_READLINK 5
+#define NFSPROC3_READ 6
+#define NFSPROC3_WRITE 7
+#define NFSPROC3_CREATE 8
+#define NFSPROC3_MKDIR 9
+#define NFSPROC3_SYMLINK 10
+#define NFSPROC3_MKNOD 11
+#define NFSPROC3_REMOVE 12
+#define NFSPROC3_RMDIR 13
+#define NFSPROC3_RENAME 14
+#define NFSPROC3_LINK 15
+#define NFSPROC3_READDIR 16
+#define NFSPROC3_READDIRPLUS 17
+#define NFSPROC3_FSSTAT 18
+#define NFSPROC3_FSINFO 19
+#define NFSPROC3_PATHCONF 20
+#define NFSPROC3_COMMIT 21
+#define NFS3_PROCEDURE_COUNT (NFSPROC3_COMMIT + 1)
+
+
+/*
+ * mount.h definitions
+ */
+#define MOUNTVER3 ((uint32_t)3)
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+
+#define MOUNTPROC_NULL ((uint32_t)(0))
+#define MOUNTPROC_DUMP ((uint32_t)(2))
+#define MOUNTPROC_UMNT ((uint32_t)(3))
+#define MOUNTPROC_UMNTALL ((uint32_t)(4))
+#define MOUNTPROC_EXPORT ((uint32_t)(5))
+#define MOUNTPROC_EXPORTALL ((uint32_t)(6))
+#define MOUNTPROC_PATHCONF ((uint32_t)(7))
+
+struct fhandle3 {
+ unsigned int fhandle3_len;
+ char *fhandle3_val;
+};
+typedef struct fhandle3 fhandle3;
+
+enum mntstat3 {
+ MNT_OK = 0,
+ MNT3ERR_PERM = 1,
+ MNT3ERR_NOENT = 2,
+ MNT3ERR_IO = 5,
+ MNT3ERR_ACCES = 13,
+ MNT3ERR_NOTDIR = 20,
+ MNT3ERR_INVAL = 22,
+ MNT3ERR_NAMETOOLONG = 63,
+ MNT3ERR_NOTSUPP = 10004,
+ MNT3ERR_SERVERFAULT = 10006
+};
+typedef enum mntstat3 mntstat3;
+
+struct mntres3_ok {
+ fhandle3 fhandle;
+ struct {
+ unsigned int auth_flavors_len;
+ int *auth_flavors_val;
+ } auth_flavors;
+};
+typedef struct mntres3_ok mntres3_ok;
+
+struct mountres3 {
+ mntstat3 fhs_status;
+ union {
+ mntres3_ok mntinfo;
+ } mntres3_u;
+};
+typedef struct mountres3 mountres3;
+
+typedef char *dirpath;
+
+
+/*
+ * External XDR functions
+ */
+
+/*
+ * Mount protocol
+ */
+extern bool_t xdr_path(XDR *, char **);
+extern bool_t xdr_fhstatus(XDR *, struct fhstatus *);
+extern bool_t xdr_dirpath(XDR *, dirpath *);
+extern bool_t xdr_mntres3(XDR *, mountres3 *);
+
+/*
+ * V2
+ */
+extern bool_t xdr_create(XDR *, char *);
+extern bool_t xdr_getattr(XDR *xdrs, char *);
+extern bool_t xdr_link(XDR *xdrs, char *);
+extern bool_t xdr_lookup(XDR *xdrs, char *);
+extern bool_t xdr_mkdir(XDR *xdrs, char *);
+extern bool_t xdr_read(XDR *xdrs, char *);
+extern bool_t xdr_readdir(XDR *xdrs, char *);
+extern bool_t xdr_readlink(XDR *xdrs, char *);
+extern bool_t xdr_remove(XDR *xdrs, char *);
+extern bool_t xdr_rename(XDR *xdrs, char *);
+extern bool_t xdr_rmdir(XDR *xdrs, char *);
+extern bool_t xdr_setattr(XDR *xdrs, char *);
+extern bool_t xdr_statfs(XDR *xdrs, char *);
+extern bool_t xdr_symlink(XDR *xdrs, char *);
+extern bool_t xdr_write(XDR *xdrs, char *);
+
+/*
+ * V3
+ */
+extern bool_t xdr_GETATTR3args(XDR *, GETATTR3args *);
+extern bool_t xdr_GETATTR3res(XDR *, GETATTR3res *);
+extern bool_t xdr_SETATTR3args(XDR *, SETATTR3args *);
+extern bool_t xdr_SETATTR3res(XDR *, SETATTR3res *);
+extern bool_t xdr_LOOKUP3args(XDR *, LOOKUP3args *);
+extern bool_t xdr_LOOKUP3res(XDR *, LOOKUP3res *);
+extern bool_t xdr_ACCESS3args(XDR *, ACCESS3args *);
+extern bool_t xdr_ACCESS3res(XDR *, ACCESS3res *);
+extern bool_t xdr_READLINK3args(XDR *, READLINK3args *);
+extern bool_t xdr_READLINK3res(XDR *, READLINK3res *);
+extern bool_t xdr_READ3args(XDR *, READ3args *);
+extern bool_t xdr_READ3res(XDR *, READ3res *);
+extern bool_t xdr_WRITE3args(XDR *, WRITE3args *);
+extern bool_t xdr_WRITE3res(XDR *, WRITE3res *);
+extern bool_t xdr_CREATE3args(XDR *, CREATE3args *);
+extern bool_t xdr_CREATE3res(XDR *, CREATE3res *);
+extern bool_t xdr_MKDIR3args(XDR *, MKDIR3args *);
+extern bool_t xdr_MKDIR3res(XDR *, MKDIR3res *);
+extern bool_t xdr_SYMLINK3args(XDR *, SYMLINK3args *);
+extern bool_t xdr_SYMLINK3res(XDR *, SYMLINK3res *);
+extern bool_t xdr_MKNOD3args(XDR *, MKNOD3args *);
+extern bool_t xdr_MKNOD3res(XDR *, MKNOD3res *);
+extern bool_t xdr_REMOVE3args(XDR *, REMOVE3args *);
+extern bool_t xdr_REMOVE3res(XDR *, REMOVE3res *);
+extern bool_t xdr_RMDIR3args(XDR *, RMDIR3args *);
+extern bool_t xdr_RMDIR3res(XDR *, RMDIR3res *);
+extern bool_t xdr_RENAME3args(XDR *, RENAME3args *);
+extern bool_t xdr_RENAME3res(XDR *, RENAME3res *);
+extern bool_t xdr_LINK3args(XDR *, LINK3args *);
+extern bool_t xdr_LINK3res(XDR *, LINK3res *);
+extern bool_t xdr_READDIR3args(XDR *, READDIR3args *);
+extern bool_t xdr_READDIR3res(XDR *, READDIR3res *);
+extern bool_t xdr_READDIRPLUS3args(XDR *, READDIRPLUS3args *);
+extern bool_t xdr_READDIRPLUS3res(XDR *, READDIRPLUS3res *);
+extern bool_t xdr_FSSTAT3args(XDR *, FSSTAT3args *);
+extern bool_t xdr_FSSTAT3res(XDR *, FSSTAT3res *);
+extern bool_t xdr_FSINFO3args(XDR *, FSINFO3args *);
+extern bool_t xdr_FSINFO3res(XDR *, FSINFO3res *);
+extern bool_t xdr_PATHCONF3args(XDR *, PATHCONF3args *);
+extern bool_t xdr_PATHCONF3res(XDR *, PATHCONF3res *);
+extern bool_t xdr_COMMIT3args(XDR *, COMMIT3args *);
+extern bool_t xdr_COMMIT3res(XDR *, COMMIT3res *);
+
+
+#endif /* __sfs_c_nfs_h */
--- /dev/null
+#ifndef lint
+static char sfs_c_pntSid[] = "@(#)sfs_c_pnt.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ *
+ *.Exported_Routines
+ * void parent(int, int, char *, char *)
+ *
+ *.Local_Routines
+ * void synchronize_children(int)
+ * int signal_Prime_Client(char *, char *)
+ * void collect_counters(int)
+ * int check_parameters(char *, char *, int)
+ * int check_counters(void)
+ * void print_results(int, int, char *, int, int, char *)
+ *
+ *.Revision_History
+ * 10-Jan-92 Teelucksingh
+ * Client passes standard deviation compute
+ * values to Prime-Client as well as an
+ * "INVALID RUN" flag.
+ *
+ * 16-Dec-91 Wittle Created.
+ */
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+#if !defined(_XOPEN_SOURCE)
+#include <sys/socket.h>
+#else
+#define AF_INET 2
+#endif
+
+/*
+ * ------------------------- External Definitions -------------------------
+ */
+
+/* forward definitions for local routines */
+static void synchronize_children(int);
+static int signal_Prime_Client(char *, char *);
+static void collect_counters(int);
+static int check_parameters(char *, char *, int);
+static int check_counters(void);
+static void print_results(int, int, char *, int, int, char *);
+static void sfs_reaper(int);
+
+/* Aggregate results storage */
+static char Client_results[(NOPS+3)*MAX_LINE_LEN];
+
+/*
+ * ------------------------- SFS Parent Code -------------------------
+ */
+
+/*
+ * Parent: wait for kids to get ready, start them, wait for them to
+ * finish, read and accumulate results.
+ */
+void
+parent(
+ int children,
+ int load,
+ char * mix_file,
+ char * iodist_file)
+{
+ char string[80]; /* for interactive startup */
+ int result;
+ int invalid_run; /* holds INVALID RUN status */
+ int runtime_val; /* store Runtime value to be printed later */
+ int Saveerrno;
+ char *nameptr;
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+#endif
+
+ /*
+ * Setup a SIGCHLD handler in case one of our beloved children dies
+ * before its time.
+ */
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ /* use XOPEN signal handling */
+
+ sig_act.sa_handler = sfs_reaper;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(66);
+ }
+#else
+ (void) signal(SIGCHLD, sfs_reaper);
+#endif
+
+ /* Change my name for error logging */
+ if ((nameptr = strrchr(sfs_Myname, '/')) != NULL)
+ sfs_Myname = ++nameptr;
+
+ /*
+ * store the Runtime value; to be printed in results
+ */
+ if (Prime_client)
+ runtime_val = Runtime - MULTICLIENT_OFFSET;
+ else runtime_val = Runtime;
+
+ /* print logfile header information */
+ (void) fprintf(stdout,"\n");
+ (void) fprintf(stdout,
+ "************************************************************************");
+ (void) fprintf(stdout,"\n");
+ (void) fflush(stdout);
+
+ /* print sfs information */
+ if (Prime_client) {
+ (void) fprintf(stderr,
+ "\nSFS NFS Version %d Benchmark Client Logfile, %s\n",
+ nfs_version, lad_timestamp());
+ (void) fprintf(stderr, "\tClient hostname = %s\n", lad_hostname);
+ (void) fprintf(stderr, "\tPrime Client hostname = %s\n",
+ Prime_client);
+ }
+
+ (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n",
+ SFS_VERSION_NUM, SFS_VERSION_DATE);
+ (void) fprintf(stderr, "NFS Protocol Version %d\n", nfs_version);
+
+ /* mount test directories */
+ (void) fprintf(stderr, "%s Mounting %d remote test directories.\n",
+ lad_timestamp(), children);
+ synchronize_children(children);
+ (void) fprintf(stderr, "%s Completed.", lad_timestamp());
+
+ /*
+ * if multi-client execution then tell Prime-Client I'm done mounting
+ * test directories.
+ */
+ if (Prime_client) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Sending DONE-MOUNT message to Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ if ((result =
+ (int) signal_Prime_Client("CLIENT_SIGNAL", ""))
+ == (int) RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s Completed.",lad_timestamp());
+ (void) fflush(stderr);
+ } else {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s: error %d sending DONE-MOUNT message to Prime Client\n",
+ sfs_Myname, result);
+ /* cleanup and exit */
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ sig_act.sa_handler = SIG_DFL;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(67);
+ }
+#else
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ (void) generic_kill(0, SIGINT);
+ exit(68);
+ }
+
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Waiting on DO-INIT message from Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ (void) fflush(stderr);
+
+ /*
+ * wait for DO-INIT message from Prime Client
+ * sfs_syncd (rpc server) sends a SIGUSR1 signal;
+ * user can also terminate experiment anytime they wish
+ * with SIGINT or SIGTERM signal
+ */
+ (void) pause();
+ (void) fprintf(stderr, "%s Received.",lad_timestamp());
+ (void) fflush(stderr);
+
+ } /* send DONE-MOUNT and got DO-INIT message */
+
+ /* initialize test directories */
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr, "%s Initializing test directories.\n",
+ lad_timestamp());
+
+ /* send SIGUSR1 to child processes */
+ (void) generic_kill(0, SIGUSR1);
+ synchronize_children(children);
+ (void) fprintf(stderr, "%s Completed.", lad_timestamp());
+ (void) fflush(stderr);
+
+ /*
+ * if multi-client execution then tell Prime-Client I'm done initializing
+ * and wait for synchronized do warmupmessage.
+ */
+ if (Prime_client) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Sending DONE-INIT message to Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ if ((result =
+ (int) signal_Prime_Client("CLIENT_SIGNAL",""))
+ == (int) RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s Completed.",lad_timestamp());
+ (void) fflush(stderr);
+ } else {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s: error %d sending DONE-INIT message to Prime Client\n",
+ sfs_Myname, result);
+ /* cleanup and exit */
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ sig_act.sa_handler = SIG_DFL;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(69);
+ }
+#else
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ (void) generic_kill(0, SIGINT);
+ exit(70);
+ }
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Waiting on DO-WARMUP message from Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ (void) fflush(stderr);
+
+ /*
+ * wait for DO-WARMUP message from Prime Client
+ * sfs_syncd (rpc server) sends a SIGUSR1 signal;
+ * user can also terminate experiment anytime they wish
+ * with SIGINT or SIGTERM signal
+ */
+ (void) pause();
+ (void) fprintf(stderr, "%s Received.",lad_timestamp());
+ (void) fflush(stderr);
+
+ } /* send DONE-INIT and got DO-WARMUP message */
+
+ if (Populate_only) {
+ (void) fprintf(stderr, "\nPopulating directories and exiting.\n");
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ sig_act.sa_handler = SIG_DFL;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(71);
+ }
+#else
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ (void) generic_kill(0, SIGUSR1);
+ while (wait((int *) 0) != -1) {
+ /* nop */
+ }
+ return;
+ }
+
+ /* do warm-up */
+ if (Warmuptime) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr, "%s Performing %d seconds pretest warmup.\n",
+ lad_timestamp(), Warmuptime);
+ (void) generic_kill(0, SIGUSR1);
+ (void) sleep(Warmuptime);
+ (void) fprintf(stderr, "%s Completed.", lad_timestamp());
+ (void) fflush(stderr);
+ }
+
+ if (Interactive) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr, "Hit <return> when ready to start test ...");
+ (void) fgets(string,10,stdin);
+ }
+
+ /*
+ * if multi-client execution then tell Prime-Client I'm done warm-up
+ * and wait for synchronized Start message.
+ */
+ if (Prime_client) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Sending READY message to Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ if ((result =
+ (int) signal_Prime_Client("CLIENT_SIGNAL",""))
+ == (int) RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s Completed.",lad_timestamp());
+ (void) fflush(stderr);
+ } else {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s: error %d sending READY message to Prime Client\n",
+ sfs_Myname, result);
+ /* cleanup and exit */
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ sig_act.sa_handler = SIG_DFL;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(72);
+ }
+#else
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ (void) generic_kill(0, SIGINT);
+ exit(73);
+ }
+
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Waiting on START message from Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ (void) fflush(stderr);
+
+ /*
+ * wait for START message from Prime Client
+ * sfs_syncd (rpc server) sends a SIGUSR1 signal;
+ * user can also terminate experiment anytime they wish
+ * with SIGINT or SIGTERM signal
+ */
+ (void) pause();
+ (void) fprintf(stderr, "%s Received.",lad_timestamp());
+ (void) fflush(stderr);
+
+ } /* send READY and got START message */
+
+ (void) fprintf(stderr, "\n");
+ if (Timed_run) {
+ if (Prime_client) {
+ (void) fprintf(stderr, "%s Starting %d seconds test run.\n",
+ lad_timestamp(), Runtime - MULTICLIENT_OFFSET);
+ } else {
+ (void) fprintf(stderr, "%s Starting %d seconds test run.\n",
+ lad_timestamp(), Runtime);
+ }
+ } else {
+ (void) fprintf(stderr, "%s Starting %d call test run.\n",
+ lad_timestamp(), Ops[TOTAL].target_calls);
+ }
+ (void) fflush(stderr);
+
+ /* signal child processes to go */
+ (void) generic_kill(0, SIGUSR1);
+
+ if (Timed_run)
+ (void) sleep(Runtime);
+
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ sig_act.sa_handler = SIG_DFL;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (sigaction(SIGCHLD,&sig_act,&old_sig_act) == -1) {
+ perror("sigaction failed: SIGCHLD");
+ exit(74);
+ }
+#else
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+
+ if (Timed_run) {
+ /*
+ * The parent and the prime are both sleeping for Runtime.
+ * If the parent wakes up first, he'll tell the children to stop.
+ * If the prime wakes up first, he'll send an SIGALRM (via syncd)
+ * to the parent. That alarm may arrive while the parent is still
+ * asleep, which is ok, or after he has starting running. Since
+ * the parent SIGARLM catcher does nothing, there is no harm done
+ * by the extra signal in this case.
+ *
+ * Perhaps, if running multi we should just wait (pause()) for
+ * the STOP signal, like we waited for the start signal. It would
+ * be more obvious. The only drawback is the OTW rpc delay in
+ * receiving the stop signal from the prime.
+ */
+ (void) generic_kill(0, SIGUSR2); /* tell children to finish */
+ }
+
+ /* Wait for all the children to finish/die */
+ while (wait((int *) 0) != -1) {
+ /* nop */
+ }
+
+ (void) fprintf(stderr, "%s Completed.", lad_timestamp());
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+
+ /* Initialize and sum up counters */
+ collect_counters(children);
+ if ((invalid_run = check_counters()) == 0)
+ invalid_run = check_parameters(iodist_file, mix_file, runtime_val);
+
+ /* print test results */
+ print_results(children, load, mix_file,
+ invalid_run, runtime_val, iodist_file);
+
+ /*
+ * if multi-client execution then tell Prime client that
+ * I'm done with 'real' work and wait for move-data message
+ * and send data across
+ */
+ if (Prime_client) {
+ (void) fprintf(stderr,
+ "%s Sending DONE-TEST message to Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ if ((result =
+ (int) signal_Prime_Client("CLIENT_SIGNAL",""))
+ == (int) RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s Completed.", lad_timestamp());
+ (void) fflush(stderr);
+ } else {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s: error %d sending DONE-TEST message to Prime Client\n",
+ sfs_Myname, result);
+ errno = Saveerrno;
+ perror("signal_Prime_Client");
+ /* cleanup and exit */
+ (void) generic_kill(0, SIGINT);
+ exit(75);
+ }
+
+ /*
+ * wait for MOVE-DATA message from Prime Client before
+ * sending send results.
+ */
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s Waiting on MOVE-DATA message from Prime Client(%s).\n",
+ lad_timestamp(), Prime_client);
+ (void) fflush(stderr);
+ (void) pause();
+ (void) fprintf(stderr, "%s Received.", lad_timestamp());
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr, "%s Sending results to Prime Client(%s)\n",
+ lad_timestamp(), Prime_client);
+ (void) fflush(stderr);
+
+
+ if ((result = (int) signal_Prime_Client("CLIENT_DATA",
+ Client_results)) == (int) RPC_SUCCESS) {
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+ } else {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "%s: error %d sending client's result to Prime Client\n",
+ sfs_Myname, result);
+ errno = Saveerrno;
+ perror("signal_Prime_Client");
+ /* cleanup and exit */
+ (void) generic_kill(0, SIGINT);
+ exit(76);
+ }
+ } /* sent done, got move-data and sent data */
+
+ (void) fprintf(stdout,"\n");
+ (void) fprintf(stdout,
+ "************************************************************************");
+ (void) fprintf(stdout,"\n");
+
+} /* parent */
+
+
+/*
+ * ------------------------ Utility Routines --------------------------
+ */
+
+
+/*
+ * Monitor Logfile until its size reaches 'children' bytes.
+ * This means that all of the children are waiting for the next instruction.
+ */
+static void
+synchronize_children(
+ int children)
+{
+ struct stat statb; /* for fstat */
+
+ do {
+ (void) sleep(1);
+ if (fstat(Log_fd, &statb) == -1) {
+ (void) fprintf(stderr, "%s: can't stat log %s", sfs_Myname, Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(77);
+ }
+ } while (statb.st_size < children);
+
+ /*
+ * Truncate the log file
+ */
+ (void)close(Log_fd);
+ Log_fd = open(Logname, (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666);
+ if (Log_fd == -1) {
+ (void) fprintf(stderr, "%s: can't truncate log %s",
+ sfs_Myname, Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(78);
+ }
+
+} /* synchronize_children */
+
+
+/*
+ * Multi-client execution support routine.
+ * Call remote procedure on Prime client
+ * to send message to sfs_prime_clnt program.
+ * The message will contain a type field set to 'type',
+ * and a data field set to 'data'.
+ */
+static int
+signal_Prime_Client(
+ char * type,
+ char * data)
+{
+ static CLIENT * clnt_handle = NULL;
+ static int transaction_id = 0;
+ int * result;
+ static int socket;
+ sync_string sync_signal;
+ char transaction_string[MAX_STR1_LEN];
+ char buf[128];
+
+ if ((int)strlen(data) > MAX_STR2_LEN) {
+ (void) fprintf(stderr,
+ "%s: %s too much data len = %d max = %d\n",
+ sfs_Myname, Prime_client, strlen(data),
+ MAX_STR2_LEN);
+ return((int)RPC_CANTENCODEARGS);
+ }
+
+ if (clnt_handle == NULL) {
+ struct sockaddr_in prime_addr;
+ struct hostent * host_info;
+
+ socket = RPC_ANYSOCK;
+
+ /* get host information for prime_client */
+ if ((host_info = gethostbyname(Prime_client)) == NULL) {
+ (void) fprintf(stderr, "%s: %s is unknown host\n",
+ sfs_Myname, Prime_client);
+ return ((int) RPC_UNKNOWNHOST);
+ }
+
+ (void) memset((char *) &prime_addr, '\0', sizeof(prime_addr));
+ (void) memmove((char *) &prime_addr.sin_addr,
+ (char *) host_info->h_addr, host_info->h_length);
+
+ prime_addr.sin_family = AF_INET;
+ prime_addr.sin_port = 0;
+
+ /*
+ * Create client "handle" used for calling SFS_SYNCPROG on the
+ * Prime Client. We tell the RPC package to use the "tcp"
+ * protocol when contacting the prime_client.
+ */
+ clnt_handle = clnttcp_create(&prime_addr, SFS_SYNCPROG,
+ SFS_SYNCVERS, &socket, MAX_STR2_LEN, MAX_STR2_LEN);
+
+ if (clnt_handle == ((CLIENT *) NULL)) {
+ /*
+ * Couldn't establish connection with the Prime_Client.
+ * Print error message and return error.
+ */
+ clnt_pcreateerror(Prime_client);
+ (void) fprintf(stderr,
+ "%s: %s Could not establish client handle to contact %s\n",
+ lad_timestamp(), sfs_Myname, Prime_client);
+ return((int) RPC_FAILED);
+ }
+ }
+
+ /* fill up xdr structure with data to send to Prime Client */
+ (void) sprintf(transaction_string,"%d_%i", Client_num, ++transaction_id);
+ sync_signal.clnt_type = type;
+ sync_signal.clnt_id = Client_num;
+ sync_signal.clnt_data = data;
+ sync_signal.clnt_transaction = transaction_string;
+
+ /* Call the remote procedure "signal_sfs_1" on the Prime Client */
+ result = signal_sfs_1(&sync_signal, clnt_handle);
+ if (result == NULL) {
+ /*
+ * An error occurred while making RPC to the Prime Client.
+ * Print error message and return error.
+ */
+ sprintf(buf, "%s Transaction %s: Could not call prime client %s",
+ lad_timestamp(), transaction_string, Prime_client);
+ clnt_perror(clnt_handle, buf);
+ return((int) RPC_CANTSEND);
+ }
+
+ /* OK, we successfully called the remote procedure. */
+ if (*result == 0) {
+ /*
+ * remote procedure was unable to successfully perform required
+ * operation on the Prime Client.
+ * Print error message and return error.
+ */
+ (void) fprintf(stderr,
+ "%s: %s Prime Client couldn't write to PC_sync file \n",
+ sfs_Myname, Prime_client);
+ return((int) RPC_FAILED);
+ }
+
+ /* remote procedure success - wrote to Prime Client sync file */
+ return((int) RPC_SUCCESS);
+
+} /* signal_Prime_Client */
+
+
+/*
+ * Read results arrays for 'children' children from Logfile
+ * and accumulate them in "Ops".
+ * Complain about any problems we see with the log file.
+ */
+static void
+collect_counters(
+ int children)
+{
+ int i;
+ int j;
+ struct stat statb;
+ sfs_results_report_type report; /* final results log */
+ int Saveerrno;
+
+ if (fstat(Log_fd, &statb) == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't stat log %s ", sfs_Myname, Logname);
+ errno = Saveerrno;
+ perror(Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(79);
+ }
+
+ if (statb.st_size != (children * sizeof(report))) {
+ (void) fprintf(stderr, "%s: log file %s has bad format\n",
+ sfs_Myname, Logname);
+ (void) generic_kill(0, SIGINT);
+ exit(80);
+ }
+
+ if (lseek(Log_fd, 0L, 0) == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't lseek log %s ", sfs_Myname, Logname);
+ errno = Saveerrno;
+ perror("lseek");
+ (void) generic_kill(0, SIGINT);
+ exit(81);
+ }
+
+ for (j = 0; j < NOPS + 1; j++) {
+ Ops[j].results.good_calls = 0;
+ Ops[j].results.bad_calls = 0;
+ Ops[j].results.fast_calls = 0;
+ Ops[j].results.time.sec = 0;
+ Ops[j].results.time.usec = 0;
+ Ops[j].results.msec2 = 0;
+ }
+ Total_fss_bytes = 0;
+ Least_fss_bytes = 0;
+ Most_fss_bytes = 0;
+ Base_fss_bytes = 0;
+
+ for (i = 0; i < children; i++) {
+ if (read(Log_fd, (char *) &report, sizeof(report)) == -1) {
+ Saveerrno = errno;
+ (void) fprintf(stderr, "%s: can't read log %s", sfs_Myname, Logname);
+ errno = Saveerrno;
+ perror("Logname");
+ (void) generic_kill(0, SIGINT);
+ exit(82);
+ }
+
+ for (j = 0; j < NOPS + 1; j++) {
+ Ops[j].results.good_calls += report.results_buf[j].good_calls;
+ Ops[j].results.bad_calls += report.results_buf[j].bad_calls;
+ Ops[j].results.fast_calls += report.results_buf[j].fast_calls;
+ ADDTIME(Ops[j].results.time, report.results_buf[j].time);
+ Ops[j].results.msec2 += report.results_buf[j].msec2;
+ }
+ Total_fss_bytes += report.total_fss_bytes;
+ Least_fss_bytes += report.least_fss_bytes;
+ Most_fss_bytes += report.most_fss_bytes;
+ Base_fss_bytes += report.base_fss_bytes;
+ }
+
+} /* collect_counters */
+
+
+/*
+ * Check the parameters for validity.
+ */
+static int
+check_parameters(
+char * iodist_file,
+char * mix_file,
+int runtime_val)
+{
+ int retval = 0;
+ char detail[40];
+
+ detail[0] = '\0';
+
+ if (iodist_file != NULL) {
+ retval = INVALID_IODIST;
+ }
+ if (mix_file != NULL) {
+ retval = INVALID_MIX;
+ }
+ if (runtime_val != DEFAULT_RUNTIME) {
+ (void) sprintf(detail, "%d != %d", runtime_val, DEFAULT_RUNTIME);
+ retval = INVALID_RUNTIME;
+ }
+ if (Access_percent != DEFAULT_ACCESS) {
+ (void) sprintf(detail, "%d != %d", Access_percent, DEFAULT_ACCESS);
+ retval = INVALID_ACCESS;
+ }
+ if (Append_percent != DEFAULT_APPEND) {
+ (void) sprintf(detail, "%d != %d", Append_percent, DEFAULT_APPEND);
+ retval = INVALID_APPEND;
+ }
+ if (Kb_per_block != DEFAULT_KB_PER_BLOCK) {
+ (void) sprintf(detail, "%d != %d", Kb_per_block, DEFAULT_KB_PER_BLOCK);
+ retval = INVALID_KB;
+ }
+ if (Files_per_dir != DEFAULT_FILES_PER_DIR) {
+ (void) sprintf(detail, "%d != %d", Files_per_dir, DEFAULT_FILES_PER_DIR);
+ retval = INVALID_NDIRS;
+ }
+ if (Fss_delta_percent != DEFAULT_DELTA_FSS) {
+ (void) sprintf(detail, "%d != %d",
+ Fss_delta_percent, DEFAULT_DELTA_FSS);
+ retval = INVALID_FSS;
+ }
+ if (Biod_max_outstanding_reads < DEFAULT_BIOD_MAX_READ) {
+ (void) sprintf(detail, "%d < %d",
+ Biod_max_outstanding_reads, DEFAULT_BIOD_MAX_READ);
+ retval = INVALID_BIODREAD;
+ }
+ if (Tot_client_num_symlinks != DEFAULT_NSYMLINKS) {
+ (void) sprintf(detail, "%d != %d",
+ Tot_client_num_symlinks, DEFAULT_NSYMLINKS);
+ retval = INVALID_NSYMLINKS;
+ }
+ if (Biod_max_outstanding_writes < DEFAULT_BIOD_MAX_WRITE) {
+ (void) sprintf(detail, "%d < %d",
+ Biod_max_outstanding_writes, DEFAULT_BIOD_MAX_WRITE);
+ retval = INVALID_BIODWRITE;
+ }
+ if (Warmuptime != DEFAULT_WARMUP) {
+ (void) sprintf(detail, "%d != %d",
+ Warmuptime, DEFAULT_WARMUP);
+ retval = INVALID_WARMUP;
+ }
+
+ if (retval != 0)
+ (void) fprintf(stdout,
+ "%s: INVALID RUN, ILLEGAL PARAMETER: Non-standard %s %s\n",
+ sfs_Myname, invalid_str[retval], detail);
+ return (retval);
+}
+
+/*
+ * Check the results in Ops[] for validity.
+ */
+static int
+check_counters(void)
+{
+ double mix_pcnt;
+ int bad_pcnt;
+ int i;
+ int ret = 0;
+
+ if (Ops[TOTAL].results.good_calls <= 0) {
+ (void) fprintf(stdout, "%s: INVALID RUN %s\n",
+ sfs_Myname, invalid_str[INVALID_GOODCALLS]);
+ ret = INVALID_GOODCALLS;
+ }
+ if (Ops[TOTAL].results.good_calls != 0)
+ bad_pcnt = (Ops[TOTAL].results.bad_calls * 100)
+ / Ops[TOTAL].results.good_calls;
+ else
+ bad_pcnt = 100;
+
+ if (bad_pcnt >= 1) {
+ (void) fprintf(stdout, "%s: INVALID RUN, %d%% %s\n",
+
+ sfs_Myname, bad_pcnt,
+ invalid_str[INVALID_FAILEDRPC]);
+ ret = INVALID_FAILEDRPC;
+ }
+
+ if (Ops[TOTAL].results.good_calls == 0) {
+ (void) fprintf(stdout, "%s: INVALID RUN, no good calls\n", sfs_Myname);
+ return (INVALID_NOTMIX);
+ }
+
+ for (i = 0; i < NOPS; i++) {
+ mix_pcnt = ((double)Ops[i].results.good_calls /
+ Ops[TOTAL].results.good_calls) * 100.0;
+ if (mix_pcnt != (double)Ops[i].mix_pcnt) {
+ if ((mix_pcnt - (double)Ops[i].mix_pcnt > 1.5) ||
+ ((double)Ops[i].mix_pcnt - mix_pcnt > 1.5)) {
+ (void) fprintf(stdout, "%s: INVALID RUN, %s target %d%% actual %4.1f%% %s\n",
+
+ sfs_Myname, Ops[i].name, Ops[i].mix_pcnt, mix_pcnt,
+ invalid_str[INVALID_NOTMIX]);
+ ret = INVALID_NOTMIX;
+ }
+ }
+ }
+
+ return (ret);
+
+} /* check_counters */
+
+
+/*
+ * Print the test run results, for 'load' load, the operation percentages
+ * in 'mixfile' percentages, and 'children' processes.
+ */
+static void
+print_results(
+ int children,
+ int load,
+ char * mix_file,
+ int invalid_flag,
+ int runtime_val,
+ char * iodist_file)
+{
+ uint_t runtime;
+ uint_t total_msec;
+ uint_t msec;
+ uint_t total_calls;
+ uint_t calls;
+ int i;
+ double squared_time_msec;
+ double sum2_msec;
+ double var_msec;
+ double stdev_msec;
+ double sq_conf_interval_msec;
+ double conf_interval_msec;
+ sfs_op_type * op_ptr;
+ sfs_results_type * results_ptr;
+ char result_string[MAX_LINE_LEN];
+
+
+ /* compute total time for all ops combined */
+ total_msec = 0;
+ for (i = 0; i < NOPS; i++) {
+ total_msec += Ops[i].results.time.sec * 1000;
+ total_msec += Ops[i].results.time.usec / 1000;
+ }
+
+ /*
+ * Report statistics based on successful calls only. The per
+ * operation routines accumulate time and count only good_calls.
+ */
+ total_calls = Ops[TOTAL].results.good_calls;
+
+
+ /*
+ * Print the client's test parameters
+ */
+ (void) fprintf(stderr, "\n\nClient Test Parameters: \n");
+ (void) fprintf(stderr, "\tNumber of processes = %d\n", children);
+ (void) fprintf(stderr, "\tRequested Load (NFS V%d operations/second) = %d\n",
+ nfs_version, load);
+ (void) fprintf(stderr, "\tMaximum number of outstanding biod writes = %d\n",
+ Biod_max_outstanding_writes);
+ (void) fprintf(stderr, "\tMaximum number of outstanding biod reads = %d\n",
+ Biod_max_outstanding_reads);
+ (void) fprintf(stderr, "\tWarm-up time (seconds) = %d\n\tRun time (seconds) = %d\n",
+ Warmuptime, runtime_val);
+ if (mix_file)
+ (void) fprintf(stderr,"\tNFS Mixfile = %s\n", mix_file);
+ if (iodist_file)
+ (void) fprintf(stderr,"\tBlock Size Distribution file = %s\n",
+ iodist_file);
+ (void) fprintf(stderr, "\tFile Set = %4d Files created for I/O operations\n",
+ (Tot_client_num_io_files/children + 1) * children);
+ (void) fprintf(stderr, "\t\t %4d Files accessed for I/O operations\n",
+ (((Tot_client_num_io_files/children + 1) * Access_percent)
+ / 100) * children);
+ (void) fprintf(stderr, "\t\t %4d Files for non-I/O operations\n",
+ (Tot_client_num_non_io_files/children + 1) * children);
+ (void) fprintf(stderr, "\t\t %4d Symlinks\n",
+ (Tot_client_num_symlinks/children + 1) * children);
+ (void) fprintf(stderr, "\t\t %4d Directories\n",
+ ((Tot_client_num_io_files/children + 1) / Files_per_dir ) * children);
+ (void) fprintf(stderr, "\t\t\tAdditional non-I/O files created as necessary\n\n");
+
+ (void) sprintf(Client_results,"%d %d %d %d %d %d\n",
+ nfs_version,
+ (Tot_client_num_io_files/children + 1) * children,
+ (((Tot_client_num_io_files/children + 1) * Access_percent)
+ / 100) *children,
+ (Tot_client_num_non_io_files/children + 1) * children,
+ (Tot_client_num_symlinks/children + 1) * children,
+ ((Tot_client_num_io_files/children + 1) / Files_per_dir ) * children);
+
+ /* print the client's results header information */
+ (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n",
+ SFS_VERSION_NUM, SFS_VERSION_DATE);
+ (void) fprintf(stdout, "SFS Single Client (%s) Results, %s\n",
+ lad_hostname, lad_timestamp());
+ (void) fflush(stdout);
+
+ /* print column headers for per operation statistics */
+ (void) fprintf(stdout,
+"----------------------------------------------------------------------------\n");
+ (void) fprintf(stdout, "\n");
+ (void) fprintf(stdout,
+"NFS V%d Target Actual NFS NFS Mean Std Dev Std Error Pcnt \n",
+nfs_version);
+ (void) fprintf(stdout,
+"Op NFS NFS Op Op Response Response of Mean,95%% of \n");
+ (void) fprintf(stdout,
+"Type Mix Mix Success Error Time Time Confidence Total\n");
+ (void) fprintf(stdout,
+" Pcnt Pcnt Count Count Msec/Op Msec/Op +- Msec/Op Time \n");
+ (void) fprintf(stdout,
+"----------------------------------------------------------------------------\n");
+ (void) fflush(stdout);
+
+ /* print per operation statistics */
+ for (i = 0; i < NOPS; i++) {
+ /* init to 0 */
+ squared_time_msec = 0.0;
+ sum2_msec = 0.0;
+ calls = 0;
+ msec = 0;
+ stdev_msec = 0;
+
+ op_ptr = &Ops[i];
+ results_ptr = &op_ptr->results;
+
+ /* get the number successful calls and total time */
+ calls = op_ptr->results.good_calls;
+ msec = (results_ptr->time.sec * 1000)
+ + (results_ptr->time.usec / 1000);
+
+ /* compute the standard deviation for the mean response time */
+ if (calls <= 1)
+ stdev_msec = 0;
+ else {
+ /* get the standard deviation */
+ squared_time_msec = results_ptr->msec2;
+ /* compute the square of the total elapsed time */
+ sum2_msec = (results_ptr->time.sec * 1000.0)
+ + (results_ptr->time.usec / 1000.0);
+ sum2_msec *= sum2_msec;
+
+ /* variance = 1/(n-1) * (sum(x^2) - 1/n * (sum(x))^2) */
+ var_msec = (squared_time_msec - (sum2_msec / calls)) / (calls-1);
+ if (var_msec == 0.0) {
+ stdev_msec = 0.0;
+ } else
+ stdev_msec = sqrt(var_msec);
+ }
+
+ /* compute the confidence interval */
+ if (calls != 0) {
+ sq_conf_interval_msec = DEFAULT_CHI_SQR_CI * (stdev_msec / calls);
+ if (sq_conf_interval_msec == 0.0) {
+ conf_interval_msec = 0.0;
+ } else
+ conf_interval_msec = sqrt(sq_conf_interval_msec);
+ } else
+ conf_interval_msec = 0.0;
+
+ /* print the per op statistics */
+ (void) fprintf(stdout,
+ "%-12s%3d%% %4.1f%% %5d %5d %5.2f %8.2f %8.2f %3.1f%%\n",
+ op_ptr->name, /* op name */
+ op_ptr->mix_pcnt, /* target mix */
+ /* actual mix */
+ total_calls ? ((double)calls / total_calls) * 100.0 : 0.0,
+ results_ptr->good_calls, /* successes */
+ results_ptr->bad_calls, /* errors */
+ calls ? ((double)msec / calls) : 0.0, /* msec/call */
+ stdev_msec, /* std dev */
+ conf_interval_msec, /* conf int */
+ /* % of time */
+ total_msec ? ((double)msec / total_msec) * 100 : 0.0);
+ (void) fflush(stdout);
+
+ /*
+ * Store client data in result_string.
+ * This string is different from client result display.
+ * The squared_time_msec and sum2_msec values are passed along
+ * to be used by the prime client to calculate the stddev value for
+ * each operation.
+ */
+ if (Prime_client) {
+ (void) sprintf(result_string,
+ "%-12s %3d%% %3.1f%% %5d %5d %4ld.%1ld %6.2f %3.1f%% %f %f\n",
+ op_ptr->name, /* op name */
+ op_ptr->mix_pcnt, /* target mix */
+ /* actual mix */
+ total_calls ? ((double)calls / total_calls) * 100.0 : 0.0,
+ results_ptr->good_calls, /* successes */
+ results_ptr->bad_calls, /* errors */
+ results_ptr->time.sec, /* total time1*/
+ results_ptr->time.usec / 100000, /* total time2*/
+ calls ? ((double)msec / calls) : 0.0, /* msec/call */
+ /* % of time */
+ total_msec ? ((double)msec / total_msec) * 100 : 0.0,
+ squared_time_msec, /* sum of sqs */
+ sum2_msec); /* sq of sums */
+ (void) strcat(Client_results, result_string);
+ }
+
+ } /* end for each op */
+
+ (void) fprintf(stdout,
+"----------------------------------------------------------------------------\n\n");
+ (void) fflush(stdout);
+
+ /* Average child runtime. (should this be the longest runtime?) */
+ runtime = Ops[TOTAL].results.time.sec / children;
+
+ /* Print summary */
+ (void) fprintf(stdout,
+ " ------------------------------------------------------------\n");
+ (void) fprintf(stdout,
+ " | SPEC SFS VERSION %6s SINGLE CLIENT RESULTS SUMMARY |\n",
+ SFS_VERSION_NUM);
+ (void) fprintf(stdout,
+ " ------------------------------------------------------------\n");
+ (void) fprintf(stdout, "NFS V%d THROUGHPUT: ", nfs_version);
+ (void) fprintf(stdout,
+ "%4d.%02d Ops/Sec AVG. RESPONSE TIME: %4d.%02d Msec/Op\n",
+ runtime ? (total_calls / runtime) : 0,
+ runtime ? ((total_calls % runtime) * 100 / runtime) : 0,
+ total_calls ? (total_msec / total_calls) : 0,
+ total_calls ? ((total_msec % total_calls) * 100 / total_calls) : 0);
+ (void) fprintf(stdout, "%s PROTOCOL\n", Tcp ? "TCP" : "UDP");
+ (void) fprintf(stdout, "FAST CALLS: %d\n", Ops[TOTAL].results.fast_calls);
+ (void) fprintf(stdout, "NFS MIXFILE: ");
+ if (mix_file)
+ (void) fprintf(stdout,"%s\n", mix_file);
+ else
+ (void) fprintf(stdout,"[ SFS Default ]\n");
+ (void) fprintf(stdout, "CLIENT REQUESTED LOAD: %d Ops/Sec \n", load);
+ (void) fprintf(stdout,
+ "TOTAL NFS OPERATIONS: %-6d TEST TIME: %d Sec \n",
+ total_calls, runtime);
+ (void) fprintf(stdout, "FILE SET SIZE CREATED: %d KB\n",
+ Total_fss_bytes);
+ (void) fprintf(stdout,
+ "FILE SET SIZE ACCESSED: %d - %d KB (%d%% to %d%% of Base)\n",
+ Least_fss_bytes, Most_fss_bytes,
+ (100 * Least_fss_bytes) / Base_fss_bytes,
+ (100 * Most_fss_bytes) / Base_fss_bytes);
+ (void) fprintf(stdout, "\n");
+ (void) fprintf(stdout,
+ "------------------------------------------------------------------------");
+ (void) fprintf(stdout, "\n\n");
+ (void) fflush(stdout);
+
+ /*
+ * store client summary results and Invalid run indicator
+ * to send to the Prime_client
+ */
+ if (Prime_client) {
+ (void) sprintf(result_string,"%d.%02d %d.%02d %d %d %d %d %d %d %d\n",
+ runtime ? (total_calls / runtime) : 0, /* ops/sec1 */
+ runtime ? ((total_calls % runtime) * 100 / runtime) : 0, /* ops/sec2 */
+ total_calls ? (total_msec / total_calls) : 0, /* mean1 */
+ total_calls ? ((total_msec % total_calls) * 100 / total_calls) : 0, /* mean2 */
+ runtime, /* run time */
+ total_calls, /* # ops */
+ invalid_flag, /* valid flag */
+ Total_fss_bytes, /* total fileset */
+ Least_fss_bytes, /* fileset low */
+ Most_fss_bytes, /* fileset high */
+ Base_fss_bytes); /* fileset base */
+ (void) strcat(Client_results, result_string);
+ }
+
+} /* print_results */
+
+
+/* ARGSUSED */
+static void
+sfs_reaper(
+ int sig_id)
+{
+ (void) fprintf(stderr, "%s: caught unexpected SIGCHLD. Exiting...\n",
+ sfs_Myname);
+ /* cleanup and exit */
+ (void) signal_Prime_Client("CLIENT_STOP", "");
+ (void) generic_kill(0, SIGINT);
+ exit(83);
+}
+/* sfs_c_pnt.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_rndSid[] = "@(#)sfs_c_rnd.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_rnd.c ---------------------
+ *
+ * Random number generator.
+ *
+ *.Exported_routines
+ * int32_t sfs_random(void)
+ * void sfs_srandom(int)
+ *
+ *.Local_routines
+ * double ran(void)
+ * int32_t spec_rand(void)
+ * void spec_srand(int)
+ *
+ *.Revision_History
+ * 28-Nov-91 Teelucksingh ANSI C
+ * 01-Aug-91 Wiryaman sfs_srandom() and sfs_random()
+ * now use spec_srand() and spec_rand()
+ * instead of srandom() and random().
+ * 17-Apr-91 Wittle Created.
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+/*
+ * Here's the source for the random number generator that SPEC uses.
+ * The function to be called is "spec_rand" which returns an integer
+ * between 1 and MAX_INT-1.
+ *
+ * One question we may wanna think about is the seeding of the random
+ * number generator. Do we start with the same seed everytime (for
+ * repeatability) or use a array of possible seeds (some seeds are better
+ * than others and SPEC prople mention that they have a list of 15
+ * "good" seeds).
+ */
+
+
+/*
+ * ------------------------- Static Declarations -------------------------
+ */
+
+static int32_t seedi = 2231;
+
+
+/*
+ * ------------------------- External Definitions -------------------------
+ */
+
+static double ran(void);
+static int32_t spec_rand(void);
+static void spec_srand(int);
+
+/*
+ * ----------------------- Random Number Routines -----------------------
+ */
+
+
+/*
+ * Seed the random number generator.
+ */
+static void
+spec_srand(
+ int seed)
+{
+ seedi = seed;
+}
+
+
+/*
+ * Returns a random number.
+ */
+static int32_t
+spec_rand(void)
+{
+ (void) ran();
+ return(seedi);
+}
+
+
+/*
+ * Compute the next random number.
+ */
+static double
+ran(void)
+
+/* See "Random Number Generators: Good Ones Are Hard To Find", */
+/* Park & Miller, CACM 31#10 October 1988 pages 1192-1201. */
+/***********************************************************/
+/* THIS IMPLEMENTATION REQUIRES AT LEAST 32 BIT INTEGERS ! */
+/***********************************************************/
+
+#define _A_MULTIPLIER 16807L
+#define _M_MODULUS 2147483647L /* (2**31)-1 */
+#define _Q_QUOTIENT 127773L /* 2147483647 / 16807 */
+#define _R_REMAINDER 2836L /* 2147483647 % 16807 */
+{
+ int32_t lo;
+ int32_t hi;
+ int32_t test;
+
+ hi = seedi / _Q_QUOTIENT;
+ lo = seedi % _Q_QUOTIENT;
+ test = _A_MULTIPLIER * lo - _R_REMAINDER * hi;
+ if (test > 0) {
+ seedi = test;
+ } else {
+ seedi = test + _M_MODULUS;
+ }
+ return((float) seedi / _M_MODULUS);
+}
+
+/*
+ * Local interface to seed random number generator.
+ */
+void
+sfs_srandom(
+ int seed)
+{
+ spec_srand(seed);
+}
+
+
+/*
+ * Local interface to obtain a random number.
+ */
+int32_t
+sfs_random(void)
+{
+ return(spec_rand());
+}
+
+static struct r_array {
+ int n1;
+ int n2;
+} *r_array;
+
+static int r_length = 0;
+
+static int
+r_array_compare(const void *i, const void *j)
+{
+ if (((struct r_array *)i)->n2 > ((struct r_array *)j)->n2)
+ return (1);
+ if (((struct r_array *)i)->n2 < ((struct r_array *)j)->n2)
+ return (-1);
+ return (0);
+}
+
+int
+init_rand_range(int length)
+{
+ int i;
+
+ /*
+ * If array already exists free it
+ */
+ if (r_length != 0) {
+ (void)free(r_array);
+ r_length = 0;
+ }
+
+ /*
+ * If length is zero just free memory and return
+ */
+ if (length == 0)
+ return (0);
+
+ /*
+ * Allocate array of sequential numbers and random numbers
+ */
+ if ((r_array = malloc(length * sizeof(struct r_array))) == NULL)
+ return (1);
+
+ r_length = length;
+
+ /*
+ * Initialize array of sequential values and random values
+ */
+ for (i = 0; i < length; i++) {
+ r_array[i].n1 = i;
+ r_array[i].n2 = sfs_random();
+ }
+
+ /*
+ * Sort random array values to put sequential values in random order
+ */
+ qsort(r_array, length, sizeof(struct r_array), r_array_compare);
+
+ return (0);
+}
+
+int
+rand_range(int index)
+{
+ return (r_array[index].n1);
+}
+/* sfs_c_rnd.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_sigSid[] = "@(#)sfs_c_sig.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_sig.c ---------------------
+ *
+ * Signal handling. Routines to send and catch signals.
+ *
+ *.Exported_Routines
+ * void sfs_alarm(int)
+ * void sfs_startup(int)
+ * void sfs_stop(int)
+ * void sfs_cleanup(int)
+ *
+ *.Local_Routines
+ * None.
+ *
+ *.Revision_History
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <sys/signal.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+#ifndef RFS
+/*
+ * ------------------------- Signal Handlers -------------------------
+ */
+
+void
+sfs_alarm(
+ int sig_id)
+{
+#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ (void) signal(sig_id, sfs_alarm);
+#endif
+ if (DEBUG_CHILD_SIGNAL)
+ (void) fprintf(stderr, "%s: caught Signal %d\n", sfs_Myname, sig_id);
+ (void) fflush(stderr);
+
+} /* sfs_alarm */
+
+
+/*
+ * Signal Handler
+ * Catch the parent's "start" signal.
+ */
+void
+sfs_startup(
+ int sig_id)
+{
+#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ (void) signal(SIGUSR1, sfs_startup);
+#endif
+ if (DEBUG_CHILD_SIGNAL)
+ (void) fprintf(stderr, "%s: caught Signal %d SIGUSR1\n",
+ sfs_Myname, sig_id);
+
+ if (Child_num == -1)
+ /* I'm the parent, ignore the signal */
+ return;
+
+ /*
+ * SIGUSR1 is used to make all phase transitions, but we
+ * only want to make the Warmup -> Testrun switch here
+ */
+ if (Current_test_phase != Warmup_phase)
+ return;
+
+ Current_test_phase = Testrun_phase;
+ start_run_phase++;
+} /* sfs_startup */
+
+
+/*
+ * Signal Handler
+ * Catch the parent's "stop" signal.
+ */
+void
+sfs_stop(
+ int sig_id)
+{
+#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ (void) signal(SIGUSR2, sfs_stop);
+#endif
+ if (DEBUG_CHILD_SIGNAL)
+ (void) fprintf(stderr, "%s: caught Signal %d SIGUSR2\n",
+ sfs_Myname, sig_id);
+
+ /* end the test */
+ Runtime = 0;
+ Current_test_phase = Results_phase;
+
+} /* sfs_stop */
+
+#endif
+
+
+/*
+ * SIGINT, SIGTERM handler
+ * Clean up and exit due to an error/abort condition.
+ * We assume if NFS_client was valid, then MOUNT_client was also valid.
+ */
+void
+sfs_cleanup(
+ int sig_id)
+{
+ if (DEBUG_CHILD_SIGNAL)
+ (void) fprintf(stderr, "%s: caught Signal %d SIGINT\n",
+ sfs_Myname, sig_id);
+
+ (void) unlink(Logname);
+ if (NFS_client != ((CLIENT *) NULL))
+ clnt_destroy(NFS_client);
+ exit(65);
+
+} /* sfs_cleanup */
+
+/* sfs_c_sig.c */
--- /dev/null
+#ifndef lint
+static char sfs_c_subSid[] = "@(#)sfs_c_sub.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * ---------------------- sfs_c_sub.c ---------------------
+ *
+ * Subroutines common to both sfs and sfs_prime_client.
+ *
+ *.Exported_Routines
+ * int generic_kill(int, int)
+ * void generic_catcher(int)
+ * char * lad_timestamp(void)
+ *
+ *.Local_Routines
+ * None.
+ *
+ *.Revision_History
+ * 16-Dec-91 Wittle Created.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <sys/signal.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+
+/*
+ * Common data shared between sfs and sfs_prime
+ *
+ * Values for invalid runs
+ */
+char *invalid_str[INVALID_MAX] = {
+ "No error",
+ "Unknown",
+ "IO distribution file",
+ "Mix file",
+ "Runtime",
+ "Access percentage",
+ "Append percentage",
+ "KB per block",
+ "Number client directories",
+ "Fileset delta",
+ "Max biod reads",
+ "Number symlinks",
+ "Max biod writes",
+ "Warmup time",
+ "No good calls",
+ "Failed RPC calls",
+ "Op mix missed",
+};
+
+/*
+ * ------------------------- Signal Handlers -------------------------
+ */
+
+/*
+ * Signal Sender. Send signal 'sig' to process 'pid'.
+ */
+int
+generic_kill(
+ int pid,
+ int signal)
+{
+ if (DEBUG_PARENT_SIGNAL)
+ (void) fprintf(stderr,
+ "%s: sending Pid %d Signal %d\n", sfs_Myname, pid ,signal);
+ return(kill((pid_t)pid, signal));
+
+} /* generic_kill */
+
+
+/*
+ * Signal Handler. Catch and reset the handler for signal 'i'.
+ */
+void
+generic_catcher(
+ int i)
+{
+#if !(defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ (void) signal(i, generic_catcher);
+#endif
+ if (DEBUG_CHILD_SIGNAL)
+ (void) fprintf(stderr, "%s: caught Signal %d\n", sfs_Myname, i);
+ (void) fflush(stderr);
+
+} /* generic_catcher */
+
+
+
+/*
+ * get the date and time and return a string, remove the END-OF-LINE (\n)
+ */
+char *
+lad_timestamp(void)
+{
+ static time_t run_date;
+ static char date_string[26];
+
+ run_date = time((time_t *)NULL);
+ (void) strncpy((char *) date_string, (char *) ctime(&run_date), 24);
+ return(date_string);
+
+} /* lad_timestamp */
+
+int
+set_debug_level(char *s)
+{
+ unsigned int i;
+ unsigned int first;
+ unsigned int last;
+ unsigned int level = 0;
+
+ if (s == NULL || *s == '\0')
+ return(0);
+
+ for (;;) {
+ if (*s == ',') {
+ s++;
+ continue;
+ }
+
+ /* find first flag to set */
+ i = 0;
+ while (isdigit(*s))
+ i = i * 10 + (*s++ - '0');
+ first = i;
+
+ /* find last flag to set */
+ if (*s == '-') {
+ i = 0;
+ while (isdigit(*++s))
+ i = i * 10 + (*s - '0');
+ }
+ last = i;
+
+ if (first != 0 && last != 0 && first < 32 && last < 32) {
+ for (i = first - 1; i < last; i++) {
+ level |= (1 << i);
+ }
+ }
+
+ /* more arguments? */
+ if (*s++ == '\0')
+ return (level);
+ }
+}
+
+/* sfs_c_sub.c */
--- /dev/null
+#!/bin/sh
+# @(#)sfs_ext_mon 2.1 97/10/23
+#
+# This sample shell script can be used to start and stop
+# external processes at the beginning and ending of the
+# sfs load generation period respectively. The name of
+# this program is pased in via the PRIME_MON_SCRIPT variable
+# in the sfs_rc or equivalent file. The sfs_prime program
+# executes this shell script just before it issues the START
+# message to all the clients ie. "script_name START"; and likewise
+# another call after all the clients have completed load generation ie.
+# "script_name DONE".
+#
+# All environment variables exported from sfs_mgr are available for use.
+#
+EXT_MON_ARG=""
+if [ $# -gt 1 ]
+then
+ EXT_MON_ARG="$2"
+fi
+
+#
+#----------------- START section -----------------
+# Code section that handles starting of external processes.
+# ALL PROCESSES SHOULD BE STARTED IN THE BACKGROUND OR ELSE
+# THE TEST WILL BLOCK WAITING FOR THEIR COMPLETION!!
+#
+if [ "$1" = "START" ]; then
+ #
+ # place commands to start performance monitoring utilities as
+ # background processes here.
+ #
+ echo "$0: started external monitoring utilities" >&2
+ exit 0
+fi
+
+#
+#----------------- DONE section -----------------
+# Code section that handles stopping of external processes.
+# ALL PROCESSES SHOULD BE STARTED IN THE BACKGROUND OR ELSE
+# THE TEST WILL BLOCK WAITING FOR THEIR COMPLETION!!
+#
+if [ "$1" = "DONE" ]; then
+ #
+ # place commands to stop performance monitoring utilities as
+ # background processes here.
+ #
+ echo "$0: stopped external monitoring utilities" >&2
+ exit 0
+fi
+
+#
+#----------------- ERROR section -----------------
+# ERROR: BAD PARAMETER
+#
+echo "$0: bad param. " >&2
+echo "usage: $0 START | DONE " >&2
+exit 1
--- /dev/null
+/*
+ * @(#)sfs_m_def.h 2.1 97/10/23
+ */
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+#define MAX_LINE_LEN 160 /* max per line results string */
+#define MAX_STR1_LEN 31 /* max msg, number, and xid str len */
+#define MAX_STR2_LEN 2560 /* total results data string length */
+#define MULTICLIENT_OFFSET 500 /* Runtime offset */
+
+/* multi-client sync logfiles and prefixes */
+#define SFS_CLIENT_SYNC_LOG "/tmp/sfs_CL" /* client logfile prefix */
+#define SFS_PRIME_SYNC_LOG "/tmp/sfs_PC_sync" /* prime client logfile */
+#define PRIME_RESULTS_LOG "/tmp/sfs_res" /* prime results logfile prefix */
+
+struct sync_string {
+ int clnt_id; /* client number */
+ char *clnt_type; /* message type, hard coded */
+ char *clnt_transaction; /* transaction id */
+ char *clnt_data; /* results strings */
+};
+typedef struct sync_string sync_string;
+
+#define SFS_SYNCPROG ((uint32_t) 100500)
+#define SFS_SYNCVERS ((uint32_t) 1)
+#define SIGNAL_NULLPROC ((uint32_t) 0)
+#define SIGNAL_SFS ((uint32_t) 1)
+
+extern bool_t xdr_sync_string(XDR *, sync_string *);
+extern int * signal_sfs_1(sync_string *, CLIENT *);
--- /dev/null
+#ifndef lint
+static char sfs_m_msgSid[] = "@(#)sfs_m_msg.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ *.Exported_routines
+ * int * signal_sfs_1(sync_string *, CLIENT *);
+ *
+ *.Local_routines
+ * None.
+ *
+ *.Revision_history
+ * 04-Dec-91 Keith Include sfs_def.h for SYSV/SVR4
+ * mem* routines.
+ * 17-Jun-91 Teelucksingh Create multi-client synchronization
+ * rpc definition
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+/*
+ * ----------------------- Static Definitions -----------------------
+ */
+
+/*
+ * Almost all rpcs are sent over TCP so we have a reliable transport,
+ * unfortunately the initialization phase may highly overload the
+ * server and the previous default of 5 seconds might timeout before
+ * it gets acknowledged. The risk of a large timeout is that there is
+ * an added period after a stop message is sent. This is usually not
+ * a problem.
+ */
+static struct timeval TIMEOUT = { 60, 0 };
+
+
+/*
+ * ------------------- Multi-client Message Handling -------------------
+ */
+
+int *
+signal_sfs_1(
+ sync_string * argp,
+ CLIENT * clnt)
+{
+ static int res;
+
+ (void) memset((char *) &res, '\0', sizeof(res));
+ if (clnt_call(clnt, SIGNAL_SFS, xdr_sync_string, (caddr_t)argp, xdr_int,
+ (caddr_t)&res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+} /* signal_sfs_1 */
+
+
+/* sfs_m_msg.c */
--- /dev/null
+#ifndef lint
+static char sccsid[] = "@(#)sfs_m_prm.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ * This program is started from sfs_mgr. It runs on some system
+ * designated as the Prime Client and synchronizes the the different
+ * phases of a SFS benchmark execution amongst multiple clients by
+ * means of RPCs. The Prime client can also be running SFS.
+ *
+ *.Exported_routines
+ * int main(int, char **)
+ *
+ *.Local_routines
+ * void multi_cleanup(int)
+ * void do_initialize(int, char **)
+ * void sync_PC_with_clients(void)
+ * int signal_sfs_clients(char *, int)
+ * void print_multi_results(void)
+ * void prog_usage(void)
+ * void printdeadclients(void)
+ *
+ *.Revision_history
+ * 11-Jul-94 ChakChung Ng Add codes for NFS/v3
+ * 02-Jul-92 0.1.9 Teelucksingh
+ * Use tcp handles for synchronization
+ * instead of udp ones. Added code to make
+ * call to shell script to start and stop
+ * external monitoring (no longer creates
+ * /tmp/SFS_START and /tmp/SFS_DONE).
+ * 10-Jan-92 0.00.19 Teelucksingh
+ * Added code for the Prime-Client to report
+ * 'INVALID RUN' from any of the clients.
+ * Also added code to compute and report
+ * the aggregate 'average response time
+ * standard deviation values'.
+ * 04-Jan-92 0.00.18 Pawlowski
+ * Add hooks for raw data dump support.
+ * 04-Dec-91 0.00.15 Keith
+ * Include string.h for SYSV/SVR4
+ * 28-Nov-91 0.00.13 Teelucksingh
+ * Fixed 'multiple signals' problem.
+ * Sync rpcs now pass a 'transaction id'
+ * to 'sfs_syncd', the sync server.
+ * 'sfs_syncd' keeps track of previous rpc
+ * calls that successfully executed.
+ * Added ANSI C features.
+ *
+ * 23-Sep-91 0.00.11 Teelucksingh
+ * Modified the format of sfs_prime output
+ * 17-Jun-91 Teelucksingh Created.
+ *
+ *
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <sys/file.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+#if !defined(_XOPEN_SOURCE)
+#include <sys/socket.h>
+#else
+#define AF_INET 2
+#endif
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+
+
+/* sfs_mpr.c function declarations */
+/* external sfs routines */
+
+/* forward definitions for local routines */
+static void multi_cleanup(int);
+static void do_initialize(int, char **);
+static void sync_PC_with_clients(void);
+static int signal_sfs_clients(char *);
+static void print_multi_results(void);
+static void prog_usage(void);
+static void printdeadclients(void);
+
+/*
+ * ----------------------- Static Definitions -----------------------
+ */
+
+#define EXTRA_INIT_TIME 3600 /* extra time to initialize before timing out */
+
+int Debug_level = 0; /* flag indicates prime client debug mode */
+char *sfs_Myname; /* program name */
+int nfs_version;
+
+static int Asleep = 0; /* flag: parent sfs process is asleep */
+static int Pc_log_fd; /* Prime client sync log fd */
+static int Num_clients = 0; /* number of clients used in run */
+static char **Client_names; /* [HOSTNAME_LEN]; array of clients host names */
+ /* sleep period before issuing a go ahead signal */
+static int Prime_sleep_time = 0;
+ /* time to wait before exiting with error */
+static int Prime_time_out = 400;
+static int Prime_runtime = DEFAULT_RUNTIME; /* seconds in benchmark run */
+
+/*
+ * the Prime_client only uses the P_* variables
+ * for calculating and reporting the Aggregate
+ * run parameters.
+ */
+static int P_children = DEFAULT_NPROCS; /* processes per client */
+static int P_total_load = DEFAULT_LOAD; /* NFS operations per second */
+static int P_percent_append = DEFAULT_APPEND; /* % of writes that append */
+static int P_percent_access = DEFAULT_ACCESS; /* % of file set accessed */
+static int P_kb_per_block = DEFAULT_KB_PER_BLOCK; /* i/o pkt block sz in KB */
+static int P_dump_data = 0; /* raw output switch */
+static int P_testop = -1; /* test mode operation number */
+static int P_percent_fss_delta = /* allowed change to file set */
+ DEFAULT_DELTA_FSS;
+ /* allowed change to file set */
+static int P_warmuptime = DEFAULT_WARMUP; /* seconds to warmup */
+static char *P_iodist_file = 0; /* block io dist table file */
+static char *P_mix_file = 0; /* mix file */
+static char *P_script_name = 0; /* external script name */
+static char *P_script_args = ""; /* external script parameters */
+static int P_tcp = 0; /* TCP */
+static FILE *sum_result_fp = NULL;
+
+/*
+ * --------------------- Biod Simulation Variables ---------------------
+ */
+static int P_biod_max_outstanding_reads = DEFAULT_BIOD_MAX_READ;
+static int P_biod_max_outstanding_writes = DEFAULT_BIOD_MAX_WRITE;
+
+/* list of nfs operations - used to verify results file */
+static char *Ops_name[NOPS] = { "null", "getattr", "setattr", "root", "lookup",
+ "readlink", "read", "wrcache", "write",
+ "create", "remove", "rename", "link",
+ "symlink", "mkdir", "rmdir", "readdir",
+ "fsstat", "access", "commit", "fsinfo",
+ "mknod", "pathconf", "readdirplus" };
+
+
+/*
+ * -------------------------- Prime-client --------------------------
+ */
+
+
+/*
+ * SIGINT Signal Handler
+ * - rpc to all clients to cleanup and exit
+ */
+static void
+multi_cleanup(
+ int sig_id)
+{
+ /* wake up this process if asleep */
+ if (Asleep == 1) {
+ (void) generic_kill(0,SIGALRM);
+ }
+ (void) fprintf(stderr,"\n%s: Caught Signal %d SIGINT\n", sfs_Myname, sig_id);
+ (void) fprintf(stderr,"\nSending interupt signal to clients ... ");
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_STOP") !=0)
+ exit(9);
+ (void) fprintf(stderr,"done\n");
+ (void) fflush(stderr);
+ (void) close(Pc_log_fd);
+ (void) unlink(SFS_PRIME_SYNC_LOG);
+ exit(10);
+
+} /* multi_cleanup */
+
+
+/*
+ * main
+ * synchronizes multi-client sfs run
+ * - wait for 'DONE-MOUNT' from all clients
+ * - tell them to `DO-INIT`
+ * - wait for `DONE-INIT` from all clients
+ * - tell them to `DO-WARMUP`
+ * - wait for 'READY from all clients
+ * - tells them to 'START' and goes to sleep
+ * - wakes up and tells clients to 'STOP'
+ * - wait for 'DONE-TEST' from all clients
+ * - tells clients to 'MOVE-DATA'
+ * - averages multi-client data and exits
+ */
+int
+main(
+ int argc,
+ char * argv[])
+{
+ int i;
+ char monitor_cmd[SFS_MAXPATHLEN+5];
+ int nsigs = 32; /* reasonable default */
+ FILE *pid_fp;
+
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+#endif
+
+ /*
+ * Place pid in pid log file
+ */
+ if ((pid_fp = fopen(SFS_PRM_PID, "a+")) == NULL) {
+ perror(SFS_PRM_PID);
+ exit(1);
+ }
+ (void) fprintf(pid_fp, "%d\n", getpid());
+ (void) fclose(pid_fp);
+
+ (void) fprintf(stderr, "\nSPEC SFS Benchmark Version %s, Creation - %s\n",
+ SFS_VERSION_NUM, SFS_VERSION_DATE);
+ (void) fflush(stderr);
+
+ /* initialize variables, parse input, etc. */
+ do_initialize(argc, argv);
+
+ /*
+ * setup value of nsigs
+ */
+#ifdef __NSIG
+ nsigs = __NSIG;
+#endif
+#ifdef _NSIG
+ nsigs = _NSIG;
+#endif
+#ifdef NSIG
+ nsigs = NSIG;
+#endif
+#if defined(SOLARIS2) && !defined(_sys_nsig)
+ nsigs = _sys_siglistn;
+#endif
+
+ /* trap for all signals */
+
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ /* use XOPEN signal handling */
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ if (DEBUG_PARENT_GENERAL) {
+ if (nsigs == 0) {
+ (void) fprintf (stderr,
+ "WARNING: nsigs not defined, no extra signals caught\n");
+ }
+ for (i = 1; i < nsigs; i++) {
+ /* attempt to set up signal handler for these signals gives an error. */
+ if (i!=SIGCHLD && i!=SIGKILL && i!=SIGSTOP && i!=SIGCONT) {
+ if (sigaction(i,&sig_act,&old_sig_act) == -1) {
+ if (errno == EINVAL)
+ (void) fprintf (stderr,
+ "Skipping invalid signal %d\n", i);
+ else {
+ perror("sigaction failed");
+ exit(11);
+ }
+ }
+ }
+ }
+ }
+
+ /* signals handlers for signals used by sfs_prime */
+ sig_act.sa_handler = multi_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGINT");
+ exit(12);
+ }
+#else
+
+ if (DEBUG_PARENT_GENERAL) {
+ if (nsigs == 0) {
+ (void) fprintf (stderr,
+ "WARNING: nsigs not defined, no extra signals caught\n");
+ }
+ for (i = 1; i < nsigs; i++) {
+ (void) signal(i, generic_catcher);
+ }
+ }
+
+ /* set up SIGINT signal handler */
+ (void) signal(SIGINT, multi_cleanup);
+
+#endif /* USE_POSIX_SIGNALS */
+
+ (void) fprintf(stderr, "Executing SFS Benchmark on %d Client(s).\n",
+ Num_clients);
+ (void) fflush(stderr);
+
+ /* wait for 'DONE-MOUNT message from the clients */
+ (void) fprintf(stderr,
+ "%s Waiting on DONE-MOUNT message from %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+
+ sync_PC_with_clients(); /* wait for clients DONE-MOUNT */
+
+ (void) fprintf(stderr, "%s Received.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* send DO-INIT message to all the clients */
+ (void) fprintf(stderr, "%s Sending DO-INIT message to %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0)
+ exit(13);
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* wait for 'DONE-INIT' message from the clients */
+ (void) fprintf(stderr,
+ "%s Waiting on DONE-INIT message from %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+
+ /*
+ * add an extra time to time_out value.
+ * initializing the SFS testdirs on clients can take a while.
+ */
+ Prime_time_out += EXTRA_INIT_TIME;
+ sync_PC_with_clients(); /* wait for clients DONE-INIT */
+ Prime_time_out -= EXTRA_INIT_TIME; /* reset time_out */
+
+ (void) fprintf(stderr, "%s Received.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* send DO-WARMUP message to all the clients */
+ (void) fprintf(stderr, "%s Sending DO-WARMUP message to %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0)
+ exit(14);
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* wait for 'READY' message from the clients */
+ (void) fprintf(stderr, "%s Waiting on READY message from %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+
+ (void) sleep(P_warmuptime);
+
+ sync_PC_with_clients(); /* wait for clients READY */
+
+ (void) fprintf(stderr, "%s Received.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /*
+ * call the program to trigger START of external monitoring
+ */
+ if (P_script_name) {
+ (void) sprintf(monitor_cmd,"%s %s %s",P_script_name,
+ "START",P_script_args);
+ if (system(monitor_cmd) != 0) {
+ (void) fprintf(stderr,"%s: external monitoring command (%s) failed - %d - continuing.\n ",
+ sfs_Myname, monitor_cmd, errno);
+ (void) fflush(stderr);
+ P_script_name = NULL;
+ }
+
+ }
+
+ /*
+ * wait period before telling clients to START - gives external
+ * performance monitoring utilities enough time to start up.
+ * Value set in sfs_rc (PRIME_SLEEP) - default is 0 seconds.
+ */
+ (void) sleep(Prime_sleep_time);
+
+ /* send START message to all the clients */
+ (void) fprintf(stderr, "%s Sending START message to %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0)
+ exit(15);
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /*
+ * Sleep for <Prime_runtime> seconds
+ * and then send STOP message to tell the sfs clients
+ * that is time to wrap things up.
+ */
+ /* set the Asleep flag go to sleep while clients are executing sfs */
+ Asleep = 1;
+ (void) sleep(Prime_runtime);
+ Asleep = 0;
+
+ (void) fprintf(stderr, "%s Sending STOP message to %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_ALARM") !=0)
+ exit(16);
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+
+ /* wait for DONE-TEST message from clients indicating they completed run */
+ (void) fprintf(stderr, "%s Waiting on DONE-TEST message from %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ sync_PC_with_clients();
+ (void) fprintf(stderr, "%s Received.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /*
+ * call the program to trigger STOP of external monitoring
+ */
+ if (P_script_name) {
+ (void) sprintf(monitor_cmd,"%s %s %s",P_script_name,
+ "DONE",P_script_args);
+ if (system(monitor_cmd) != 0) {
+ (void) fprintf(stderr,"%s: external monitoring command (%s) failed - %d - continuing.\n ",
+ sfs_Myname, monitor_cmd, errno);
+ (void) fflush(stderr);
+ P_script_name = NULL;
+ }
+ }
+
+ /* give enough time to stop external performance monitoring utilities. */
+ (void) sleep(Prime_sleep_time);
+
+ /*
+ * send MOVE-DATA message to Clients to move data across
+ */
+ (void) fprintf(stderr, "%s Sending MOVE-DATA message to %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ if ((int) signal_sfs_clients("PRIME_SIGNAL") !=0)
+ exit(17);
+ (void) fprintf(stderr, "%s Completed.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* wait for SEND-DATA message from all the clients */
+ (void) fprintf(stderr,
+ "%s Waiting on SEND-DATA message from %d client(s).\n",
+ lad_timestamp(), Num_clients);
+ (void) fflush(stderr);
+ sync_PC_with_clients();
+ (void) fprintf(stderr, "%s Received.\n", lad_timestamp());
+ (void) fflush(stderr);
+
+ /* summarize and print aggregate results */
+ print_multi_results();
+
+ /* close files and exit success */
+ (void) close(Pc_log_fd);
+ (void) unlink(SFS_PRIME_SYNC_LOG);
+ (void) unlink(SFS_PRM_PID);
+ return(0);
+
+} /* main */
+
+/*
+ * initialize control variables, open logfiles etc.
+ */
+static void
+do_initialize(
+ int argc,
+ char *argv[])
+{
+ int c;
+ FILE *check_fp;
+ int i;
+ char *cp;
+ extern char *optarg;
+ extern int optind;
+
+
+ sfs_Myname = argv[0];
+ if (argc <= 1) {
+ prog_usage();
+ /* NOTREACHED */
+ }
+
+ while ((c = getopt(argc, argv, "a:A:b:B:C:d:f:k:K:l:m:p:QR:s:t:T:W:w:x:z")) != EOF)
+ switch (c) {
+ case 'a': /* Percent of file set to access;
+ * used in aggregate report.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal acces value %s\n",
+ sfs_Myname, optarg);
+ exit(18);
+ }
+ P_percent_access = atoi(optarg);
+ if (P_percent_access < 0 || P_percent_access > 100) {
+ (void) fprintf(stderr,
+ "%s: %% access must be between 0 and 100\n",
+ sfs_Myname);
+ exit(19);
+ }
+ break;
+
+ case 'A': /* Percent of writes that append;
+ * used in aggregate report.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal append value %s\n",
+ sfs_Myname, optarg);
+ exit(20);
+ }
+ P_percent_append = atoi(optarg);
+ if (P_percent_append < 0 || P_percent_append > 100) {
+ (void) fprintf(stderr,
+ "%s: %% append must be between 0 and 100\n",
+ sfs_Myname);
+ exit(21);
+ }
+ break;
+
+ case 'b': /* Set block size distribution table file
+ * used in aggregate report.
+ */
+ if ((check_fp = fopen(optarg, "r")) == NULL) {
+ cp = strerror(errno);
+ (void) fprintf(stderr, "%s: bad block size file %s: %s\n",
+ sfs_Myname, optarg, cp);
+ exit(22);
+ }
+ P_iodist_file = optarg;
+ (void) fclose(check_fp);
+ break;
+
+
+ case 'B': /* Set the per packet maximum block size
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal block size value %s\n",
+ sfs_Myname, optarg);
+ exit(23);
+ }
+ P_kb_per_block = atoi(optarg);
+ if ((P_kb_per_block < 1) ||
+ (P_kb_per_block > (DEFAULT_MAX_BUFSIZE/1024))) {
+ (void) fprintf(stderr, "%s: illegal block size value %s\n",
+ sfs_Myname, optarg);
+ exit(24);
+ }
+ break;
+
+ case 'C': /*
+ * Set summary result file
+ */
+ if ((sum_result_fp = fopen(optarg, "a+")) == NULL) {
+ cp = strerror(errno);
+ (void) fprintf(stderr,
+ "%s: Unable to create summary result file %s: %s\n",
+ sfs_Myname, optarg, cp);
+ exit(222);
+ }
+ break;
+
+ case 'd': /*
+ * Set Debug_level
+ */
+ Debug_level = set_debug_level(optarg);
+ break;
+
+ case 'f': /* Percent change in file set size
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal file set delta value %s\n",
+ sfs_Myname, optarg);
+ exit(26);
+ }
+ P_percent_fss_delta = atoi(optarg);
+ if (P_percent_fss_delta < 0 || P_percent_fss_delta > 100) {
+ (void) fprintf(stderr,
+ "%s: %% file set delta must be between 0 and 100\n",
+ sfs_Myname);
+ exit(27);
+ }
+ break;
+
+ case 'k': /*
+ * program to start and stop external
+ * performance monitoring. Called at start
+ * and completion of core load generation period.
+ */
+ if ((check_fp = fopen(optarg, "r")) == NULL) {
+ cp = strerror(errno);
+ (void) fprintf(stderr,
+ "%s: program %s protected or missing: %s\n",
+ sfs_Myname, optarg, cp);
+ exit(28);
+ }
+ P_script_name = optarg;
+ (void) fclose(check_fp);
+ break;
+
+ case 'K': /*
+ * Command-line parameters for the external monitor
+ * (see the "-k" option, above)
+ */
+ P_script_args = optarg;
+ break;
+
+ case 'l': /* Set load
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal load value %s\n",
+ sfs_Myname, optarg);
+ exit(29);
+ }
+ P_total_load = atoi(optarg);
+ if (P_total_load < 0) {
+ (void) fprintf(stderr, "%s: load must be > 0\n", sfs_Myname);
+ exit(30);
+ }
+ break;
+
+ case 'm': /* Set mix from a file
+ * used in aggregate reporting only.
+ */
+ if ((check_fp = fopen(optarg, "r")) == NULL) {
+ cp = strerror(errno);
+ (void) fprintf(stderr, "%s: bad mix file: %s: %s\n",
+ sfs_Myname, optarg, cp);
+ exit(31);
+ }
+ P_mix_file = optarg;
+ (void) fclose(check_fp);
+ break;
+
+ case 'p': /* Set number of child processes
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal procs value %s\n",
+ sfs_Myname, optarg);
+ exit(32);
+ }
+ P_children = atoi(optarg);
+ if (P_children < 0) {
+ (void) fprintf(stderr, "%s: number of children must be > 0\n",
+ sfs_Myname);
+ exit(33);
+ }
+ break;
+
+ case 'Q': /* Set NFS/TCP behaviour */
+ P_tcp = 1;
+ break;
+
+ case 'R': /* set maximum async read concurrency level
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal read count value %s\n",
+ sfs_Myname, optarg);
+ exit(34);
+ }
+ P_biod_max_outstanding_reads = atoi(optarg);
+ if (P_biod_max_outstanding_reads < 0) {
+ (void) fprintf(stderr, "%s: read count must be >= 0\n",
+ sfs_Myname);
+ exit(35);
+ }
+ break;
+
+ case 's':
+ /*
+ * Set sleep time so external processes will
+ * have time to startup
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal sleep value %s\n",
+ sfs_Myname, optarg);
+ exit(36);
+ }
+ Prime_sleep_time = atoi(optarg);
+ break;
+
+ case 't': /* Set SFS Runtime value */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal time value %s\n",
+ sfs_Myname, optarg);
+ exit(37);
+ }
+ Prime_runtime = atoi(optarg);
+ if (Prime_runtime < 0) {
+ (void) fprintf(stderr, "%s: run time must be >= 0\n",
+ sfs_Myname);
+ exit(38);
+ }
+ break;
+
+ case 'T': /* Set Test mode operation
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal time_out value %s\n",
+ sfs_Myname, optarg);
+ exit(39);
+ }
+ P_testop = atoi(optarg);
+ if (P_testop >= NOPS) {
+ (void) fprintf(stderr, "%s: illegal test value %d\n",
+ sfs_Myname, P_testop);
+ exit(40);
+ }
+ break;
+
+ case 'W': /* set maximum async write concurrency level
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal write count value %s\n",
+ sfs_Myname, optarg);
+ exit(41);
+ }
+ P_biod_max_outstanding_writes = atoi(optarg);
+ if (P_biod_max_outstanding_writes < 0) {
+ (void) fprintf(stderr, "%s: write count must be >= 0\n",
+ sfs_Myname);
+ exit(42);
+ }
+ break;
+
+ case 'w': /* Set warmup time
+ * used in aggregate reporting only.
+ */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr, "%s: illegal warmup value %s\n",
+ sfs_Myname, optarg);
+ exit(43);
+ }
+ P_warmuptime = atoi(optarg);
+ if (P_warmuptime < 0) {
+ (void) fprintf(stderr, "%s: warmup time must be >= 0\n",
+ sfs_Myname);
+ exit(44);
+ }
+ break;
+
+ case 'x': /* Set Prime-Client time_out value */
+ if (!isdigit(optarg[0])) {
+ (void) fprintf(stderr,
+ "%s: illegal time_out value %s\n",
+ sfs_Myname, optarg);
+ exit(45);
+ }
+ Prime_time_out = atoi(optarg);
+ break;
+
+ case 'z': /* Do raw data dumps
+ * used in aggregate reporting only.
+ */
+ P_dump_data++;
+ break;
+
+ default:
+ prog_usage();
+ /* NOTREACHED */
+ } /* end switch on argument */
+
+ Num_clients = argc - optind;
+
+ /*
+ * allocate space and store clients names
+ */
+ Client_names = (char **) malloc(Num_clients * sizeof(char *));
+ if (Client_names == (char **) 0) {
+ (void) fprintf(stderr, "%s: client name malloc %d bytes failed",
+ sfs_Myname, Num_clients * sizeof(char **));
+ exit(46);
+ }
+
+ for (i = 0; optind < argc; i++, optind++) {
+ Client_names[i] = argv[optind];
+ if (gethostbyname(argv[optind]) == NULL) {
+ (void) fprintf(stderr, "\n%s: unknown client - %s\n",
+ sfs_Myname, argv[optind]);
+ exit(47);
+ }
+ }
+
+ if (sum_result_fp == NULL)
+ sum_result_fp = stdout;
+
+ Pc_log_fd = open(SFS_PRIME_SYNC_LOG, (O_RDWR | O_CREAT), 0666);
+ if (Pc_log_fd == -1) {
+ perror(SFS_PRIME_SYNC_LOG);
+ exit(48);
+ }
+
+} /* do_initialize */
+
+
+/*
+ * Small utility routine to pretty print out the names
+ * of the clients which did not respond to the message.
+ */
+static void
+printdeadclients(void)
+{
+ int *clients;
+ int client;
+ int i;
+ FILE *fd;
+
+ if ((clients = (int *)malloc(sizeof(int) * Num_clients)) == NULL) {
+ (void) fprintf(stderr, "%s: malloc failed\n", sfs_Myname);
+ (void) fflush(stderr);
+ return;
+ }
+
+ for (i = 0; i < Num_clients; i++) {
+ clients[i] = 0;
+ }
+
+ fd = fopen(SFS_PRIME_SYNC_LOG, "r");
+ if (fd == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ (void) fflush(stderr);
+ return;
+ }
+
+ while(fread(&client, sizeof(int), 1, fd) == 1) {
+ if (client > 0 && client <= Num_clients)
+ clients[client - 1] = 1;
+ }
+
+ for (i = 0; i < Num_clients; i++) {
+ if (clients[i] == 0) {
+ (void) fprintf(stderr, "\n%s: Did not get signal from client %s\n",
+ sfs_Myname, Client_names[i]);
+ (void) fflush(stderr);
+ }
+ }
+ (void) fclose(fd);
+ free (clients);
+}
+
+/*
+ * monitor Logfile until all Clients write to it.
+ *
+ * Each client appends its client id to the file. So the size
+ * of the log file divided by sizeof(int) is the number of
+ * clients that have responded.
+ */
+static void
+sync_PC_with_clients(void)
+{
+ struct stat statb; /* for fstat */
+ int num_secs; /* keep count of time */
+ int clientsremaining;
+
+ num_secs = 0;
+ do {
+ (void) sleep(1);
+ if (fstat(Pc_log_fd, &statb) == -1) {
+ (void) fprintf(stderr, "%s: can't stat Prime Client log %s",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ exit(49);
+ }
+ num_secs++;
+ clientsremaining = Num_clients - (statb.st_size / sizeof(int));
+ } while ( (clientsremaining > 0) && (num_secs < Prime_time_out));
+
+ /* if clients not responding then terminate experiment */
+ if (clientsremaining > 0) {
+ (void) fprintf(stderr,
+ "\n%s: Prime Client timeout - did not get signal from %d client(s)\n",
+ sfs_Myname, clientsremaining);
+ printdeadclients();
+ /* send message to clients to stop */
+ (int) signal_sfs_clients("PRIME_STOP");
+ exit(50);
+ }
+
+ /* if more clients than exist responded then syncd is telling us to exit */
+ if (clientsremaining < 0) {
+ (void) fprintf(stderr,
+ "\n%s: Prime Client got too many signals - expected %d got %ld\n",
+ sfs_Myname, Num_clients, statb.st_size / sizeof(int));
+ /* send message to clients to stop */
+ (int) signal_sfs_clients("PRIME_STOP");
+ exit(51);
+ }
+
+ /* success, so go ahead and truncate the sync logfile */
+ (void)close(Pc_log_fd);
+ Pc_log_fd = open(SFS_PRIME_SYNC_LOG,
+ (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666);
+ if (Pc_log_fd == -1) {
+ /* problem in truncating sync logfile - stop experiment */
+ (void) fprintf(stderr, "%s: can't truncate Prime Client log %s",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ (void) fflush(stderr);
+ (int) signal_sfs_clients("PRIME_STOP");
+ exit(52);
+ }
+
+} /* sync_PC_with_clients */
+
+
+#ifdef CLOSE_CLNT_HANDLE
+static int close_clnt_handle = 1;
+#else
+static int close_clnt_handle = 0;
+#endif
+/*
+ * makes RPC to all clients, dependent on message
+ * Save the client handles to keep from doing unnecessary portmapper calls.
+ * This can help if we have to route through the busy server.
+ */
+static int
+signal_sfs_clients(char *message)
+{
+ static int Transac_num = 0; /* transaction number */
+ static CLIENT ** sfs_clnt_handle = NULL;
+ static int *sfs_socket = NULL;
+ int * result;
+ int i;
+ sync_string sync_signal;
+ char transaction_string[MAX_STR1_LEN];
+
+ (void) sprintf(transaction_string,"Prime_%i",++Transac_num);
+ sync_signal.clnt_type = message;
+ sync_signal.clnt_data = "";
+ sync_signal.clnt_transaction = transaction_string;
+
+ if (sfs_clnt_handle == NULL) {
+ sfs_socket = (int *) calloc(Num_clients, sizeof(int));
+ if (sfs_socket == (int *) 0) {
+ (void) fprintf(stderr, "%s: socket malloc failed.\n",
+ sfs_Myname);
+ exit(53);
+ }
+
+ /* allocate space for tcp handles */
+ sfs_clnt_handle = (CLIENT **) calloc(Num_clients, sizeof(CLIENT));
+ if (sfs_clnt_handle == (CLIENT **)0 ) {
+ (void) fprintf(stderr, "%s: clnttcp_create out of memory\n",
+ sfs_Myname);
+ exit(54);
+ }
+ }
+
+ /* set up the tcp handles for all the clients */
+ for (i = 0; i < Num_clients; i++) {
+ if (sfs_clnt_handle[i] == NULL) {
+ struct hostent *host_info;
+ struct sockaddr_in clnt_addr;
+#ifdef SUNOS
+ int fd;
+#endif
+
+ sfs_socket[i] = RPC_ANYSOCK;
+
+ if ((host_info = gethostbyname(Client_names[i])) == NULL)
+ return((int) RPC_UNKNOWNHOST);
+ (void) memset((char *) &clnt_addr, '\0', sizeof(clnt_addr));
+ (void) memmove((char *) &clnt_addr.sin_addr,
+ (char *) host_info->h_addr,
+ host_info->h_length);
+ clnt_addr.sin_family = AF_INET;
+ clnt_addr.sin_port = 0;
+
+ /*
+ * Create client "handle" used for calling CL_MESSAGEPROG on the
+ * sfs client(s). We tell the RPC package to use the "tcp"
+ * protocol when contacting the clients.
+ */
+ sfs_clnt_handle[i] = clnttcp_create(&clnt_addr, SFS_SYNCPROG,
+ SFS_SYNCVERS, &sfs_socket[i],
+ MAX_STR2_LEN, MAX_STR2_LEN);
+
+ if (sfs_clnt_handle[i] == (CLIENT *) NULL) {
+ /*
+ * Couldn't establish connection with the sfs Client.
+ * print error message and return status
+ */
+ clnt_pcreateerror(Client_names[i]);
+ return((int) RPC_FAILED);
+ }
+#ifdef SUNOS
+ /*
+ * Some commands in 4.X will fail if there are too many file
+ * descriptors open, if the sfs_ext_mon uses one
+ * of those then it will fail. We set the close-on-exec
+ * flag to help them.
+ */
+ if (clnt_control(sfs_clnt_handle[i], CLGET_FD, (char *)&fd) ==
+ FALSE) {
+ clnt_pcreateerror(Client_names[i]);
+ return((int) RPC_FAILED);
+ }
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+ perror("F_SETFD");
+ return((int) RPC_FAILED);
+ }
+#endif
+ } /* end no client handle */
+
+ /*
+ * Call the remote procedure "signal_sfs_cl_1" on each of the clients
+ * pass the client number so sfs_syncd will know which client files
+ * to look for in a shared /tmp area
+ */
+ sync_signal.clnt_id = i + 1;
+ result = signal_sfs_1(&sync_signal, sfs_clnt_handle[i]);
+
+ if (result == NULL) {
+ /* Error occurred. Print error message and return error. */
+ clnt_perror(sfs_clnt_handle[i], Client_names[i]);
+ return((int) RPC_CANTSEND);
+ }
+
+ /* Okay, we successfully called the remote procedure. */
+ if (*result == 0) {
+ /*
+ * remote procedure was unable to write to its sync file.
+ * Print error message and return failure.
+ */
+ (void) fprintf(stderr,
+ "\n%s: Unable to perform remote procedure on %s . \n",
+ sfs_Myname, Client_names[i]);
+ (void) fflush(stderr);
+ return((int) RPC_FAILED);
+ }
+ if (close_clnt_handle) {
+ clnt_destroy(sfs_clnt_handle[i]);
+ sfs_clnt_handle[i] = NULL;
+ }
+ } /* end for each client */
+
+ return(0);
+
+} /* signal_sfs_clients */
+
+
+/*
+ * summarize and print multi-client results
+ */
+static void
+print_multi_results(void)
+{
+ FILE * fd; /* results files */
+ int i;
+ int k;
+ char res_file[SFS_MAXPATHLEN]; /* results filename */
+ char str[MAX_LINE_LEN];
+
+ double stdev_msec, var_msec;
+ float tot_squared_time_msec, tot_sum2_msec;
+ float tot_got, tot_secs, tot_msec_calls, tot_res_time;
+ int tot_want, tot_calls, tot_errors;
+
+ double sq_conf_interval_msec;
+ double conf_interval_msec;
+ int totals;
+ int invalid = 0;
+
+#define MAXOPNAME 19
+ struct client_stats {
+ /* First line */
+ int version;
+ int num_io_files;
+ int num_access_io_files;
+ int num_non_io_files;
+ int num_symlinks;
+ int num_dirs;
+ /* NOPS lines */
+ struct client_op_stats {
+ char op[MAXOPNAME];
+ int want;
+ float got;
+ int calls;
+ int errors;
+ float secs;
+ float msec_calls;
+ float res_time;
+ float squared_time_msec;
+ float sum2_msec;
+ } op_stats[NOPS];
+ /* Last line */
+ float sum_calls_sec;
+ float sum_msec_calls;
+ int sum_secs;
+ int sum_calls;
+ int client_invalid_flag;
+ uint32_t total_fss_bytes;
+ uint32_t least_fss_bytes;
+ uint32_t most_fss_bytes;
+ uint32_t base_fss_bytes;
+ } *client_stats;
+
+
+ /*
+ * malloc space for statistics
+ * Note last entry is for running totals.
+ */
+ client_stats = (struct client_stats *) calloc(Num_clients+1,
+ sizeof(struct client_stats));
+ if (client_stats == (struct client_stats *)0) {
+ (void) fprintf(stderr, "%s: client_stats malloc failed.\n",
+ sfs_Myname);
+ (void) fflush(stderr);
+ exit(55);
+ }
+
+ totals = Num_clients;
+ /*
+ * Read each client file one at a time gathering statistics
+ */
+ for (i = 0; i < Num_clients;i++) {
+ (void) sprintf(res_file, "%s%i", PRIME_RESULTS_LOG, i + 1);
+ fd = fopen(res_file, "r");
+ if (fd == (FILE *)NULL) {
+ (void) fprintf(stderr, "%s: Cannot open results file - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(56);
+ }
+
+ /*
+ * the SFS clients compute its File set info. at runtime;
+ * the clients pass back the computed value in the results
+ * (RPC) info. Get the fileset info used by the clients and compute
+ * the aggregate value.
+ */
+ if (fgets(str, MAX_LINE_LEN, fd) == NULL) {
+ (void) fprintf(stderr,"%s: can't read data in results file - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(57);
+ }
+ if (sscanf(str,"%d %d %d %d %d %d",
+ &client_stats[i].version,
+ &client_stats[i].num_io_files,
+ &client_stats[i].num_access_io_files,
+ &client_stats[i].num_non_io_files,
+ &client_stats[i].num_symlinks,
+ &client_stats[i].num_dirs) != 6) {
+ (void) fprintf(stderr,"%s: data in results file unparseable - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(58);
+ }
+
+ client_stats[totals].num_io_files += client_stats[i].num_io_files;
+ client_stats[totals].num_access_io_files +=
+ client_stats[i].num_access_io_files;
+ client_stats[totals].num_non_io_files +=
+ client_stats[i].num_non_io_files;
+ client_stats[totals].num_symlinks +=
+ client_stats[i].num_symlinks;
+ client_stats[totals].num_dirs += client_stats[i].num_dirs;
+
+ /* Gather per operation statistics */
+ for (k = 0; k < NOPS; k++) {
+ if (fgets(str, MAX_LINE_LEN, fd) == NULL) {
+ (void) fprintf(stderr,
+ "%s: can't read data in results file - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(59);
+ }
+ if (sscanf(str, "%s %d%% %f%% %d %d %f %f %f%% %f %f",
+ client_stats[i].op_stats[k].op,
+ &client_stats[i].op_stats[k].want,
+ &client_stats[i].op_stats[k].got,
+ &client_stats[i].op_stats[k].calls,
+ &client_stats[i].op_stats[k].errors,
+ &client_stats[i].op_stats[k].secs,
+ &client_stats[i].op_stats[k].msec_calls,
+ &client_stats[i].op_stats[k].res_time,
+ &client_stats[i].op_stats[k].squared_time_msec,
+ &client_stats[i].op_stats[k].sum2_msec) != 10) {
+ (void) fprintf(stderr,"%s: data in results file unparseable - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(60);
+ }
+ if (strcmp(client_stats[i].op_stats[k].op,Ops_name[k]) != 0) {
+ (void) fprintf(stderr, "%s: bad data in results file\n",
+ sfs_Myname);
+ (void) fflush(stderr);
+ exit(61);
+ }
+ }
+
+ if (fgets(str, 100, fd) == NULL) {
+ (void) fprintf(stderr,"%s: can't read data in results file - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(62);
+ }
+ if (sscanf(str,"%f %f %d %d %d %u %u %u %u",
+ &client_stats[i].sum_calls_sec,
+ &client_stats[i].sum_msec_calls,
+ &client_stats[i].sum_secs,
+ &client_stats[i].sum_calls,
+ &client_stats[i].client_invalid_flag,
+ &client_stats[i].total_fss_bytes,
+ &client_stats[i].least_fss_bytes,
+ &client_stats[i].most_fss_bytes,
+ &client_stats[i].base_fss_bytes) != 9) {
+ (void) fprintf(stderr,"%s: data in results file unparseable - %s\n",
+ sfs_Myname, res_file);
+ (void) fflush(stderr);
+ exit(63);
+ }
+
+ client_stats[totals].sum_secs += client_stats[i].sum_secs;
+ client_stats[totals].sum_calls += client_stats[i].sum_calls;
+ client_stats[totals].sum_calls_sec +=
+ client_stats[i].sum_calls_sec;
+ client_stats[totals].sum_msec_calls +=
+ client_stats[i].sum_msec_calls;
+ client_stats[totals].total_fss_bytes +=
+ client_stats[i].total_fss_bytes;
+ client_stats[totals].least_fss_bytes +=
+ client_stats[i].least_fss_bytes;
+ client_stats[totals].most_fss_bytes +=
+ client_stats[i].most_fss_bytes;
+ client_stats[totals].base_fss_bytes +=
+ client_stats[i].base_fss_bytes;
+
+ (void) fclose(fd);
+ } /* for Num_clients */
+
+ nfs_version = client_stats[0].version;
+
+ /*
+ * print the aggregate test parameters
+ */
+ (void) fprintf(stdout, "\nAggregate Test Parameters: \n");
+ (void) fprintf(stdout, " Number of processes = %d\n",
+ P_children * Num_clients);
+ (void) fprintf(stdout, " Requested Load (NFS V%d operations/second) = %d\n",
+ nfs_version, P_total_load * Num_clients);
+ (void) fprintf(stdout, "%s%d\n",
+ " Maximum number of outstanding biod writes = ",
+ P_biod_max_outstanding_writes);
+ (void) fprintf(stdout, "%s%d\n",
+ " Maximum number of outstanding biod reads = ",
+ P_biod_max_outstanding_reads);
+
+ (void) fprintf(stdout, "%s%d\n%s%d\n",
+ " Warm-up time (seconds) = ", P_warmuptime,
+ " Run time (seconds) = ", Prime_runtime);
+ if (P_mix_file)
+ (void) fprintf(stdout,"%s%s\n", " NFS Mixfile = ", P_mix_file);
+ if (P_iodist_file)
+ (void) fprintf(stdout,"%s%s\n", " Block Size Distribution file = ",
+ P_iodist_file);
+
+
+ (void) fprintf(stdout, "%s%6d%s\n", " File Set = ",
+ client_stats[totals].num_io_files,
+ " Files created for I/O operations");
+ (void) fprintf(stdout, "%s%10d%s\n", " ",
+ client_stats[totals].num_access_io_files,
+ " Files accessed for I/O operations");
+ (void) fprintf(stdout, "%s%10d%s\n", " ",
+ client_stats[totals].num_non_io_files,
+ " Files for non-I/O operations");
+ (void) fprintf(stdout, "%s%10d%s\n", " ",
+ client_stats[totals].num_symlinks,
+ " Symlinks");
+ (void) fprintf(stdout, "%s%10d%s\n", " ",
+ client_stats[totals].num_dirs,
+ " Directories");
+ (void) fprintf(stdout, "%s%s\n", " ",
+ " Additional non-I/O files created as necessary\n");
+
+
+ (void) fprintf(stdout,"SFS Aggregate Results for %d Client(s), %s\n",
+ Num_clients, lad_timestamp());
+ (void) fprintf(stderr, "SPEC SFS Benchmark Version %s, Creation - %s\n",
+ SFS_VERSION_NUM, SFS_VERSION_DATE);
+ (void) fprintf(stdout, "NFS Protocol Version %d\n", nfs_version);
+
+ /* print column headers for per operation statistics */
+ (void) fprintf(stdout,
+"------------------------------------------------------------------------------");
+(void) fprintf(stdout, "\n");
+(void) fprintf(stdout,"%s\n%s\n%s\n%s\n%s\n",
+"NFS Target Actual NFS NFS Mean Std Dev Std Error Pcnt ",
+"Op NFS NFS Op Op Response Response of Mean,95% of ",
+"Type Mix Mix Success Error Time Time Confidence Total",
+" Pcnt Pcnt Count Count Msec/Op Msec/Op +- Msec/Op Time ",
+"------------------------------------------------------------------------------");
+ (void) fflush(stdout);
+
+ /* print per operation statistics */
+ for (k = 0; k < NOPS; k++) {
+ tot_got = 0;
+ tot_secs = 0;
+ tot_msec_calls = 0;
+ tot_res_time = 0;
+ tot_want = 0;
+ tot_calls = 0;
+ tot_errors = 0;
+ tot_sum2_msec = 0;
+ tot_squared_time_msec = 0;
+
+ /* total the results from each client */
+ for (i = 0; i < Num_clients; i++) {
+
+ tot_want += client_stats[i].op_stats[k].want;
+ tot_got += client_stats[i].op_stats[k].got;
+ tot_calls += client_stats[i].op_stats[k].calls;
+ tot_errors += client_stats[i].op_stats[k].errors;
+ tot_secs += client_stats[i].op_stats[k].secs;
+ tot_msec_calls += client_stats[i].op_stats[k].msec_calls;
+ tot_res_time += client_stats[i].op_stats[k].res_time;
+ tot_squared_time_msec +=
+ client_stats[i].op_stats[k].squared_time_msec;
+ tot_sum2_msec += client_stats[i].op_stats[k].sum2_msec;
+
+ } /* end for each client */
+
+ /*
+ * If the total wanted is zero and no operations succeeded or
+ * errored don't print out the
+ * summary. However leave it in the individual client logs
+ * in case there is some interesting error.
+ */
+ if (tot_want == 0 && tot_calls == 0 && tot_errors == 0)
+ continue;
+
+ /* compute the standard deviation for the mean response time */
+ if (tot_calls <= 1) {
+ stdev_msec = 0;
+ var_msec = 0;
+ } else {
+ /* variance = 1/(n-1) * (sum(x^2) - 1/n * (sum(x))^2) */
+ var_msec = (tot_squared_time_msec - (tot_sum2_msec / tot_calls)) /
+ (tot_calls-1);
+
+ if(var_msec == 0.0) {
+ stdev_msec = 0.0;
+ } else
+ stdev_msec = sqrt(var_msec);
+ }
+
+ /* compute the confidence interval */
+ if (tot_calls > 0) {
+ sq_conf_interval_msec = DEFAULT_CHI_SQR_CI *
+ (stdev_msec / tot_calls);
+ if (sq_conf_interval_msec == 0.0) {
+ if (DEBUG_PARENT_GENERAL) {
+ (void) fprintf(stderr,
+ "Error computing confidence interval for mean\n");
+ (void) fflush(stderr);
+ }
+ conf_interval_msec = 0.0;
+ } else
+ conf_interval_msec = sqrt(sq_conf_interval_msec);
+ } else {
+ conf_interval_msec = 0.0;
+ }
+
+ /* print the per op statistics */
+ (void) fprintf(stdout,
+ "%-12s%3d%% %5.1f%% %7d %5d %8.2f %8.2f %8.2f %5.1f%%\n",
+ Ops_name[k], /* op name */
+ tot_want / Num_clients, /* target mix */
+ tot_got / Num_clients, /* actual mix */
+ tot_calls, /* successes */
+ tot_errors, /* errors */
+ tot_msec_calls / Num_clients, /* mean */
+ stdev_msec, /* std dev */
+ conf_interval_msec, /* conf int */
+ tot_res_time / Num_clients); /* % of time */
+
+ } /* end for each op */
+
+ (void) fprintf(stdout,
+"------------------------------------------------------------------------------\n");
+
+ /* check and report client INVALID RUN */
+ for (i = 0; i < Num_clients; i++) {
+ if (client_stats[i].client_invalid_flag != 0) {
+ invalid++;
+ (void) fprintf(stdout,"INVALID RUN reported for Client %d (%s).\n",
+ i+1, Client_names[i]);
+ if (client_stats[i].client_invalid_flag >= INVALID_GOODCALLS) {
+ (void) fprintf(stdout,"INVALID RUN, %s\n",
+ invalid_str[client_stats[i].client_invalid_flag]);
+ } else {
+ (void) fprintf(stdout,
+ "INVALID RUN, ILLEGAL PARAMETER: Non-standard %s\n",
+ invalid_str[client_stats[i].client_invalid_flag]);
+ }
+ }
+ }
+
+ (void) fprintf(stdout, "\n");
+ (void) fprintf(stdout,
+ " --------------------------------------------------------\n");
+ (void) fprintf(stdout,
+ " | SPEC SFS VERSION %6s AGGREGATE RESULTS SUMMARY |\n",
+ SFS_VERSION_NUM);
+ (void) fprintf(stdout,
+ " --------------------------------------------------------\n");
+ (void) fprintf(stdout, "NFS V%d THROUGHPUT: ", nfs_version);
+ (void) fprintf(stdout,"%7.0f Ops/Sec AVG. RESPONSE TIME: %7.1f Msec/Op\n",
+ client_stats[totals].sum_calls_sec,
+ client_stats[totals].sum_msec_calls / Num_clients);
+
+ (void) fprintf(stdout, "%s PROTOCOL\n", P_tcp ? "TCP" : "UDP");
+ (void) fprintf(stdout, "NFS MIXFILE:");
+ if (P_mix_file)
+ (void) fprintf(stdout,"%s\n", P_mix_file);
+ else (void)
+ (void) fprintf(stdout," [ SFS default ]\n");
+
+ (void) fprintf(stdout, "AGGREGATE REQUESTED LOAD: %d Ops/Sec \n",
+ (Num_clients * P_total_load));
+
+ (void) fprintf(stdout,"TOTAL NFS OPERATIONS: %8d TEST TIME: %d Sec \n",
+ client_stats[totals].sum_calls,
+ client_stats[totals].sum_secs / Num_clients);
+ (void) fprintf(stdout,"NUMBER OF SFS CLIENTS: %d\n", Num_clients);
+ (void) fprintf(stdout,
+ "TOTAL FILE SET SIZE CREATED: %6.1f MB\n" ,
+ client_stats[totals].total_fss_bytes/1024.0);
+ (void) fprintf(stdout,
+ "TOTAL FILE SET SIZE ACCESSED: %6.1f - %6.1f MB (%lu%% to %lu%% of Base)\n",
+ client_stats[totals].least_fss_bytes/1024.0,
+ client_stats[totals].most_fss_bytes/1024.0,
+ (100 * client_stats[totals].least_fss_bytes) /
+ client_stats[totals].base_fss_bytes,
+ (100 * client_stats[totals].most_fss_bytes) /
+ client_stats[totals].base_fss_bytes);
+ (void) fprintf(stdout, "\n");
+
+ (void) fprintf(stdout,
+ "------------------------------------------------------------------------");
+ (void) fprintf(stdout,"\n\n");
+ (void) fflush(stdout);
+
+ /*
+ * Summary results file format:
+ * response total elps prcs biod vers
+ * load thruput time ops time V P fileset_sz clnt rd wr
+ * DDDDDDD FFFFFFF FFFFFFF DDDDDDDD DDDD D C DDDDDDDDDD DDD DD DD DD SSSS
+ */
+ (void) fprintf(sum_result_fp,
+"%7s %7d %7.0f %7.1f %8d %4d %1d %c %10lu %3d %2d %2d %2d %s\n",
+ invalid ? "INVALID" : "",
+ Num_clients * P_total_load,
+ client_stats[totals].sum_calls_sec,
+ client_stats[totals].sum_msec_calls / Num_clients,
+ client_stats[totals].sum_calls,
+ client_stats[totals].sum_secs / Num_clients,
+ nfs_version,
+ P_tcp ? 'T' : 'U',
+ (unsigned long)client_stats[totals].total_fss_bytes,
+ Num_clients,
+ P_children,
+ P_biod_max_outstanding_reads,
+ P_biod_max_outstanding_writes,
+ SFS_VERSION_NUM);
+ (void) fflush(sum_result_fp);
+
+ (void) free(client_stats);
+} /* print_multi_results */
+
+static void
+prog_usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-a access_pcnt] [-A append_pcnt] [-b blocksz_file] [-B block_size]\n",
+ sfs_Myname);
+ (void) fprintf(stderr,
+ "\t[-C summary_file] [-d debug_level] [-f file_set_delta]\n");
+ (void) fprintf(stderr,
+ "\t[-k script_name] [-K script_args] [-l load] [-m mix_file]\n");
+ (void) fprintf(stderr,
+ "\t[-p procs] [-Q] [-R biod_reads] [-s sleeptime]\n");
+ (void) fprintf(stderr,
+ "\t[-t time] [-T op_num] [-w warmup] [-x timeout]\n");
+ (void) fprintf(stderr,
+ "\t[-W biod_writes] [-z] <hostname1> [<hostname2>...]\n");
+
+
+ (void) fflush(stderr);
+ (int) signal_sfs_clients("PRIME_STOP");
+ exit(64);
+ /* NOTREACHED */
+} /* prog_usage */
+
+/* sfs_m_prm.c */
--- /dev/null
+#ifndef lint
+static char sccsid[] = "@(#)sfs_m_snc.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ *.Exported_routines
+ * None.
+ *
+ *.Local_routines
+ * void sfs_syncprog_1(struct svc_req *, SVCXPRT *)
+ * int * signal_sync_sfs_1(struct sync_string *)
+ * void lad_syncd_cleanup(int)
+ *
+ *.Revision_history
+ * 04-Dec-91 Keith Include sfs_def.h for SYSV/SVR4 mem*
+ * functions. Include string.h for SYSV/SVR4.
+ * 28-Nov-91 Teeluckingh Fixed 'multiple signals to sfs'
+ * problem. Uses a 'transaction id' field in
+ * the sync rpc xdr structure to compare
+ * previous rpc, if the current transaction id
+ * matches the previous one then sfs_syncd
+ * just return 'success' to the client. If the
+ * transaction ids do not match, the actions
+ * are performed and the transaction id value
+ * is saved.
+ * 17-Jun-91 Teelucksingh Creation - multi-client
+ * synchronization server sfs_syncd.
+ * Processes the sfs sync rpcs between systems.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <sys/signal.h>
+#include <sys/file.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+
+/*
+ * ----------------------- External Definitions -----------------------
+ */
+
+/* forward definitions for local routines */
+static void sfs_syncprog_1(struct svc_req *, SVCXPRT *);
+static int * signal_sync_sfs_1(struct sync_string *);
+static void lad_syncd_cleanup(int);
+
+/*
+ * ----------------------- Static Definitions -----------------------
+ */
+
+int Debug_level = 0; /* flag indicates prime client debug mode */
+char *sfs_Myname; /* program name */
+
+static char previous_transaction[MAX_STR1_LEN]; /* to hold transaction id */
+
+
+/*
+ * ------------------- Multi-client Synchronization -------------------
+ */
+
+
+/*ARGSUSED*/
+int
+main(
+ int argc,
+ char * argv[])
+{
+ char *nameptr;
+ SVCXPRT * transp;
+ FILE *pid_fp;
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ struct sigaction sig_act, old_sig_act;
+#endif /* USE_POSIX_SIGNALS */
+
+ /*
+ * Place pid in pid log file
+ */
+ if ((pid_fp = fopen(SFS_SYNCD_PID, "a+")) == NULL) {
+ perror(SFS_SYNCD_PID);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(1);
+ }
+ (void) fprintf(pid_fp, "%d\n", getpid());
+ (void) fclose(pid_fp);
+
+ sfs_Myname = argv[0];
+
+ if ((nameptr = strrchr(argv[0], '/')) != NULL)
+ sfs_Myname = ++nameptr;
+
+#if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
+ /* use XOPEN signal handling */
+ sig_act.sa_handler = generic_catcher;
+ (void)sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+
+ /* signals handlers for signals used by sfs_prime */
+ sig_act.sa_handler = lad_syncd_cleanup;
+ if (sigaction(SIGINT,&sig_act,&old_sig_act) != 0) {
+ perror("sigaction failed: SIGINT");
+ (void) unlink(SFS_SYNCD_PID);
+ exit(4);
+ }
+#else
+ /* set up SIGINT signal handler */
+ (void) signal(SIGINT, lad_syncd_cleanup);
+#endif /* USE_POSIX_SIGNALS */
+
+ (void) fprintf(stderr,"--------------------\n");
+ (void) fprintf(stderr,"Start of sfs run.\n");
+
+ (void) pmap_unset(SFS_SYNCPROG, SFS_SYNCVERS);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == ((SVCXPRT *) NULL)) {
+ (void) fprintf(stderr, "%s: cannot create udp service.\n", sfs_Myname);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(5);
+ }
+ if (!svc_register(transp, SFS_SYNCPROG, SFS_SYNCVERS,
+ sfs_syncprog_1, IPPROTO_UDP)) {
+ (void) fprintf(stderr,
+ "%s: unable to register (SFS_SYNCPROG,SFS_SYNCVERS, udp).\n",
+ sfs_Myname);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(6);
+ }
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == ((SVCXPRT *) NULL)) {
+ (void) fprintf(stderr, "%s: cannot create tcp service.\n", sfs_Myname);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(6);
+ }
+ if (!svc_register(transp, SFS_SYNCPROG, SFS_SYNCVERS,
+ sfs_syncprog_1, IPPROTO_TCP)) {
+ (void) fprintf(stderr,
+ "%s: unable to register (SFS_SYNCPROG, SFS_SYNCVERS, tcp).\n",
+ sfs_Myname);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(7);
+ }
+
+ svc_run();
+ (void) fprintf(stderr, "%s: svc_run returned\n", sfs_Myname);
+ return(1);
+
+} /* main */
+
+
+static void
+sfs_syncprog_1(
+ struct svc_req * rqstp,
+ SVCXPRT * transp)
+{
+ union {
+ sync_string signal_sync_sfs_1_arg;
+ } argument;
+ char * result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char * (*local)();
+
+ switch (rqstp->rq_proc) {
+ case SIGNAL_NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case SIGNAL_SFS:
+ xdr_argument = xdr_sync_string;
+ xdr_result = xdr_int;
+ local = (char * (*)()) signal_sync_sfs_1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *) &argument, '\0', sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ (void) fprintf(stderr, "%s: unable to free arguments\n", sfs_Myname);
+ (void) unlink(SFS_SYNCD_PID);
+ exit(8);
+ }
+
+} /* sfs_syncprog_1 */
+
+
+/*
+ * signal_sync_sfs_1 - multi-client synch RPC
+ * Provides interface between sfs program running
+ * on multiple clients and the controlling sfs_prime program.
+ */
+static int *
+signal_sync_sfs_1(
+ struct sync_string * sfs_signal)
+{
+ static int result = 0 ; /* return status - failure */
+ FILE * fp;
+ int sfs_pid; /* sfs parent process pid */
+ char datafile[SFS_MAXPATHLEN]; /* results file */
+ char CL_Logname[SFS_MAXPATHLEN];
+
+ result = 0;
+ /* if a duplicate transactions then just return success to calling client */
+ if (strcmp(sfs_signal->clnt_transaction, previous_transaction) == 0) {
+ (void) fprintf(stderr,"%s: Got a duplicate signal - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ result = 1;
+ return(&result);
+ }
+
+ if (strcmp(sfs_signal->clnt_type,"CLIENT_SIGNAL") == 0) {
+
+ /*
+ * message from parent sfs process on client to Prime-client
+ * (sfs_prime).
+ *
+ * Append client id to Prime client sync logfile
+ */
+ fp = fopen(SFS_PRIME_SYNC_LOG, "a");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ return (&result);
+ }
+ (void) fwrite((char *)&sfs_signal->clnt_id,
+ sizeof(sfs_signal->clnt_id), 1, fp);
+ (void) fclose(fp);
+ result = 1;
+ (void) sprintf(previous_transaction, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got Client_SIGNAL - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ return (&result); /* success */
+
+ } else if (strcmp(sfs_signal->clnt_type,"CLIENT_DATA") == 0) {
+
+ /*
+ * message from parent sfs process on client to Prime-client
+ * completed run, here are my results. Write it to file and let
+ * Prime client know about it.
+ */
+ (void) sprintf(datafile,"%s%d",
+ PRIME_RESULTS_LOG, sfs_signal->clnt_id);
+ fp = fopen(datafile, "w");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, datafile);
+ return (&result);
+ }
+ (void) fprintf(fp,"%s",sfs_signal->clnt_data);
+ (void) fclose(fp);
+
+ /* after writing data write client id to sync log */
+ fp = fopen(SFS_PRIME_SYNC_LOG, "a");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ return (&result);
+ }
+ (void) fwrite((char *)&sfs_signal->clnt_id,
+ sizeof(sfs_signal->clnt_id), 1, fp);
+ (void) fclose(fp);
+
+ /* let the remote process know success */
+ result = 1;
+ (void) sprintf(previous_transaction, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got Client_DATA - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ return (&result);
+
+ } else if (strcmp(sfs_signal->clnt_type,"CLIENT_STOP") == 0) {
+
+ /*
+ * message from parent sfs process on client to Prime-client
+ * (sfs_prime) to stop due to error.
+ */
+ fp = fopen(SFS_PRIME_SYNC_LOG, "a");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, SFS_PRIME_SYNC_LOG);
+ return (&result);
+ }
+ /*
+ * Write out client id 1000 times to fool prime into thinking
+ * all clients have responded and will get an error when it
+ * tries to communicate to it.
+ */
+ for (result = 0; result < 1000; result++)
+ (void) fwrite((char *)&sfs_signal->clnt_id,
+ sizeof(sfs_signal->clnt_id), 1, fp);
+ (void) fclose(fp);
+ result = 1;
+ (void) sprintf(previous_transaction, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got Client_STOP - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ return (&result); /* success */
+
+ } else if (strcmp(sfs_signal->clnt_type,"PRIME_SIGNAL") == 0) {
+
+ /*
+ * message from the Prime client (sfs_prime)
+ * send SIGUSR1 signal to parent sfs process on
+ * client - signals it to proceed
+ */
+ (void) sprintf(CL_Logname,"%s%d",
+ SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id);
+ fp = fopen(CL_Logname, "r");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, CL_Logname);
+ return(&result);
+ }
+ if (fscanf(fp,"%d",&sfs_pid) != 1)
+ return (&result);
+ if ((int) generic_kill(sfs_pid, SIGUSR1) == 0) {
+ result = 1;
+ (void) sprintf(previous_transaction,
+ sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got PRIME_SIGNAL(SIGUSR1) - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr," Sent SIGUSR1\n");
+ return (&result); /* success */
+ } else
+ return (&result);
+
+ } else if (strcmp(sfs_signal->clnt_type,"PRIME_ALARM") == 0) {
+
+ /*
+ * message from the Prime client (sfs_prime)
+ * send SIGALRM signal to parent sfs process on
+ * client - tell it to wake up and finish execution at this time
+ */
+
+ (void) sprintf(CL_Logname,"%s%d",
+ SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id);
+ fp = fopen(CL_Logname, "r");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, CL_Logname);
+ return (&result);
+ }
+ if (fscanf(fp,"%d",&sfs_pid) != 1)
+ return (&result);
+ if ((int) generic_kill(sfs_pid, SIGALRM) == 0) {
+ result = 1;
+ (void) sprintf(previous_transaction,
+ sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got PRIME_ALARM(SIGALRM) - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr," Sent SIGALRM\n");
+ return (&result); /* success */
+ } else
+ return (&result);
+
+ } else if (strcmp(sfs_signal->clnt_type,"PRIME_STOP") == 0) {
+
+ /*
+ * message from Prime-client
+ * sent SIGINT signal to sfs parent process
+ * to tell it to terminate experiment now
+ */
+ (void) sprintf(CL_Logname,"%s%d",
+ SFS_CLIENT_SYNC_LOG, sfs_signal->clnt_id);
+ fp = fopen(CL_Logname, "r");
+ if (fp == NULL) {
+ (void) fprintf(stderr,"%s: Cannot open %s\n",
+ sfs_Myname, CL_Logname);
+ return (&result);
+ }
+ if (fscanf(fp,"%d",&sfs_pid) != 1)
+ return (&result);
+ if ((int) generic_kill(sfs_pid, SIGINT) == 0) {
+ result = 1;
+ (void) sprintf(previous_transaction,
+ sfs_signal->clnt_transaction);
+ (void) fprintf(stderr,"%s: Got PRIME_STOP(SIGSTOP) - %s\n",
+ sfs_Myname, sfs_signal->clnt_transaction);
+ (void) fprintf(stderr," Sent SIGINT\n");
+ return (&result); /* success */
+ } else
+ return (&result);
+
+ } else
+ return (&result); /* failure */
+
+} /* signal_sync_sfs_1 */
+
+/* ARGSUSED */
+static void
+lad_syncd_cleanup(
+ int sig_id)
+{
+ (void) pmap_unset(SFS_SYNCPROG, SFS_SYNCVERS);
+ (void) fprintf(stderr, "Unregistered sfs_syncd.\n");
+ (void) unlink(SFS_SYNCD_PID);
+ exit(0);
+
+} /* lad_syncd_cleanup */
+
+/* sfs_m_snc.c */
--- /dev/null
+#ifndef lint
+static char sfs_m_xdrSid[] = "@(#)sfs_m_xdr.c 2.1 97/10/23";
+#endif
+
+/*
+ * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+ * All rights reserved.
+ * Standard Performance Evaluation Corporation (SPEC)
+ * 6585 Merchant Place, Suite 100
+ * Warrenton, VA 20187
+ *
+ * This product contains benchmarks acquired from several sources who
+ * understand and agree with SPEC's goal of creating fair and objective
+ * benchmarks to measure computer performance.
+ *
+ * This copyright notice is placed here only to protect SPEC in the
+ * event the source is misused in any manner that is contrary to the
+ * spirit, the goals and the intent of SPEC.
+ *
+ * The source code is provided to the user or company under the license
+ * agreement for the SPEC Benchmark Suite for this product.
+ */
+
+/*****************************************************************
+ * *
+ * Copyright 1991,1992 Legato Systems, Inc. *
+ * Copyright 1991,1992 Auspex Systems, Inc. *
+ * Copyright 1991,1992 Data General Corporation *
+ * Copyright 1991,1992 Digital Equipment Corporation *
+ * Copyright 1991,1992 Interphase Corporation *
+ * Copyright 1991,1992 Sun Microsystems, Inc. *
+ * *
+ *****************************************************************/
+
+/*
+ *.Exported_routines
+ * bool_t xdr_sync_string(XDR *, sync_string *)
+ *
+ *.Revision_history
+ * 28-Nov-91 0.0.13 Teelucksingh
+ * added 'transaction id' field to xdr data
+ * structure and ANSI C features.
+ * 17-Jun-91 0.0.7 Teelucksingh - Creation
+ * Multi-client synchronization rpc xdr
+ * functions.
+ */
+
+
+/*
+ * ------------------------- Include Files -------------------------
+ */
+
+/*
+ * ANSI C headers
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sfs_c_def.h"
+#include "sfs_m_def.h"
+
+/*
+ * --------------- Multi-client Message XDR Routines ---------------
+ */
+
+bool_t
+xdr_sync_string(
+ XDR * xdrs,
+ sync_string * objp)
+{
+ if (!xdr_int(xdrs, (int *) &objp->clnt_id)) {
+ (void) fprintf(stderr, "%s: can't encode client id %d",
+ sfs_Myname, objp->clnt_id);
+ return (FALSE);
+ }
+ if (!xdr_string(xdrs, (char **) &objp->clnt_type,
+ (unsigned int) MAX_STR1_LEN)) {
+ (void) fprintf(stderr, "%s: can't encode client type %s",
+ sfs_Myname, objp->clnt_type);
+ return (FALSE);
+ }
+ if (!xdr_string(xdrs, (char **) &objp->clnt_transaction,
+ (unsigned int) MAX_STR1_LEN)) {
+ (void) fprintf(stderr, "%s: can't encode client transaction %s",
+ sfs_Myname, objp->clnt_transaction);
+ return (FALSE);
+ }
+ if (!xdr_string(xdrs, (char **) &objp->clnt_data,
+ (unsigned int) MAX_STR2_LEN)) {
+ (void) fprintf(stderr, "%s: can't encode client data %s",
+ sfs_Myname, objp->clnt_data);
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+/* sfs_m_xdr.c */
--- /dev/null
+#! /bin/sh
+# @(#)sfs_mcr 2.1 97/10/23
+#
+#
+# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+# All rights reserved.
+# Standard Performance Evaluation Corporation (SPEC)
+# 6585 Merchant Place, Suite 100
+# Warrenton, VA 20187
+# This product contains benchmarks acquired from several sources who
+# understand and agree with SPEC's goal of creating fair and objective
+# benchmarks to measure computer performance.
+#
+# This copyright notice is placed here only to protect SPEC in the
+# event the source is misused in any manner that is contrary to the
+# spirit, the goals and the intent of SPEC.
+#
+# The source code is provided to the user or company under the license
+# agreement for the SPEC Benchmark Suite for this product.
+#
+# *****************************************************************
+# * *
+# * Copyright 1991,1992 Legato Systems, Inc. *
+# * Copyright 1991,1992 Auspex Systems, Inc. *
+# * Copyright 1991,1992 Data General Corporation *
+# * Copyright 1991,1992 Digital Equipment Corporation *
+# * Copyright 1991,1992 Interphase Corporation *
+# * Copyright 1991,1992 Sun Microsystems, Inc. *
+# * *
+# *****************************************************************
+#
+# Usage sfs_mcr <sfs parameter string>
+#
+# Teelucksingh - Creation (6/17/91)
+#
+# This script is remotely started from the Prime-client
+# by sfs_mgr .
+#
+
+#
+# Client pid files
+#
+SFS_PNT_PID="/tmp/sfs_pnt_pid"
+SFS_SYNCD_PID="/tmp/sfs_syncd_pid"
+
+# SFS client log files
+CLIENT_LOG_FILES="/tmp/sfs_CL$CLIENT_NUM \
+ /tmp/sfs_sig$CLIENT_NUM \
+ /tmp/sfs_x$CLIENT_NUM"
+
+if [ "$1" = "cleanup" ]; then
+ #
+ # do cleanup
+ #
+ rm -f $CLIENT_LOG_FILES
+
+ #
+ # clean up any 'old' sfs processes
+ #
+ if [ -f $SFS_PNT_PID ]; then
+ kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1
+ rm -f $SFS_PNT_PID
+ fi
+ if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+ fi
+
+ exit
+fi
+
+# read command line arguments
+SFS_PROG=$1
+shift
+SFS_DIR=$1
+shift
+S_LOGFILE=$1
+shift
+C_LOGFILE=$1
+shift
+CLIENT_NUM=$1
+shift
+
+# print start message
+echo "========================================================================" >> $C_LOGFILE
+echo "" >> $C_LOGFILE
+
+#
+# decide whether to use BSD (which one) or SYSV variant of commands
+#
+# do test to see whether to use hostname or uname
+sh -c "hostname > /dev/null 2>&1" > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+ HOSTNAME_CMD="hostname"
+else
+ sh -c "uname -n > /dev/null 2>&1" > /dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ HOSTNAME_CMD="uname -n"
+ else
+ echo "sfs_mcr: can't use hostname(1) or uname(1), exiting." >> $C_LOGFILE
+ exit 1
+ fi
+fi
+
+#
+# trap for signals used by sfs
+#
+# Try to find cpp in the common places, if not there then let PATH find it
+if [ "$CPP" = "" ]
+then
+ if [ -f /lib/cpp ]
+ then
+ CPP=/lib/cpp
+ elif [ -f /usr/ccs/lib/cpp ]
+ then
+ CPP=/usr/ccs/lib/cpp
+ else
+ CPP=cpp
+ fi
+fi
+
+#
+# Allow trap numbers to be defined externally for broken systems
+#
+if [ "$TRAP_NUMS" = "" ]
+then
+ echo "#include <signal.h>" > /tmp/sfs_sig$CLIENT_NUM
+ echo "myprint SIGINT SIGALRM SIGTERM SIGUSR1 SIGUSR2" >> \
+ /tmp/sfs_sig$CLIENT_NUM
+ cat /tmp/sfs_sig$CLIENT_NUM | $CPP | grep myprint | \
+ awk '{print $2 " " $3 " " $4 " " $5 " " $6}' > /tmp/sfs_x$CLIENT_NUM
+ TRAP_NUMS=`cat /tmp/sfs_x$CLIENT_NUM`
+fi
+trap "" $TRAP_NUMS
+
+#
+# start the sync daemon on the client
+#
+# Let's truncate the syncd log file at the start of each invocation
+# of sfs_mcr. Else it grows unbounded.
+#
+trap "" $TRAP_NUMS
+$SFS_DIR/sfs_syncd > $S_LOGFILE 2>&1 &
+echo "Started: sfs_syncd on client (`$HOSTNAME_CMD`). " >> $C_LOGFILE
+
+#
+# start SFS
+trap "" $TRAP_NUMS
+echo "Starting: $SFS_DIR/$SFS_PROG -N $CLIENT_NUM $*" >> $C_LOGFILE
+
+$SFS_DIR/$SFS_PROG -N $CLIENT_NUM $* >> $C_LOGFILE 2>&1
+
+if [ $? -ne 0 ]; then # error condition
+ # clean up
+ echo "sfs_mcr: sfs benchmark terminated with error status" >>$C_LOGFILE
+fi
+
+#
+# clean up any 'old' sfs processes
+#
+if [ -f $SFS_PNT_PID ]; then
+ kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1
+ rm -f $SFS_PNT_PID
+fi
+if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+fi
+
+trap $TRAP_NUMS
--- /dev/null
+#! /bin/sh
+# @(#)sfs_mgr 2.1 97/10/23
+#
+# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+# All rights reserved.
+# Standard Performance Evaluation Corporation (SPEC)
+# 6585 Merchant Palce, Suite 100
+# Warrenton, VA 20187
+# This product contains benchmarks acquired from several sources who
+# understand and agree with SPEC's goal of creating fair and objective
+# benchmarks to measure computer performance.
+#
+# This copyright notice is placed here only to protect SPEC in the
+# event the source is misused in any manner that is contrary to the
+# spirit, the goals and the intent of SPEC.
+#
+# The source code is provided to the user or company under the license
+# agreement for the SPEC Benchmark Suite for this product.
+#
+# *****************************************************************
+# * *
+# * Copyright 1991,1992 Legato Systems, Inc. *
+# * Copyright 1991,1992 Auspex Systems, Inc. *
+# * Copyright 1991,1992 Data General Corporation *
+# * Copyright 1991,1992 Digital Equipment Corporation *
+# * Copyright 1991,1992 Interphase Corporation *
+# * Copyright 1991,1992 Sun Microsystems, Inc. *
+# * *
+# *****************************************************************
+#
+# Usage sfs_mgr [-r <rc file>] [-s <suffix>] [-v <level>]
+#
+# Teelucksingh - Creation (6/17/91)
+#
+# Starts SFS (sfs_mcr) on clients with parameters
+# specified in sfs_rc .
+# Starts Prime-client program (sfs_prime)
+# Can have multiple runs with incrementing load
+# Summarized result(s) placed in sfsres.<suffix>
+# Log of multi-client run placed in sfslog.<suffix>
+# Individual client result(s) placed in sfs<cnnn>.<suffix>
+#
+#
+
+# --------------- defined constants and strings --------------
+#
+
+STARline='************************************************************************'
+RHOSTSCKMSG1="Ensure permissions in .rhosts or hosts.equiv allows remote operation."
+RHOSTSCKMSG2="Or check target directory/file existence or permissions."
+USAGE="usage: $0 [-r <rc file>] [-s <suffix>] [-v <level>]"
+
+# ----------------- variable initialization ------------------
+#
+
+error=FALSE
+
+# --------------- program initialization phase ---------------
+#
+# get the command line arguments
+#
+# init with default
+#
+SUFFIX=out
+RC_FILE=./sfs_rc
+VALIDATE=0
+#
+if [ $# -gt 6 ]
+then
+ echo $USAGE
+ exit 1
+fi
+while [ $# -gt 0 ]
+do
+ if [ "$#" -lt 2 ]
+ then
+ echo $USAGE
+ exit 1
+ fi
+ case $1 in
+ -r)
+ RC_FILE=$2
+ ;;
+ -s)
+ SUFFIX=$2
+ ;;
+ -v)
+ VALID_LEVEL=$2
+ VALIDATE=1
+ ;;
+ *) echo $USAGE
+ exit 1
+ esac
+ shift; shift
+done
+
+#
+# pass in environment variables from sfs_rc
+#
+if [ ! -r "$RC_FILE" ]; then
+ echo "sfs_mgr: missing or protected rc file $RC_FILE"
+ exit 1
+fi
+. "$RC_FILE"
+
+#
+# Make sure WORK_DIR is defined
+#
+if [ "$WORK_DIR" = "" ]
+then
+ echo "sfs_mgr: WORK_DIR not defined, check sfs_rc file, exiting."
+ exit 1
+fi
+
+#
+# Prime client output files
+#
+P_OUTFILE=$WORK_DIR/sfsres.$SUFFIX
+P_SUMFILE=$WORK_DIR/sfssum.$SUFFIX
+P_LOGFILE=$WORK_DIR/sfslog.$SUFFIX
+P_VALFILE=$WORK_DIR/sfsval.$SUFFIX
+
+#
+# Client pid files
+#
+SFS_PNT_PID="/tmp/sfs_pnt_pid"
+SFS_PRM_PID="/tmp/sfs_prm_pid"
+SFS_SYNCD_PID="/tmp/sfs_syncd_pid"
+
+#
+# --------------------
+# Setup machine/OS dependant parameters
+#
+# decide whether to use BSD (which one) or SYSV variant of commands
+#
+# do echo test to get no end-of-line character
+#
+op=`echo "\c"`
+if [ "$op" = "\c" ]; then
+ ECHO_NONL="echo -n"
+ NONL=
+else
+ ECHO_NONL="echo"
+ NONL="\c"
+fi
+
+#
+# do test to see whether to use hostname or uname
+#
+if sh -c "hostname > /dev/null 2>&1" > /dev/null 2>&1
+then
+ HOSTNAME_VAL=`hostname`
+elif sh -c "uname -n > /dev/null 2>&1" > /dev/null 2>&1
+then
+ HOSTNAME_VAL=`uname -n`
+else
+ echo "sfs_mgr: can't use hostname(1) or uname(1), exiting."
+ echo "sfs_mgr: can't use hostname(1) or uname(1), exiting." \
+ >> $P_LOGFILE
+ exit 1
+fi
+
+#
+# Make sure RSH is defined, if not set reasonable default
+# RSH_CMD overrides RSH if set
+#
+if [ "$RSH_CMD" != "" ]
+then
+ RSH=$RSH_CMD
+fi
+
+if [ "$RSH" = "" ]
+then
+ RSH="rsh"
+fi
+
+#
+# If CPP is not already defined then
+# try to find cpp in the common places, if not there then let PATH find it
+#
+if [ "$CPP" = "" ]; then
+ if [ -f /lib/cpp ]
+ then
+ CPP=/lib/cpp
+ elif [ -f /usr/ccs/lib/cpp ]
+ then
+ CPP=/usr/ccs/lib/cpp
+ else
+ CPP=cpp
+ fi
+fi
+
+#
+# trap for signals used by sfs programs
+#
+if [ "$TRAP_NUMS" = "" ]
+then
+ echo "#include <signal.h>" > /tmp/sfs_tmp1
+ echo "myprint SIGINT SIGALRM SIGTERM SIGUSR1 SIGUSR2" >> /tmp/sfs_tmp1
+ cat /tmp/sfs_tmp1 | $CPP | grep myprint | \
+ awk '{print $2 " " $3 " " $4 " " $5 " " $6}' > /tmp/sfs_tmp2
+ TRAP_NUMS=`cat /tmp/sfs_tmp2`
+fi
+rm -f /tmp/sfs_tmp1 /tmp/sfs_tmp2
+#
+# --------------------
+
+#
+# Get NFS version number
+#
+SFS_PROG="sfs"
+if [ "$NFS_VERSION" != "" ]
+then
+ if [ "$NFS_VERSION" = "3" ]
+ then
+ SFS_PROG="sfs3"
+ elif [ "$NFS_VERSION" != "2" ]
+ then
+ echo "sfs_mgr: Illegal NFS version number: $NFS_VERSION" \
+ >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: Illegal NFS version number: $NFS_VERSION"
+ exit 1
+ fi
+fi
+
+#
+# print logfile header information
+#
+echo '========================================================================'\
+ >>$P_LOGFILE
+echo " " >>$P_LOGFILE
+echo "SFS NFS Benchmark Prime Client Logfile." >>$P_LOGFILE
+echo " Creation Date: `date`" >>$P_LOGFILE
+echo " Prime Client hostname: $HOSTNAME_VAL" >>$P_LOGFILE
+
+#
+# check for mixfile and block-size file
+# if specified
+#
+# check for mixfile
+#
+if [ "$MIXFILE" != "" -a ! -r "$WORK_DIR/$MIXFILE" ]
+then
+ echo "sfs_mgr: error missing or protected mixfile $WORK_DIR/$MIXFILE" \
+ >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: error missing or protected mixfile $WORK_DIR/$MIXFILE"
+ exit 1
+fi
+
+#
+# check for block size file
+#
+if [ "$BLOCK_FILE" != "" -a ! -r "$WORK_DIR/$BLOCK_FILE" ]
+then
+ echo "sfs_mgr: error missing or protected block size file \
+ $WORK_DIR/$BLOCK_FILE" >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: error missing or protected block size file \
+ $WORK_DIR/$BLOCK_FILE"
+ exit 1
+fi
+
+#
+#
+NUM_CLIENTS=0
+PRIME_CLIENT_NUM=0
+for i in $CLIENTS; do
+ NUM_CLIENTS=`expr $NUM_CLIENTS + 1`
+ #
+ # hack: First try a simple remote "echo" to
+ # /dev/null. If the $RSH fails, then we don't have
+ # permission to execute the remote command sfs_mcr.
+ # The initial probe is necessary because we must
+ # background the sfs_mcr rsh because we're looping
+ # on all clients, and spawn a bunch, and the rsh won't
+ # detach from the command. So, the probe.
+ #
+ $RSH $i -l $SFS_USER "echo >/dev/null" >/dev/null 2>&1 </dev/null
+ if [ $? -ne 0 ]; then
+ echo "sfs_mgr: test rsh to $i failed"
+ echo " $RHOSTSCKMSG1"
+ echo "sfs_mgr: test rsh to $i failed" >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ exit 1
+ fi
+
+ # Get canonical hostname of client $i and see if it is the prime client.
+ client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null`
+ if [ "$client_name" = $HOSTNAME_VAL ]
+ then
+ PRIME_CLIENT_NUM=$NUM_CLIENTS
+ continue
+ fi
+ #
+ # Also check to make sure the work directory exists
+ #
+ exists=`$RSH $i -l $SFS_USER sh -c \"if [ -d $WORK_DIR ]\; then echo 0 \; else echo 1 \; fi\" </dev/null`
+ if [ "$exists" != 0 ]; then
+ echo "sfs_mgr: $WORK_DIR on $i does not exist"
+ echo "sfs_mgr: $WORK_DIR on $i does not exist" >> $P_LOGFILE
+ exit 1
+ fi
+ #
+ # propagate the mixfile to remote clients
+ #
+ if [ "$MIXFILE" != "" ]; then
+ rcp "$WORK_DIR/$MIXFILE" \
+ "$SFS_USER"@"$i":"$WORK_DIR/$MIXFILE" >> $P_LOGFILE 2>&1
+ if [ $? -ne 0 ]; then
+ echo \
+ "sfs_mgr: can't rcp mix file $WORK_DIR/$MIXFILE to client $i."
+ echo " $RHOSTSCKMSG1"
+ echo " $RHOSTSCKMSG2"
+ echo \
+ "sfs_mgr: can't rcp mix file $WORK_DIR/$MIXFILE to client $i." \
+ >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ echo " $RHOSTSCKMSG2" >> $P_LOGFILE
+ exit 1
+ fi
+ fi
+ #
+ # propagate block size file to remote clients
+ #
+ if [ "$BLOCK_FILE" != "" ]; then
+ rcp "$WORK_DIR/$BLOCK_FILE" \
+ "$SFS_USER"@"$i":"$WORK_DIR/$BLOCK_FILE" >> $P_LOGFILE 2>&1
+ if [ $? -ne 0 ]; then
+ echo \
+ "sfs_mgr: can't rcp block size file $WORK_DIR/$BLOCK_FILE to client $i."
+ echo " $RHOSTSCKMSG1"
+ echo " $RHOSTSCKMSG2"
+ echo \
+ "sfs_mgr: can't rcp block size file $WORK_DIR/$BLOCK_FILE to client $i." \
+ >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ echo " $RHOSTSCKMSG2" >> $P_LOGFILE
+ exit 1
+ fi
+ fi
+done
+
+if [ "$NUM_CLIENTS" -eq 0 ]; then
+ echo "Cannot run SFS with no clients."
+ echo "Assign value to CLIENT variable in sfs_rc."
+ echo "Cannot run SFS with no clients." >> $P_LOGFILE
+ echo "Assign value to CLIENT variable in sfs_rc." >> $P_LOGFILE
+ exit 1
+fi
+
+
+echo " Number of Clients: $NUM_CLIENTS" >>$P_LOGFILE
+echo " Client hostname(s): $CLIENTS" >>$P_LOGFILE
+echo " " >>$P_LOGFILE
+
+#
+# Loop invariant setup
+# -------------------
+#
+# check for program that starts external monitoring
+#
+if [ "$PRIME_MON_SCRIPT" != "" -a ! -x "$WORK_DIR/$PRIME_MON_SCRIPT" ]
+then
+ echo "sfs_mgr: error missing or not executeable program \
+ $WORK_DIR/$PRIME_MON_SCRIPT" >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: error missing or not executeable program \
+ $WORK_DIR/$PRIME_MON_SCRIPT"
+ exit 1
+fi
+
+#
+# Set default number of procs if missing
+#
+PRCS=$PROCS
+if [ "$PRCS" = "" ]; then
+ PRCS=4
+fi
+
+if [ "$MNT_POINTS" = "" ]; then
+ echo "sfs_mgr: MNT_POINTS not specified" >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: MNT_POINTS not specified"
+ exit 1
+fi
+
+set `echo $MNT_POINTS`
+NUM_MNTS=$#
+MPC=`expr $NUM_CLIENTS \* $PRCS`
+if [ $NUM_MNTS -ne 1 -a $NUM_MNTS -ne $PRCS -a $NUM_MNTS -ne $MPC ]; then
+ ESTR=""
+ if [ $PROCS -ne $MPC ]; then
+ ESTR="or $MPC"
+ fi
+ echo "sfs_mgr: incorrect number of MNT_POINTS ($NUM_MNTS) must be $PROCS $ESTR" >> $P_LOGFILE 2>&1
+ echo "sfs_mgr: incorrect number of MNT_POINTS ($NUM_MNTS) must be $PROCS $ESTR"
+ exit 1
+fi
+
+#
+# -----------------
+#
+trap "" $TRAP_NUMS
+
+#
+# clean up any 'old' sfs processes
+#
+if [ -f $SFS_PRM_PID ]; then
+ kill -2 `cat $SFS_PRM_PID` > /dev/null 2>&1
+ rm -f $SFS_PRM_PID
+fi
+if [ -f $SFS_PNT_PID ]; then
+ kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1
+ rm -f $SFS_PNT_PID
+fi
+if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+fi
+
+#
+# Prime Client sfs_syncd logfile
+#
+S_LOGFILE=$WORK_DIR/syncd_$PRIME_CLIENT_NUM.log
+
+#
+# Determine the number of test runs (TOTAL_RUNS)
+# from user supplied values in sfs_rc
+#
+NUM_LOADS=0
+LOAD_ARRAY=""
+DEFAULT_LOAD=60
+#
+# get the number of LOAD elements (NUM_LOADS)
+#
+for i in $LOAD; do
+ NUM_LOADS=`expr $NUM_LOADS + 1`
+done
+#
+# if NUM_LOADS > 1 then the number of test runs (TOTAL_RUNS) = NUM_LOADS and
+# Report conflict if user specifies multiple LOAD elements as well as
+# NUM_RUNS > 1.
+#
+# if NUM_LOADS <= 1 then the number of test runs (TOTAL_RUNS) = NUM_RUNS
+#
+if [ "$NUM_LOADS" -gt 1 ]; then
+ TOTAL_RUNS=$NUM_LOADS
+ LOAD_ARRAY=$LOAD
+ if [ "$NUM_RUNS" -gt 1 ]; then
+ echo "Cannot specify an array of LOAD values as well as NUM_RUNS >1."
+ echo "Cannot specify an array of LOAD values as well as NUM_RUNS >1." \
+ >> $P_LOGFILE 2>&1
+ exit 1
+ fi
+else
+ TOTAL_RUNS=$NUM_RUNS
+ if [ "$NUM_LOADS" -eq 0 ]; then
+ LOAD=$DEFAULT_LOAD
+ fi
+ LOAD_ARRAY=$LOAD
+ i=1
+ while [ "$i" -lt "$NUM_RUNS" ]; do
+ LOAD_ARRAY="$LOAD_ARRAY `expr $LOAD + $i \* $INCR_LOAD`"
+ i=`expr $i + 1`
+ done
+fi
+
+#
+# Loop invariant parameters
+# create parameter strings here ... from sfs_rc values.
+# - SFS_PARAM : sfs parameters
+# - SFS_VPARAM : sfs validation parameters
+# - SFS_PRIME_PARAM : sfs_prime parameters
+SFS_PARAM=
+SFS_VPARAM=
+SFS_PRIME_PARAM=
+#
+# get runtime
+#
+if [ "$RUNTIME" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -t $RUNTIME"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -t $RUNTIME"
+fi
+
+#
+# get mixfile filename, if specified
+#
+if [ "$MIXFILE" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -m $WORK_DIR/$MIXFILE"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -m $WORK_DIR/$MIXFILE"
+fi
+
+#
+# get sfs DEBUG level
+#
+if [ "$DEBUG" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -d $DEBUG"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -d $DEBUG"
+fi
+
+#
+# get access percentage
+#
+if [ "$ACCESS_PCNT" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -a $ACCESS_PCNT"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -a $ACCESS_PCNT"
+fi
+
+#
+# get append percentage
+#
+if [ "$APPEND_PCNT" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -A $APPEND_PCNT"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -A $APPEND_PCNT"
+fi
+
+#
+# get block size
+#
+if [ "$BLOCK_SIZE" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -B $BLOCK_SIZE"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -B $BLOCK_SIZE"
+fi
+
+#
+# get block size filename, if specified
+#
+if [ "$BLOCK_FILE" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -b $WORK_DIR/$BLOCK_FILE"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -b $WORK_DIR/$BLOCK_FILE"
+fi
+
+#
+# get maximum number of outstanding biod reads
+#
+if [ "$BIOD_MAX_READS" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -R $BIOD_MAX_READS"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -R $BIOD_MAX_READS"
+fi
+
+#
+# get maximum number of outstanding biod writes
+#
+if [ "$BIOD_MAX_WRITES" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -W $BIOD_MAX_WRITES"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -W $BIOD_MAX_WRITES"
+fi
+
+#
+# get directory count
+#
+if [ "$DIR_COUNT" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -D $DIR_COUNT"
+fi
+
+#
+# get file count
+#
+if [ -n "$FILE_COUNT" ]; then
+ if [ "$FILE_COUNT" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -F $FILE_COUNT"
+ fi
+fi
+
+#
+# get symbolic link count
+#
+if [ "$SYMLINK_COUNT" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -S $SYMLINK_COUNT"
+fi
+
+#
+# set flag for raw data dump if option set
+#
+if [ "$DUMP" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -z"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -z"
+fi
+
+#
+# set flag for NFS/TCP if variable is "1" or "on"
+#
+if [ "$TCP" != "" ]
+then
+ if [ "$TCP" = "1" -o "$TCP" = "on" ]; then
+ SFS_PARAM="$SFS_PARAM -Q"
+ SFS_VPARAM="$SFS_VPARAM -Q"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -Q"
+ fi
+fi
+
+#
+# get number of processes
+#
+if [ "$PROCS" -ne 0 ]; then
+ SFS_PARAM="$SFS_PARAM -p $PROCS"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -p $PROCS"
+fi
+
+#
+# get warm-up value (allow 0 warmup)
+#
+if [ "$WARMUP_TIME" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -w $WARMUP_TIME"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -w $WARMUP_TIME"
+fi
+
+#
+# get sfs_prime sleep value
+#
+if [ "${PRIME_SLEEP:-0}" -gt 0 ]; then
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -s $PRIME_SLEEP"
+fi
+
+#
+# get file set percentage delta
+#
+if [ "$FILESET_DELTA" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -f $FILESET_DELTA"
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -f $FILESET_DELTA"
+fi
+
+#
+# get sfs_prime timeout value
+#
+if [ "${PRIME_TIMEOUT:-0}" -gt 0 ]; then
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -x $PRIME_TIMEOUT"
+fi
+
+#
+# get populate only flag
+#
+if [ "$POPULATE" != "" ]; then
+ SFS_PARAM="$SFS_PARAM -P"
+fi
+
+#
+# check for program that starts external monitoring
+#
+if [ "$PRIME_MON_SCRIPT" != "" ]; then
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -k $WORK_DIR/$PRIME_MON_SCRIPT"
+ # check for parameters to the monitor program; use a different method
+ # to test for nonempty because the arguments may start with a hyphen,
+ # which would confuse the "test" command.
+ if [ "x$PRIME_MON_ARGS" != "x" ]; then
+ SFS_PRIME_PARAM="$SFS_PRIME_PARAM -K '$PRIME_MON_ARGS'"
+ fi
+
+ echo "" >> $P_LOGFILE
+fi
+
+#
+# Add clients to prime
+#
+SFS_PRIME_PARAM="$SFS_PRIME_PARAM $CLIENTS"
+
+#
+# get prime client hostname
+#
+SFS_PARAM="$SFS_PARAM -M $HOSTNAME_VAL"
+
+#### End client loop invariant section
+#
+# VALIDATE stuff
+#
+if [ "$VALIDATE" -gt 0 ]; then
+ echo "Executing SFS NFS Validation ..."
+ #
+ # if validate option used then take the first client
+ # from the CLIENT array and run the SFS validation
+ # suite using the first element on the MOUNT_PNTS list.
+
+ set `echo $CLIENTS`
+ VALID_CLIENT=$1
+
+ set `echo $MNT_POINTS`
+ VALID_MOUNT_PNT=$1
+
+ if [ $NUM_MNTS -eq 1 -a -f "$WORK_DIR/$VALID_MOUNT_PNT" ]
+ then
+ #
+ # If the mount point and is actually a file
+ # name then we assume that it is a file containing a list
+ # of mount points one line per client, possibly of the format
+ # hostname:path
+ #
+ MNT_PTS=`while read CLNT_LINE MNT_LINE
+ do
+ if [ $VALID_CLIENT = $CLNT_LINE ]
+ then
+ echo $MNT_LINE
+ break
+ fi
+ done < $WORK_DIR/$VALID_MOUNT_PNT`
+
+ set `echo $MNT_PTS`
+ VALID_MOUNT_PNT=$1
+ fi
+
+ echo "Starting SFS NFS validation on client ($VALID_CLIENT)"
+ echo " $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT"
+ echo "Starting SFS Validation suite on client ($VALID_CLIENT)" \
+ > $P_VALFILE 2>&1
+ echo " $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT" \
+ >> $P_VALFILE 2>&1
+
+ # Get canonical hostname of $VALID_CLIENT and see if it is the prime client.
+ client_name=`$RSH $VALID_CLIENT -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null`
+ if [ "$client_name" = $HOSTNAME_VAL ]; then
+ $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT \
+ >> $P_VALFILE 2>&1
+ # if error then clean-up and exit
+ if [ $? -ne 0 ]; then
+ echo "SFS NFS validation failed."
+ echo "See $P_VALFILE for results."
+ exit 1
+ else
+ echo "SFS NFS validation completed successfully."
+ echo "See $P_VALFILE for results."
+ exit 0
+ fi
+ else
+ $RSH $VALID_CLIENT -l $SFS_USER \
+ "( cd $WORK_DIR; \
+ $SFS_DIR/$SFS_PROG $SFS_VPARAM -V $VALID_LEVEL $VALID_MOUNT_PNT )" \
+ >> $P_VALFILE 2>&1
+ if [ $? -ne 0 ]; then
+ echo \
+ "sfs_mgr: can't run validation pass of sfs on client $VALID_CLIENT."
+ echo " $RHOSTSCKMSG1"
+ echo \
+ "sfs_mgr: can't run validation pass of sfs on client $VALID_CLIENT." \
+ >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ exit 1
+ fi
+ # if error then clean-up and exit
+ tail -1 $P_VALFILE | grep -s 'validation completed successfully'
+ if [ $? -ne 0 ]; then
+ echo "SFS NFS validation failed."
+ echo "See $P_VALFILE for results."
+ echo "SFS NFS validation failed." >> $P_LOGFILE
+ echo "See $P_VALFILE for results." >> $P_LOGFILE
+ exit 1
+ else
+ echo "SFS NFS validation completed successfully."
+ echo "See $P_VALFILE for results."
+ echo "SFS NFS validation completed successfully." >> $P_LOGFILE
+ echo "See $P_VALFILE for results." >> $P_LOGFILE
+ exit 0
+ fi
+ fi
+fi
+
+
+#
+# Prime client /tmp logfiles - used for clean up
+#
+PRIME_LOG_FILES="/tmp/sfs_PC_sync \
+ /tmp/sfs_x$PRIME_CLIENT_NUM \
+ /tmp/sfs_CL$PRIME_CLIENT_NUM \
+ /tmp/sfs_mpr$PRIME_CLIENT_NUM \
+ /tmp/sfs_res*"
+
+#
+# start test
+# MAIN CLIENT LOOP
+#
+RUN=1
+for LOAD_INDEX in $LOAD_ARRAY; do
+ LOAD_VALUE=`expr $LOAD_INDEX / $NUM_CLIENTS`
+
+ export LOAD_VALUE LOAD_INDEX SUFFIX WORK_DIR
+
+ echo " ">>$P_LOGFILE
+ echo "$STARline" >> $P_LOGFILE
+ echo "$STARline" >> $P_OUTFILE
+
+ #
+ # clean up /tmp files
+ #
+ for i in $PRIME_LOG_FILES; do
+ if [ -f $i ]; then
+ if [ -w $i ]; then
+ rm $i
+ else
+ echo "sfs_mgr: error could not remove file - $i"
+ echo "sfs_mgr: error could not remove file - $i" >> $P_LOGFILE
+ exit 1
+ fi
+ fi
+ done
+ #
+ # restart the sfs_syncd process
+ #
+ if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+ fi
+
+ trap "" $TRAP_NUMS
+
+ echo "Test Run $RUN of $TOTAL_RUNS" >>$P_LOGFILE
+ echo " " >>$P_LOGFILE
+ echo " `date`"
+ $ECHO_NONL " Executing run $RUN of $TOTAL_RUNS ... $NONL"
+ sh -c "$SFS_DIR/sfs_syncd >> $S_LOGFILE 2>&1 &"
+ sleep 15
+ echo "Started: sfs_syncd on Prime-Client ($HOSTNAME_VAL)" \
+ >> $P_LOGFILE
+
+ #
+ # start sfs on all the clients
+ #
+ CLIENTS_NUM=1
+ for i in $CLIENTS; do
+ #
+ # compose client's logfile name
+ #
+ if [ "$CLIENTS_NUM" -lt 10 ]; then
+ C_LOGFILE="$WORK_DIR"/sfsc00"$CLIENTS_NUM"."$SUFFIX"
+ elif [ "$CLIENTS_NUM" -lt 100 ]; then
+ C_LOGFILE="$WORK_DIR"/sfsc0"$CLIENTS_NUM"."$SUFFIX"
+ else
+ C_LOGFILE="$WORK_DIR"/sfsc"$CLIENTS_NUM"."$SUFFIX"
+ fi
+
+ #
+ # compose client's sfs_syncd logfile name
+ #
+ S_LOGFILE=$WORK_DIR/syncd_$CLIENTS_NUM.log
+
+ if [ $NUM_MNTS -eq 1 -a -f "$WORK_DIR/$MNT_POINTS" ]
+ then
+ #
+ # If there is only one mount point and it is actually a file
+ # name then we assume that it is a file containing a list
+ # of mount points one line per client, possibly of the format
+ # hostname:path
+ #
+ MNT_PTS=`while read CLNT_LINE MNT_LINE
+ do
+ if [ $i = $CLNT_LINE ]
+ then
+ echo $MNT_LINE
+ break
+ fi
+ done < $WORK_DIR/$MNT_POINTS`
+ else
+ #
+ # construct MNT_PTS for this particular CLIENTS_NUM (client)
+ # from MNT_POINTS, using total number of CLIENTS
+ # and PRCS [number of processes per client]
+ #
+ # PRCS must be a multiple of NUM_MNTS,
+ # no need to resequence the list of mount points
+ #
+ MNT_PTS="$MNT_POINTS"
+ fi
+
+ #
+ # if prime client in $CLIENT then start sfs locally
+ #
+ client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null`
+ if [ "$client_name" = $HOSTNAME_VAL ]; then
+ echo "`date` $i start:" >>$P_LOGFILE
+ echo " $SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS" \
+ >> $P_LOGFILE
+
+ trap "" $TRAP_NUMS
+ $SFS_DIR/$SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS \
+ >> $C_LOGFILE 2>&1 &
+ else
+ #
+ # Cause remote client to cleanup
+ #
+ $RSH $i -l $SFS_USER "( cd $WORK_DIR; \
+ $SFS_DIR/sfs_mcr cleanup )" >>/dev/null 2>&1 </dev/null
+
+ #
+ # remotely start sfs_mcr script on clients
+ #
+ echo "`date` $i start:" >>$P_LOGFILE
+ echo " $SFS_PROG -N $CLIENTS_NUM -l $LOAD_VALUE $SFS_PARAM $MNT_PTS" \
+ >> $P_LOGFILE
+
+ $RSH $i -l $SFS_USER "( cd $WORK_DIR; \
+ $SFS_DIR/sfs_mcr $SFS_PROG $SFS_DIR \
+ $S_LOGFILE $C_LOGFILE $CLIENTS_NUM \
+ -l $LOAD_VALUE $SFS_PARAM $MNT_PTS ) &"\
+ >>/dev/null 2>&1 </dev/null &
+ fi
+
+ #
+ # increment client num
+ #
+ CLIENTS_NUM=`expr $CLIENTS_NUM + 1`
+ done
+
+ #
+ # start the Prime client program, sfs_prime,
+ # and wait for completion
+ #
+ trap "" $TRAP_NUMS
+ $SFS_DIR/sfs_prime -l $LOAD_VALUE -C $P_SUMFILE $SFS_PRIME_PARAM \
+ > /tmp/sfs_mpr$PRIME_CLIENT_NUM 2>> $P_LOGFILE
+
+ #
+ # if error then clean-up set error flag, and break out
+ #
+ if [ $? -ne 0 ]; then
+ echo "sfs_mgr: sfs_prime returned an error, exiting"
+ echo "sfs_mgr: sfs_prime returned an error, exiting" \
+ >> $P_LOGFILE 2>&1
+ if [ -f $SFS_PNT_PID ]; then
+ kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1
+ rm -f $SFS_PNT_PID
+ fi
+ if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+ fi
+ error=TRUE
+ break # break out of for loop
+ fi
+
+ #
+ # record results
+ #
+ cat /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_LOGFILE
+ cat /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_OUTFILE
+ rm /tmp/sfs_mpr$PRIME_CLIENT_NUM >> $P_LOGFILE 2>&1
+
+ #
+ # increment RUN value and reset SFS_PARAM
+ #
+ RUN=`expr $RUN + 1`
+ echo " done"
+ #
+ echo "" >> $P_LOGFILE
+ echo "$STARline" >> $P_LOGFILE
+ echo "$STARline" >> $P_OUTFILE
+
+ #
+ # test run(s) completed
+ #
+done # END OF MAIN CLIENT LOOP: 'for LOAD_INDEX in $LOAD_ARRAY'
+
+#
+# copy log files from clients
+#
+CLIENTS_NUM=1
+for i in $CLIENTS; do
+ #
+ # compose client's logfile name
+ #
+ if [ "$CLIENTS_NUM" -lt 10 ]; then
+ C_LOGFILE="$WORK_DIR"/sfsc00"$CLIENTS_NUM"."$SUFFIX"
+ elif [ "$CLIENTS_NUM" -lt 100 ]; then
+ C_LOGFILE="$WORK_DIR"/sfsc0"$CLIENTS_NUM"."$SUFFIX"
+ else
+ C_LOGFILE="$WORK_DIR"/sfsc"$CLIENTS_NUM"."$SUFFIX"
+ fi
+ #
+ # Copy over the logfiles. We copy the files to a temporary
+ # file on the chance that either the prime client is also
+ # a load generating client and we might rcp a file over
+ # itself, or the $WORK_DIR is NFS mounted by all clients,
+ # in which case we don't want to really remove the remote
+ # file since it is the same as the 'local' file on the
+ # prime. While not necessarily efficient, this is correct.
+ #
+ client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null`
+ if [ $client_name != $HOSTNAME_VAL ]; then
+ #
+ # copy to temporary file: of different name than target
+ # in off chance /tmp is our $WORK_DIR
+ #
+ rcp "$SFS_USER"@"$i":"$C_LOGFILE" \
+ /tmp/sfs"$CLIENTS_NUM"."$SUFFIX"
+ if [ $? -ne 0 ]; then
+ echo "sfs_mgr: can't rcp $C_LOGFILE from client $i."
+ echo " $RHOSTSCKMSG1"
+ echo " $RHOSTSCKMSG2"
+ echo "sfs_mgr: can't rcp $C_LOGFILE from client $i." >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ echo " $RHOSTSCKMSG2" >> $P_LOGFILE
+ exit 1
+ fi
+ $RSH $i -l $SFS_USER "/bin/rm $C_LOGFILE"
+ if [ $? -ne 0 ]; then
+ echo "sfs_mgr: can't remove $C_LOGFILE on client $i."
+ echo " $RHOSTSCKMSG1"
+ echo "sfs_mgr: can't remove $C_LOGFILE on client $i." >> $P_LOGFILE
+ echo " $RHOSTSCKMSG1" >> $P_LOGFILE
+ exit 1
+ fi
+ mv /tmp/sfs"$CLIENTS_NUM"."$SUFFIX" "$C_LOGFILE"
+ fi
+ CLIENTS_NUM=`expr $CLIENTS_NUM + 1`
+done
+
+#
+# if we got an error, terminate sfs_mgr
+#
+if [ "$error" = TRUE ]; then
+ exit 1
+fi
+
+#
+# copy 'raw data dump' files from clients
+# only do this for one point, the final one--it dosn't make
+# sense to concatenate dumps from different loads
+#
+if [ "$DUMP" != "" ]; then
+ CLIENTS_NUM=1
+ for i in $CLIENTS; do
+ #
+ # compose client's raw dump logfile name
+ #
+ if [ "$CLIENTS_NUM" -lt 10 ]; then
+ CLNTNUM=00"$CLIENTS_NUM"
+ elif [ "$CLIENTS_NUM" -lt 100 ]; then
+ CLNTNUM=0"$CLIENTS_NUM"
+ else
+ CLNTNUM="$CLIENTS_NUM"
+ fi
+ PRC=$PROCS
+ if [ "$PRC" = "" ]; then
+ PRC=4
+ fi
+ PRCJ=0
+ while [ $PRCJ -lt $PRC ]; do
+ if [ "$PRCJ" -lt 10 ]; then
+ PROCNUM=00"$PRCJ"
+ elif [ "$PRCJ" -lt 100 ]; then
+ PROCNUM=0"$PRCJ"
+ else
+ PROCNUM="$PRCJ"
+ fi
+ RAWFILE=c${CLNTNUM}-p${PROCNUM}
+ #
+ # copy over the logfiles
+ # clean out (remove) originals
+ #
+ client_name=`$RSH $i -l $SFS_USER 'hostname || uname -n' 2>/dev/null </dev/null`
+ if [ $client_name != $HOSTNAME_VAL ]; then
+ $RSH $i -l $SFS_USER \
+ cat /tmp/"$RAWFILE" \
+ >> "$WORK_DIR"/"$RAWFILE"."$SUFFIX"
+ $RSH $i -l $SFS_USER \
+ /bin/rm /tmp/"$RAWFILE"
+ else
+ cat /tmp/"$RAWFILE" \
+ >> "$WORK_DIR"/"$RAWFILE"."$SUFFIX"
+ /bin/rm /tmp/"$RAWFILE"
+ fi
+ PRCJ=`expr $PRCJ + 1`
+ done
+ CLIENTS_NUM=`expr $CLIENTS_NUM + 1`
+ done
+fi
+
+#
+# cleanup processes before ending
+#
+if [ -f $SFS_PRM_PID ]; then
+ kill -2 `cat $SFS_PRM_PID` > /dev/null 2>&1
+ rm -f $SFS_PRM_PID
+fi
+if [ -f $SFS_PNT_PID ]; then
+ kill -2 `cat $SFS_PNT_PID` > /dev/null 2>&1
+ rm -f $SFS_PNT_PID
+fi
+if [ -f $SFS_SYNCD_PID ]; then
+ kill -2 `cat $SFS_SYNCD_PID` > /dev/null 2>&1
+ rm -f $SFS_SYNCD_PID
+fi
+
+#
+# cleanup temporary files
+#
+for i in $PRIME_LOG_FILES; do
+ if [ -f $i ]; then
+ if [ -w $i ]; then
+ rm $i
+ else
+ echo "sfs_mgr: error could not remove file - $i"
+ echo "sfs_mgr: error could not remove file - $i" >> $P_LOGFILE
+ exit 1
+ fi
+ fi
+done
+
+echo '========================================================================'\ >>$P_LOGFILE
+exit 0
--- /dev/null
+##############################################################################
+#
+# @(#)sfs_rc 2.1 97/10/23
+#
+# Specify SFS parameters for sfs runs in this file.
+#
+# The following parameters are configurable within the SFS run and
+# reporting rules.
+#
+# See below for details.
+#
+# Example shows a run of 100 to 1000 ops/sec
+#
+LOAD="100"
+INCR_LOAD=100
+NUM_RUNS=10
+PROCS=4
+CLIENTS="client1 client2"
+MNT_POINTS="srvr:/a srvr:/b srvr:/c srvr:/d srvr:/e srvr:/f srvr:/g srvr:/h"
+BIOD_MAX_WRITES=2
+BIOD_MAX_READS=2
+TCP="on"
+NFS_VERSION="3"
+SFS_USER="`id | tr '()' ' ' | awk '{print $2}' `"
+SFS_DIR="`pwd`/bin"
+WORK_DIR="`pwd`/result"
+PRIME_MON_SCRIPT=""
+PRIME_MON_ARGS=""
+RSH="rsh"
+#
+# The following parameters are strictly defined within the SFS
+# run and reporting rules and may not be changed.
+#
+RUNTIME=300
+WARMUP_TIME=300
+MIXFILE=""
+ACCESS_PCNT=10
+APPEND_PCNT=70
+BLOCK_SIZE=8
+BLOCK_FILE=""
+DIR_COUNT=30
+FILE_COUNT=
+SYMLINK_COUNT=20
+#
+# The following parameters are useful for debugging or general system
+# tuning. They may not be used during during a reportable SFS run.
+#
+DEBUG=
+DUMP=
+POPULATE=
+PRIME_SLEEP=0
+PRIME_TIMEOUT=0
+#
+# The default SFS options are implied if no values are assigned to
+# the variables. The variables and their meaning are as follows.
+#
+# The following parameters are configurable within the SFS run and
+# reporting rules.
+#
+# LOAD - array of numbers specifying the NFS loads
+# (NFS calls per second) to be generated by all clients
+# combined. The number of consecutive runs equals the size
+# of the array and the peak load equals the largest value
+# in the array.
+# PROCS - number of SFS sub-processes to generate NFS
+# calls (DEFAULT PROCS = 4).
+# MNT_POINTS - string containing the mount points of NFS-mounted
+# filesystems on the client which will be used in the test.
+# BIOD_MAX_WRITES - maximum number of outstanding async (biod) writes
+# (DEFAULT BIOD_MAX_WRITES = 2).
+# BIOD_MAX_READS - maximum number of outstanding async (biod) reads
+# (DEFAULT BIOD_MAX_READS = 2).
+# TCP - If set ("on") then use NFS/TCP behaviour rather
+# NFS_VERSION - Set the version of the NFS protocol to use
+# (DEFAULT [or unset] NFS_VERSION = 2)
+# NUM_RUNS - number indicating the number of multi-client runs. NUM_RUNS
+# should only be used if the size of the LOAD array <= 1;
+# INCR_LOAD - number indicating the load increment factor in NFS call/sec.
+# The first run generates LOAD calls/sec,
+# subsequent runs are made with LOAD + (N * INCR_LOAD)
+# calls/sec; where initial N = 0. INCR_LOAD should
+# only be used if the size of the LOAD array <= 1.
+# CLIENTS - string containing client's host names; include the Prime-
+# Client's host name if you will be using the Prime-Client to
+# generate NFS loads.
+# SFS_USER - string containing the user account name that has appropriate
+# permission to execute SFS on each of the clients. The
+# user account name used on the Prime-Client should be added
+# to the SFS_USER's .rhosts file on each of the clients.
+# SFS_DIR - string containing the directory path where the SFS
+# executables are stored; should be the same for all systems
+# WORK_DIR - string containing directory path where the SFS output
+# files are stored, this should be the same for all systems.
+# RSH - OS dependent version of remote shell executable ON THE
+# PRIME CLIENT
+# PRIME_MON_SCRIPT - string containing the name of a shell script used to
+# control the start and stop of any external SFS
+# processes like external performance monitors. The sample
+# The sample shell script 'sfs_ext_mon' shows the
+# expected semantics of the program.
+# PRIME_MON_ARGS - string containing optional arguments that are passed
+# to the PRIME_MON_SCRIPT.
+#
+# The following parameters are strictly defined within the SFS
+# run and reporting rules and may not be changed.
+#
+# RUNTIME - number of seconds to generate load
+# (DEFAULT RUNTIME = 300).
+# WARMUP_TIME - number of seconds to warmup
+# (DEFAULT WARMUP_TIME = 300).
+# MIXFILE - string containing the NFS call distribution filename;
+# a copy of $MIXFILE must be placed in the $WORK_DIR
+# directory on the Prime_Client. The Prime-Client will
+# propagate a copy to all the other clients in the test.
+# ACCESS_PCNT - percent of total file set available for use by i/o
+# operations that will be accessed.
+# (DEFAULT ACCESS_PCNT = 10).
+# APPEND_PCNT - percent of writes that append rather than overwrite
+# (DEFAULT APPEND_PCNT = 70).
+# BLOCK_SIZE - number of KB in a block, up to 8 KB
+# (DEFAULT BLOCK_SIZE = 8 ).
+# BLOCK_FILE - string containing the block transfer sizes filename.
+# A copy of $BLOCK_FILE must be placed in the $WORK_DIR
+# directory on the Prime_Client. The Prime-Client will
+# propagate a copy to all the other clients in the test.
+# DIR_COUNT - number of files per directory to use for directory
+# operations (DEFAULT DIR_COUNT = 30).
+# FILE_COUNT - number of files to use for read and write
+# operations. By default, number of files is
+# calculated from the specified LOAD and ACCESS_PCNT.
+# SYMLINK_COUNT - number of symbolic links to use for symlink
+# operations (DEFAULT SYMLINK_COUNT = 20).
+#
+# The following parameters are useful for debugging or general system
+# tuning. They may not be used during during a reportable SFS run.
+#
+# DUMP - If set, dump raw data points at end of run
+# POPULATE - If set ("on") the only populate the file set and do
+# run test.
+#
+# PRIME_SLEEP - number of seconds Prime-Client should sleep after starting
+# and stopping the SFS external monitoring facility and
+# before sending synchronization message to clients. This is a
+# provision to allow sufficient time for starting and stopping
+# other performance monitoring utilities that could be used
+# during SFS execution.
+# PRIME_TIMEOUT - number of seconds Prime-Client should wait for a
+# response from all the clients. Zero indicates the
+# default should be used.
--- /dev/null
+#!/bin/sh
+#
+# @(#)sfs_suchown 2.1 97/10/23
+#
+# Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
+# All rights reserved.
+# Standard Performance Evaluation Corporation (SPEC)
+# 6585 Merchant Place, Suite 100
+# Warrenton, VA 20187
+# This product contains benchmarks acquired from several sources who
+# understand and agree with SPEC's goal of creating fair and objective
+# benchmarks to measure computer performance.
+#
+# This copyright notice is placed here only to protect SPEC in the
+# event the source is misused in any manner that is contrary to the
+# spirit, the goals and the intent of SPEC.
+#
+# The source code is provided to the user or company under the license
+# agreement for the SPEC Benchmark Suite for this product.
+
+#
+SPEC=${SPEC-0}
+
+if [ "$SPEC" != 0 ] ; then
+ rm -f $BENCH/bin/sfs
+ rm -f $BENCH/bin/sfs3
+ rm -f $BENCH/bin/sfs_syncd
+ rm -f $BENCH/bin/sfs_prime
+ rm -f $BENCH/bin/sfs_mgr
+ rm -f $BENCH/bin/sfs_mcr
+ rm -f $BENCH/bin/sfs_ext_mon
+fi
+
+#
+# If first argument is clobber, just remove executeables
+#
+if [ "$1" = "clobber" ]
+then
+ exit 0
+fi
+
+#
+# if first argument == -DRESVPORT then we must make executeable
+# setuid to root.
+#
+if [ "$1" = "-DRESVPORT" ]
+then
+ shift
+ echo "Setting root ownership on sfs setuid executable in order to"
+ echo "perform binding to privileged port. You may be asked to enter"
+ echo "the root password."
+ #
+ su $ROOTUSER <<EOF
+ chown root $*
+ chmod 04755 $*
+EOF
+fi
+
+#
+# if the SPEC tools are configured then move binaries to RESULTSDIR
+#
+if [ "$SPEC" != 0 ] ; then
+ mv sfs $BENCH/bin
+ mv sfs3 $BENCH/bin
+ mv sfs_syncd $BENCH/bin
+ mv sfs_prime $BENCH/bin
+ cp sfs_mgr $BENCH/bin
+ cp sfs_mcr $BENCH/bin
+ cp sfs_ext_mon $BENCH/bin
+fi
+exit 0
--- /dev/null
+/*
+ * SFS sync daemon rpcgen configuration file
+ * @(#)sfs_sync.x 2.1 97/10/23
+ *
+ * XXX Not currently used to generate source code
+ */
+
+const MAX_STR1_LEN = 31;
+const MAX_STR2_LEN = 2560;
+
+struct sync_string {
+ int clnt_id; /* client number */
+ string clnt_type<MAX_STR1_LEN>; /* message type, hard coded */
+ string clnt_transaction<MAX_STR1_LEN>; /* transaction id */
+ string clnt_data<MAX_STR2_LEN>; /* results strings */
+};
+
+program SFS {
+ version SFS {
+ int
+ SIGNAL_NULLPROC (void) = 0;
+ int
+ SIGNAL_SFS (sync_string) = 1;
+ } = 1;
+} = 100500;
--- /dev/null
+main()
+{
+ int i = 1;
+ i <<= 2;
+ printf("%d\n", i);
+}