From: Michael Vrable Date: Fri, 23 Apr 2010 19:48:14 +0000 (-0700) Subject: Import TBBT (NFS trace replay). X-Git-Url: http://git.vrable.net/?p=bluesky.git;a=commitdiff_plain;h=adc8816a09e5b6be2e58f4a7c28d2418a74cce9c Import TBBT (NFS trace replay). Code downloaded from http://www.ecsl.cs.sunysb.edu/TBBT/. --- diff --git a/TBBT/README b/TBBT/README new file mode 100644 index 0000000..d2886cf --- /dev/null +++ b/TBBT/README @@ -0,0 +1,50 @@ +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 diff --git a/TBBT/trace_init/EXAMPLES b/TBBT/trace_init/EXAMPLES new file mode 100755 index 0000000..2ed5187 --- /dev/null +++ b/TBBT/trace_init/EXAMPLES @@ -0,0 +1,170 @@ +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# $Id: EXAMPLES,v 1.3 2003/07/28 14:27:16 ellard Exp $ +# +# For nfsscan version 0.10a (dated 7/25/2003). + +INTRODUCTION + +The usual procedure for analyzing a trace is the following: + + 1. Use nfsscan to produce a tabularized summary of each + 300-second segment of the trace. For these examples, + we'll call this DEFAULT_TABLE. + + Depending on what you're looking for, the default + settings of nfsscan might not provide all the info + you're going to want in the next step. The default is + to omit per-client, per-user, per-group, and per-file + stats and only tally total operation counts. [THE + DEFAULTS MAY CHANGE IN A FUTURE RELEASE.] + + 2. Use ns_timeagg to create a summary of activity in the + entire trace named SUMMARY_TABLE from DEFAULT_TABLE. + + Note that almost anything ns_timeagg and ns_split can + do can also be done directly with nfsscan. However, + the implicit goal of ns_timeagg and ns_split is to + AVOID re-running nfsscan. It is much faster to + re-process a table created by nfsscan than it is to + re-create the table -- the input to nfsscan is + typically several million (or billion) lines of trace + data, while the output is usually only a few thousand + table rows. + + 3. Use ns_quickview to plot interesting aspects of the + DEFAULT_TABLE and/or SUMMARY_TABLE. + + 4. [optional] Use ns_split and/or ns_tsplit to isolate + interesting parts of DEFAULT_TABLE (such as per-client + or per-user counts). Repeat steps 2 and 3 with the + results. + + 5. [optional] If steps 2-4 found anything interesting, re-run + nfsscan with new parameters to take a closer look at + the trace. + +Examples and discussion of these steps and related topics is given +below. + +For these examples, TRACE is a trace file gathered by nfsdump (or +another tool that creates traces files in the same format), and TABLE.ns +is a file created by nfsscan from TRACE. The suffix ".ns" is also +used to denote files that contain tables created by nfsscan, +ns_timeagg, ns_split, and ns_tsplit. Example commandlines always +begin with "%". + +1. RUNNING NFSSCAN + +2. CREATING A SUMMARY TABLE + + To compute a table contsisting of a single row with counts for + each operation tallied by the nfsscan run, aggregate over time + with a time length of zero. (Zero is treated as a special + time length that includes the entire input table.) + + % ns_timeagg -t0 TABLE.ns > SUMMARY.ns + + Note that timeagg will always aggregate over every (except + time) attribute, so it does not matter whether or not the + TABLE.ns contains per-client, per-user, per-group, or per-file + data. The sum will always be the same. + + On the other hand, if you want to prevent ns_timeagg from + aggregating over a particular attribute, specify that + attribute in the same manner as with nfsscan. For example, to + create a table with a single row containing the operation + count per user: + + % ns_timeagg -t0 -BU TABLE.ns > SUMMARY.ns + + Of course, ns_timeagg cannot create data out of thin air. If + TABLE.ns does not contain per-user information then -BU will + have no effect. + +3. PLOTTING THE DATA + + To simply plot the total operation count: + + % ns_quickview TABLE.ns + % gv qv.ps + +WHICH CLIENT REQUESTS THE MOST OPERATIONS? + +Method: use nfsscan to tally the per-client operation counts for the + entire trace file (by using -t0), and then sort by the TOTAL + op count fields: + + If TABLE contains per-client information, then this is easy: + + % ns_timeagg -t0 -BC TABLE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + + If TABLE does not contain per-client info, then it's necessary + to re-run nfsscan: + + % nfsscan -t0 -BC TRACE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + + The output from either command is a two-column table. The + first column is the total operation count of each client, and + the second column is the ID of each client. + +WHICH CLIENT DOES THE MOST READING? + + If we've already got TABLE, and it contains per-client info, + then the easiest way is to simply use extract the read count + column (instead of the TOTAL column) from TABLE: + + % ns_timeagg -t0 -BC TABLE | grep -v '^#' \ + | awk '{print $9, $3}' | sort -nr + + Or, we can nfsscan. Because we're not interested in anything + except the read count, we can change the list of operations + that nfsscan tabulates so that it only counts reads. (Of course, + the resulting table is useless for anything except answering + this particular question, and since nfsscan is expensive to run + this is probably wasteful.) + + % nfsscan -t0 -BC -Oread -i TRACE | grep -v '^#' \ + | awk '{print $7, $3}' | sort -nr + +WHICH CLIENT DOES THE MOST FSSTATS? + + fsstat is not ordinarily tabulated by nfsscan. To tell nfsscan + to keep track of it, we can change the list of operations to consist + only of fsstat: + + % nfsscan -t0 -BC -Ofsstat -i TRACE | ... + + As mentioned in the previous example, it is often wasteful to + run nfsscan just to get one number. Another approach is to + add fsstat to the default list of "interesting" operations, by + using "+" at the start of the operation list. This tells nfsscan + to append the given list of operations to the default list: + + % nfsscan -t0 -BC -O+fsstat -i TRACE | ... + + An implication of this is that it's impossible to know what + each column in the table represents unless you know what + operations were considered "interesting" for each run of + nfsscan. To help with this, nfsscan includes the commandline + and column titles at the start of each file it creates. + +WHICH USER DOES THE MOST READING? + + This is exactly like the previous example, except that we use + -BU instead -BC, to do everything per-user instead of + per-client. + +WHAT DIRECTORIES ARE HOTTEST? + + Use the -d option to find the cummulative number of operations + per directory, then sort the results by operation count. In + order to avoid drowning in data you might choose to print + print only the top 100: + + % nfsscan -i TRACE -t0 -d | grep '^D' \ + | awk '{print $7, $5}' | sort -nr | head -100 + diff --git a/TBBT/trace_init/INSTALL b/TBBT/trace_init/INSTALL new file mode 100755 index 0000000..6849497 --- /dev/null +++ b/TBBT/trace_init/INSTALL @@ -0,0 +1,61 @@ +# $Id: INSTALL,v 1.4 2003/07/28 14:27:16 ellard Exp $ + +NFSSCAN INSTALLATION INSTRUCTIONS + +This is version 0.10a of nfsscan, dated 7/25/2003. + +THIS IS A PRELIMINARY RELEASE OF NEW SOFTWARE: + +- THE COMMANDLINE FORMATS MAY EVOLVE RAPIDLY OVER THE NEXT SEVERAL + RELEASES. + +- DEBUGGING MESSAGES AND WARNINGS MAY APPEAR ON STDERR. MOST OF THESE + ARE MEANINGLESS, BUT IF YOU ENCOUNTER A PROBLEM WITH THE + PROGRAM PLEASE INCLUDE THEM IN YOUR BUG REPORT. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +System Requirements: + + - You must have PERL 5 (5.005 or later) installed in order to + use any of these tools. (None of these tools have + been tested with PERL 6 or later.) + + - If you plan to use ns_quickview, make sure that gnuplot is + installed on your system. + + ns_quickview has been tested with gnuplot 3.7 and + should work with later versions. If you have problems + with ns_quickview, please include any diagnostics + messages from gnuplot and the version of gnuplot with + your bug report. + + If gnuplot is not in your path, you must edit + ns_quickview to set GNUPLOT_PATH to the appropriate + path after step 2 in the installation instructions. + +Installation: + +1. Un-tar the distribution into an empty directory. + +2. Edit the first line of nfsscan and ns_* to point to wherever your + favorite version of PERL 5 is installed. If you don't like + looking at debugging messages, remove the -w from the + invocation of PERL. + +3. Make sure that nfsscan and ns_* are executable and that + all the files are publically readable: + + chmod 444 * + chmod 555 nfsscan ns_* + +4. Read NOTES.TXT for last-minute info or errata for this release, + and changes from previous releases. + +5. Read nfsscan.txt and EXAMPLES for more information about how to + use the tools. + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PLEASE report bugs, problems or suggestions for improvements to +ellard@eecs.harvard.edu. diff --git a/TBBT/trace_init/NOTES.TXT b/TBBT/trace_init/NOTES.TXT new file mode 100755 index 0000000..040500d --- /dev/null +++ b/TBBT/trace_init/NOTES.TXT @@ -0,0 +1,17 @@ +# $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. diff --git a/TBBT/trace_init/commands b/TBBT/trace_init/commands new file mode 100755 index 0000000..e71f0b1 --- /dev/null +++ b/TBBT/trace_init/commands @@ -0,0 +1,20 @@ +#!/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 diff --git a/TBBT/trace_init/common.pl b/TBBT/trace_init/common.pl new file mode 100755 index 0000000..5959ee1 --- /dev/null +++ b/TBBT/trace_init/common.pl @@ -0,0 +1,61 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_timeagg,v 1.4 2003/07/10 20:13:31 ellard Exp $ + +# Allows rounding of the start time, so that minor clock drift doesn't +# make trace periods appear to begin at funny times (i.e. one or two +# seconds after midnight, instead of midnight). +# +# This is only meant to help with SMALL amounts of drift. Trying to +# use this to correct for anything more than a small fraction of the +# sample interval is asking for trouble. + +require 'timelocal.pl'; + +sub findStartTime { + my ($time, $rounding) = @_; + + my $new_time = int ($time); + + if (defined $rounding && $rounding != 0) { + $rounding = int ($rounding); + + my $leftover = $new_time % $rounding; + + if ($leftover > $rounding / 2) { + $new_time += $rounding; + } + + $new_time -= ($new_time % $rounding); + } + + return $new_time; +} + +1; diff --git a/TBBT/trace_init/counts.pl b/TBBT/trace_init/counts.pl new file mode 100755 index 0000000..33d7ad9 --- /dev/null +++ b/TBBT/trace_init/counts.pl @@ -0,0 +1,103 @@ +# +# Copyright (c) 2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: counts.pl,v 1.9 2003/07/28 14:27:16 ellard Exp $ + +package counts; + +@OpList = (); +%KeysSeen = (); + +%OpCounts = (); + +sub init { + my (@oplist) = @_; + + @OpList = @oplist; +} + +sub printTitle { + my ($out) = @_; + + my $str = "#C time client fh euid egid"; + + foreach my $op ( @OpList ) { + $str .= " $op"; + } + $str .= "\n"; + + print $out $str; +} + +sub printOps { + my ($start_time, $out) = @_; + my ($k, $str, $op, $nk); + + my @allkeys = sort keys %KeysSeen; + + foreach $k ( @allkeys ) { + my $tot = "$k,TOTAL"; + + if ($main::OMIT_ZEROS && + (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) { + next; + } + + $str = sprintf ("C %s %s", $start_time, &key::key2str ($k)); + + foreach $op ( @OpList ) { + $nk = "$k,$op"; + if (exists $OpCounts{$nk}) { + if ($op eq 'readM' || $op eq 'writeM') { + $str .= sprintf (" %.3f", + $OpCounts{$nk} / (1024 * 1024)); + } + else { + $str .= " $OpCounts{$nk}" + } + } + else { + $str .= " 0"; + } + } + $str .= "\n"; + print $out $str; + } +} + +sub resetOpCounts { + + # Clear the counts on everything we've seen. + + foreach my $op ( keys %OpCounts ) { + $OpCounts{$op} = 0; + } +} + +1; + diff --git a/TBBT/trace_init/extract-hierarchy b/TBBT/trace_init/extract-hierarchy new file mode 100755 index 0000000..e6b2b9f --- /dev/null +++ b/TBBT/trace_init/extract-hierarchy @@ -0,0 +1,4 @@ +#Usage extract-hierarchy trace_file [-S] +#nfsscan $1 +echo nfsscan $1 +rfs.pl $2 diff --git a/TBBT/trace_init/hier.pl b/TBBT/trace_init/hier.pl new file mode 100755 index 0000000..3e2ebfc --- /dev/null +++ b/TBBT/trace_init/hier.pl @@ -0,0 +1,715 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $ +# +# hier.pl - Tools to map out the file system hierarchy. This is +# accomplished by snooping out the lookup calls. +# +# This is expensive because the hierarchy can require a LOT of space +# to store for a large system with lots of files (especially if files +# come and go). Don't construct the hierarchy unless you want it -- +# and be prepared to prune it from time to time. + +package hier; + +# Tables used by the outside world: + +%fh2Parent = (); +%fh2Name = (); +%fh2Attr = (); +%fh2AttrOrig = (); +%parent2fh = (); + +#RFS: init FS +%rootsName = (); +%discardFHs = (); +%rootsFHs = (); +#RFS: dependency table +%fhCreate = (); + +%rfsAllFHs = (); +%fhType = (); # we use %fhIsDir instead +$rfsLineNum = 0; + + + + + + +# Library-private tables and variables. + +%pendingCallsXIDnow = (); +%pendingCallsXIDfh = (); +%pendingCallsXIDname = (); + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 5 * 60; # Five minutes. + +sub processLine { + my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_; + + &addRfsAllFHs($line, $proto, $op, $uxid, + $now, $response, $fh_type); + + if ($now > $nextPruneTime) { + &prunePending ($now - $PRUNE_INTERVAL); + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + + my $uxid = "$client-$xid"; + + # 'lookup', 'create', 'rename', 'delete', + # 'getattr', 'setattr' + + #RFS: add mkdir/rmdir/symlink + if ( $op eq 'lookup' || $op eq 'create' || $op eq 'mkdir' || + ($op eq 'symlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) { + return (&doLookup ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'rename') { + } + elsif ($op eq 'remove' || $op eq 'rmdir') { + # RFS: why remove these entries? Just let them exist since + # there is generation number available to distinguish btw removed dir/file + # and new dir/file with the same inode number. + #return (&doRemove ($line, $proto, $op, $uxid, + # $now, $response, $fh_type)); + } + elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' || + ($op eq 'readlink' && ($proto eq 'C3' || $proto eq 'R3' ) ) ) { + return (&doGetAttr ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'setattr') { + } +} + + +# get time stamp + +sub getTimeStamp{ + my ($line) = @_; + + if (! ($line =~ /^[0-9]/)) { + print "getTimeStamp return undef\n"; + return undef; + } + else { + my @l = split (' ', $line, 2); + return $l[0]; + } +} + +sub addRfsAllFHs { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + my $fh = undef; + + my $checkfh = undef; + $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh'); + if (defined $checkfh) { + $fh = nfsd::nfsDumpCompressFH ($fh_type, $checkfh); + } + + my $fh2 = undef; + + if ($op eq 'rename' || $op eq 'link') { + $checkfh = nfsd::nfsDumpParseLineField ($line, 'fh2'); + if (defined $checkfh) { + $fh2 = nfsd::nfsDumpCompressFH ($fh_type, $checkfh); + } + } + + if (defined $fh) { + # record the first appearance of the fh + if ( !exists $rfsAllFHs{$fh} ) { + $rfsAllFHs{$fh} = $rfsLineNum ; + } + } + + if (defined $fh2) { + if ( !exists $rfsAllFHs{$fh2} ) { + $rfsAllFHs{$fh2} = $rfsLineNum; + } + } + + return ; +} + + +sub doLookup { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDnow{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + + if ($response eq 'OK') { + my $cfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + my $type = nfsd::nfsDumpParseLineField ($line, 'ftype'); + + #if ($type == 2) + { + $fhIsDir{$cfh} = $type; + } + + # Original code + # $fh2Parent{$cfh} = $pfh; + # $fh2Name{$cfh} = $name; + # $parent2fh{"$pfh,$name"} = $cfh; + # RFS code: in case of the rename, we will record the name of the old name + $fh2Parent{$cfh} = $pfh; + if (! exists $fh2Name{$cfh}) { + $fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + } else { + # keep the old name in the fh2Name{$cfh} + # and we also add the newname and pfh mapping + #$fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + } + + my ($size, $mode, $atime, $mtime, $ctime, $nlink) = + nfsd::nfsDumpParseLineFields ($line, + 'size', 'mode', + 'atime', 'mtime', 'ctime', 'nlink'); + my $ts = getTimeStamp($line); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime"; + if (! exists $fh2AttrOrig{$cfh} ) { + $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + + } + + return ; +} + +sub doRemove { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $pfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $pfh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + delete $pendingCallsXIDnow{$uxid}; + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + my $cfh = $parent2fh{"$pfh,$name"}; + + if ($response eq 'OK') { + if ($op eq 'remove') { + printFileInfo ($cfh, 'D'); + + delete $fh2Parent{$cfh}; + delete $fh2Name{$cfh}; + delete $fh2Attr{$cfh}; + delete $fhs2AttrOrig{$cfg}; + delete $parent2fh{"$pfh,$name"}; + } + } + } + + return ; +} + +sub doGetAttr { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + #if (nfsd::nfsDumpParseLineField ($line, 'fh') + # eq '00018961-57570100-d2440000-61890100') { + # printf STDERR "Seen it ($op)\n"; + #} + + if (! defined $fh) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; +# RFS debug code +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($fh eq $wantfh) { +# print "JIAWU: doGetAttr call $wantfh\n"; +#} + } + else { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $fh = $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDnow{$uxid}; +# RFS debug code +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($fh eq $wantfh) { +# print "JIAWU: doGetAttr response $wantfh\n"; +#} + + if ($response ne 'OK') { + return ; + } + + my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype'); + if (!defined $ftype) { + print STDERR "BAD $line"; + return ; + } + + #if ($ftype == 2) + { + $fhIsDir{$fh} = $ftype; + } + + #RFS comment: here if fh is a directory, then it will not be add + # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name + # if ($ftype != 1) { + # return ; + #} + if ($ftype != 1) { + #return ; + } + + + my ($mode, $size, $atime, $mtime, $ctime, $nlink) = + nfsd::nfsDumpParseLineFields ($line, + 'mode', 'size', 'atime', 'mtime', 'ctime', 'nlink'); + my $ts = getTimeStamp($line); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime"; + + if (! exists $fh2AttrOrig{$fh} ) { + $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } + $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime $nlink $ts"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %pendingCallsXIDnow ) { + if ($pendingCallsXIDnow{$uxid} < $when) { +# RFS debug code +my $fh = $pendingCallsXIDfh{$uxid}; +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: prunePending $wantfh\n"; +} +#enf RFS + delete $pendingCallsXIDnow{$uxid}; + } + } + + return ; +} + +# Return as much of the path for the given fh as possible. It may or +# may not reach the root (or the mount point of the file system), but +# right now we don't check. Usually on busy systems the data is +# complete enough so that most paths are complete back to the mount +# point. + +sub findPath { + my ($fh) = @_; + my $isdir = 0; + my $cnt = 0; + my $MaxPathLen = 40; + + if (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) { + $isdir = 1; + } + + my @path = (); + while ($fh && exists $fh2Name{$fh}) { + unshift (@path, $fh2Name{$fh}); + + if ( ($fh2Name{$fh} ne '"RFSNN0"' ) ) { + if (! exists $fh2Parent{$fh}) { + print STDERR "$fh2Name{$fh} "; + if ( ($fh2Name{$fh} eq '"RFSNN0"' ) ) { + print STDERR "eq RFSNN0\n"; + } else { + print STDERR "NOT eq RFSNN0\n"; + } + } + if ($fh eq $fh2Parent{$fh}) { + unshift (@path, '(LOOP)'); + last; + } + } + + if ($cnt++ > $MaxPathLen) { + print STDERR "findPath: path too long (> $MaxPathLen)\n"; + unshift (@path, '(TOO-LONG)'); + last; + } + + $fh = $fh2Parent{$fh}; + } + + # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory) + if ($fh && !exists $fh2Name{$fh} && (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ) { + if (exists $rootsName{$fh}) { + #print "JIAWU: $rootsName{$fh}\n"; + unshift(@path, $rootsName{$fh}); + } else { + print "JIAWU: WARNING! No rootsName for this fh: $fh\n"; + unshift(@path, $fh); + } + } else { + if ($fh && !exists $fh2Name{$fh} && (!exists $fhIsDir{$fh} || (exists $fhIsDir{$fh} && $fhIsDir{$fh}!=2)) ) { + if (exists $discardFHs{$fh}) { + open NOATTRDIR, ">>noattrdirdiscard" || die "open noattrdirdiscard failed\n"; + print NOATTRDIR "$fh DISCARD\n"; + close NOATTRDIR; + } else { + # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0 + unshift(@path, '"RFSNN0"'); + $fhIsDir{$fh}=2; + $fh2Name{$fh} = '"RFSNN0"'; + $rootsName{$fh} = '"RFSNN0"'; + open NOATTRDIR, ">>noattrdir-root"; + print NOATTRDIR "$fh /RFSNN0/\n"; + close NOATTRDIR; + } + } + } + + + my $str = ''; + $cnt = 0; + foreach my $p ( @path ) { + $p =~ s/^.//; + $p =~ s/.$//; + $str .= "/$p"; + $cnt++; + } + + if ($isdir) { + $str .= '/'; + } + + if ($cnt == 0) { + $str = '.'; + } + + return ($str, $cnt); +} + + +$total_unknown_fh = 0; +$total_known_fh = 0; + +sub printAll { + my ($start_time, $out) = @_; + + my %allfh = (); + my $fh; + my $u = 0; + my $k = 0; + + # RFS print more information here + open (OUT_RFS, ">rfsinfo") || + die "Can't create $OutFileBaseName.rfs."; + + foreach $fh ( keys %fh2Attr ) { + $allfh{$fh} = 1; + } + foreach $fh ( keys %fh2Name ) { + $allfh{$fh} = 1; + } + + #RFS: before printFileInfo, name those roots' name + + #RFS there are three kind of fh + # 1. fh/name paired (fh/attr must) + # 2. fh/attr but no fh/name: type file (discard related operations) + # 3. fh/attr but no fh/name: type dir (keep as persuedo root) + $u = $k = 0; + my $sn=1; + foreach $fh ( keys %allfh ) { + if (exists $fh2Parent{$fh} ) { + $k++; + } + else { + $u++; + my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + if ($type eq 'D') { + $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++); + $rootsFHs{$fh} = 2; + } + else { + $discardFHs{$fh} = 1; + } + } + } + print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n"; + $u = keys %rootsFHs; + print OUT_RFS "#RFS: root fh list($u)\n"; + foreach $fh (keys %rootsName) { + print OUT_RFS "#RFS: $rootsName{$fh} $fh\n"; + } + $u = keys %discardFHs; + print OUT_RFS "#RFS: discard fh list($u)\n"; + print OUT_RFS join("\n", keys %discardFHs, ""); + + + print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n"; + + print $out "#T starttime = $start_time\n"; + foreach $fh ( keys %allfh ) { + printFileInfoOutputFile ($fh, 'A', $out); + } + + my $numfh2Name = keys %fh2Name; + my $numfh2Attr = keys %fh2Attr; + print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n"; + + + $u = $k = 0; + foreach $fh ( keys %allfh ) { + if ( exists $fh2Name{$fh} ) {$k++;} + else {$u++;} + } + print OUT_RFS "#stat: total fh with name = $k, without name = $u\n"; + + print OUT_RFS "#stat: finally, total known fh = $total_known_fh, unknown = $total_unknown_fh\n"; + +# Note: fh with name (8303), fh without name (103) +# root fh list: 18 +# discard fh list: 85 +# known fh (8321): ( fh with name(8303) + root fh list (18) = 8321) +# unknown fh (85) +# +# All fh from the those data structures: 8321 + 85 = 8303+103 +# Or, in keys %allfh +# +# + print OUT_RFS "#RFS\n"; + close OUT_RFS; + + open (MISSED, ">missdiscardfh") || + die "Can't create missdiscardfh."; + foreach $fh (keys %rfsAllFHs) { + if ( !exists $allfh{$fh} && + ( (defined $fh2Name{$fh}) && ($fh2Name{$fh} ne '"RFSNN0"')) ) { + print MISSED "$fh LN: $rfsAllFHs{$fh}\n" + } + } + close MISSED; + +# check for a special fh +#my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +#if ($allfh{$wantfh} == 1) { +# print OUT_RFS "JIAWU: found $wantfh\n"; +#} else { +# print OUT_RFS "JIAWU: NOT found $wantfh\n"; +#} +#foreach $fh ( keys %allfh ) { +# if ( $fh eq $wantfh ) { +# print OUT_RFS "JIAWU: found $wantfh\n"; +# printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS); +# last; +# } +#} +#print OUT_RFS "JIAWU: after \n"; + + +} + +sub printFileInfoOutputFile { + my ($fh, $state, $out) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + #my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + my $type = $fhIsDir{$fh}; + if (!defined $type) + { + print STDERR "unknown ftype(U) for fh: $fh\n"; + $type = 'U'; + } + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + + print $out "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +sub printFileInfo { + my ($fh, $state) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh} && $fhIsDir{$fh}==2) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1 -1 -1 -1"; + + print "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +# +# The flow to create the dependency table +# +# create(dirfh, name, attr) -->newfh, new attr +# mkdir(dirfh, name, attr) -> newfh, new attr +# +# remove(dirfh, name) --> status +# rmdir(dirfh, name) --> status +# rename(dirfh, name, todirfh, toname) --> status +# +# link(newdirfh, newname, dirfh, name) --> status (newdir/newname=>dir/name) +# syslink(newdirfh, newname, string) --> status (newdir/newname=>"string") +# readlink(fh) --> string +# lookup(dirfh, name) --> fh, attr +# getattr(fh) --> attr +# setattr(fh, attr) --> attr +# read(fh, offset, count) -> attr, data +# write(fh, offset, count, data) --> attr +# readdir(dirfh, cookie, count) --> entries +# statfs(fh) --> status +# +# +# +# +# for each line the trace file: +# if (op == R2 or R3) continue; #skip the response line +# switch (the op) +# { +# # CREATION OPs: +# case create: +# case remove: +# # DELETE OPs: +# case mkdir: +# case rmdir: +# # other OPs +# +# +# +# +# +1; diff --git a/TBBT/trace_init/hier.pl.old b/TBBT/trace_init/hier.pl.old new file mode 100755 index 0000000..b7f0cf4 --- /dev/null +++ b/TBBT/trace_init/hier.pl.old @@ -0,0 +1,550 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: hier.pl,v 1.14 2003/07/26 20:52:03 ellard Exp $ +# +# hier.pl - Tools to map out the file system hierarchy. This is +# accomplished by snooping out the lookup calls. +# +# This is expensive because the hierarchy can require a LOT of space +# to store for a large system with lots of files (especially if files +# come and go). Don't construct the hierarchy unless you want it -- +# and be prepared to prune it from time to time. + +package hier; + +# Tables used by the outside world: + +%fh2Parent = (); +%fh2Name = (); +%fh2Attr = (); +%fh2AttrOrig = (); +%parent2fh = (); +#RFS +%rootsName = (); +%discardFHs = (); +%rootsFHs = (); + + +# Library-private tables and variables. + +%pendingCallsXIDnow = (); +%pendingCallsXIDfh = (); +%pendingCallsXIDname = (); + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 5 * 60; # Five minutes. + +sub processLine { + my ($line, $proto, $op, $xid, $client, $now, $response, $fh_type) = @_; + + if ($now > $nextPruneTime) { + &prunePending ($now - $PRUNE_INTERVAL); + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + + my $uxid = "$client-$xid"; + + # 'lookup', 'create', 'rename', 'delete', + # 'getattr', 'setattr' + + #RFS: add mkdir/rmdir + if ($op eq 'lookup' || $op eq 'create' || $op eq 'mkdir') { + return (&doLookup ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'rename') { + } + elsif ($op eq 'remove' || $op eq 'rmdir') { + # RFS: why remove these entries? Just let them exist since + # there is generation number available to distinguish btw removed dir/file + # and new dir/file with the same inode number. + #return (&doRemove ($line, $proto, $op, $uxid, + # $now, $response, $fh_type)); + } + elsif ($op eq 'getattr' || $op eq 'read' || $op eq 'write' ) { + return (&doGetAttr ($line, $proto, $op, $uxid, + $now, $response, $fh_type)); + } + elsif ($op eq 'setattr') { + } +} + +sub doLookup { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDnow{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + + if ($response eq 'OK') { + my $cfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + my $type = nfsd::nfsDumpParseLineField ($line, 'ftype'); + + if ($type == 2) { + $fhIsDir{$cfh} = 1; + } + + $fh2Parent{$cfh} = $pfh; + $fh2Name{$cfh} = $name; + $parent2fh{"$pfh,$name"} = $cfh; + + my ($size, $mode, $atime, $mtime, $ctime) = + nfsd::nfsDumpParseLineFields ($line, + 'size', 'mode', + 'atime', 'mtime', 'ctime'); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + #$fh2Attr{$cfh} = "$size $mode $atime $mtime $ctime"; + if (! exists $fh2AttrOrig{$cfh} ) { + $fh2AttrOrig{$cfh} = "$size $mode $op $atime $mtime $ctime"; + } + $fh2Attr{$cfh} = "$size $mode $op $atime $mtime $ctime"; + } + + } + + return ; +} + +sub doRemove { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $tag = ($proto eq 'C3') ? 'name' : 'fn'; + my $name = nfsd::nfsDumpParseLineField ($line, $tag); + + # All directories have (at least) three names: the + # given name, and "." and "..". We're only interested + # in the given name. + + if ($name eq '"."' || $name eq '".."') { + return ; + } + + my $pfh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $pfh; + $pendingCallsXIDname{$uxid} = $name; + } + elsif ($proto eq 'R3' || $proto eq 'R2') { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $pfh = $pendingCallsXIDfh{$uxid}; + my $name = $pendingCallsXIDname{$uxid}; + + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDname{$uxid}; + delete $pendingCallsXIDnow{$uxid}; + + if (! exists $parent2fh{"$pfh,$name"}) { + return ; + } + + my $cfh = $parent2fh{"$pfh,$name"}; + + if ($response eq 'OK') { + if ($op eq 'remove') { + printFileInfo ($cfh, 'D'); + + delete $fh2Parent{$cfh}; + delete $fh2Name{$cfh}; + delete $fh2Attr{$cfh}; + delete $fhs2AttrOrig{$cfg}; + delete $parent2fh{"$pfh,$name"}; + } + } + } + + return ; +} + +sub doGetAttr { + my ($line, $proto, $op, $uxid, $now, $response, $fh_type) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + my $fh = nfsd::nfsDumpCompressFH ($fh_type, + nfsd::nfsDumpParseLineField ($line, 'fh')); + + #if (nfsd::nfsDumpParseLineField ($line, 'fh') + # eq '00018961-57570100-d2440000-61890100') { + # printf STDERR "Seen it ($op)\n"; + #} + + if (! defined $fh) { + return ; + } + + $pendingCallsXIDnow{$uxid} = $now; + $pendingCallsXIDfh{$uxid} = $fh; +# RFS debug code +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: doGetAttr call $wantfh\n"; +} + } + else { + if (! exists $pendingCallsXIDnow{$uxid}) { + return ; + } + + my $fh = $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDfh{$uxid}; + delete $pendingCallsXIDnow{$uxid}; +# RFS debug code +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: doGetAttr response $wantfh\n"; +} + + if ($response ne 'OK') { + return ; + } + + my ($ftype) = nfsd::nfsDumpParseLineFields ($line, 'ftype'); + if (!defined $ftype) { + print STDERR "BAD $line"; + return ; + } + + if ($ftype == 2) { + $fhIsDir{$fh} = 1; + } + + #RFS comment: here if fh is a directory, then it will not be add + # in the two hash table %fh2Attr(%fh2AttrOrig) and %fh2Name + # if ($ftype != 1) { + # return ; + #} + if ($ftype != 1) { + #return ; + } + + + my ($mode, $size, $atime, $mtime, $ctime) = + nfsd::nfsDumpParseLineFields ($line, + 'mode', 'size', 'atime', 'mtime', 'ctime'); + + # RFS: modify here to get/maintain more file attributes + # we can just check the ctime and compare it with trace-start-time + # to decide whether to create a file/diretory. + # atime - last access time of the file + # mtime - last modification time of the file + # ctime - last file status change time + + # $fh2Attr{$fh} = "$size $mode $atime $mtime $ctime"; + + if (! exists $fh2AttrOrig{$fh} ) { + $fh2AttrOrig{$fh} = "$size $mode $op $atime $mtime $ctime"; + } + $fh2Attr{$fh} = "$size $mode $op $atime $mtime $ctime"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %pendingCallsXIDnow ) { + if ($pendingCallsXIDnow{$uxid} < $when) { +# RFS debug code +my $fh = $pendingCallsXIDfh{$uxid}; +my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; +if ($fh eq $wantfh) { + print "JIAWU: prunePending $wantfh\n"; +} +#enf RFS + delete $pendingCallsXIDnow{$uxid}; + } + } + + return ; +} + +# Return as much of the path for the given fh as possible. It may or +# may not reach the root (or the mount point of the file system), but +# right now we don't check. Usually on busy systems the data is +# complete enough so that most paths are complete back to the mount +# point. + +sub findPath { + my ($fh) = @_; + my $isdir = 0; + my $cnt = 0; + my $MaxPathLen = 40; + + if (exists $fhIsDir{$fh}) { + $isdir = 1; + } + + my @path = (); + while ($fh && exists $fh2Name{$fh}) { + unshift (@path, $fh2Name{$fh}); + if ($fh eq $fh2Parent{$fh}) { + unshift (@path, '(LOOP)'); + last; + } + + if ($cnt++ > $MaxPathLen) { + print STDERR "findPath: path too long (> $MaxPathLen)\n"; + unshift (@path, '(TOO-LONG)'); + last; + } + + $fh = $fh2Parent{$fh}; + } + + # RFS: append the ~user (fh and !exists $fh2Name{$fh} and type is Directory) + if ($fh && !exists $fh2Name{$fh} && exists $fhIsDir{$fh}) { + if (exists $rootsName{$fh}) { + print "JIAWU: $rootsName{$fh}\n"; + unshift(@path, $rootsName{$fh}); + } else { + print "JIAWU: WARNING! No rootsName for this fh: $fh\n"; + unshift(@path, $fh); + } + } else { + if ($fh && !exists $fh2Name{$fh} && !exists $fhIsDir{$fh}) { + if (exists $discardFHs{$fh}) { + open NOATTRDIR, ">>noattrdirdiscard"; + print NOATTRDIR "$fh DISCARD\n"; + close NOATTRDIR; + } else { + # RFS: if a possible fh without attr and name, then regard it as a special root ~/RFSNN0 + unshift(@path, '"RFSNN0"'); + $fhIsDir{$fh}=1; + $fh2Name{$fh} = '"RFSNN0"'; + $rootsName{$fh} = '"RFSNN0"'; + open NOATTRDIR, ">>noattrdir-root"; + print NOATTRDIR "$fh RFSNN0\n"; + close NOATTRDIR; + } + } + } + + + my $str = ''; + $cnt = 0; + foreach my $p ( @path ) { + $p =~ s/^.//; + $p =~ s/.$//; + $str .= "/$p"; + $cnt++; + } + + if ($isdir) { + $str .= '/'; + } + + if ($cnt == 0) { + $str = '.'; + } + + return ($str, $cnt); +} + + +$total_unknown_fh = 0; +$total_known_fh = 0; + +sub printAll { + my ($start_time, $out) = @_; + + my %allfh = (); + my $fh; + my $u = 0; + my $k = 0; + + # RFS print more information here + open (OUT_RFS, ">rfsinfo") || + die "Can't create $OutFileBaseName.rfs."; + + foreach $fh ( keys %fh2Attr ) { + $allfh{$fh} = 1; + } + foreach $fh ( keys %fh2Name ) { + $allfh{$fh} = 1; + } + + #RFS: before printFileInfo, name those roots' name + + #RFS there are three kind of fh + # 1. fh/name paired (fh/attr must) + # 2. fh/attr but no fh/name: type file (discard related operations) + # 3. fh/attr but no fh/name: type dir (keep as persuedo root) + $u = $k = 0; + my $sn=1; + foreach $fh ( keys %allfh ) { + if (exists $fh2Parent{$fh} ) { + $k++; + } + else { + $u++; + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + if ($type eq 'D') { + $rootsName{$fh} = sprintf("\"RFSNN%d\"", $sn++); + $rootsFHs{$fh} = 1; + } + else { + $discardFHs{$fh} = 1; + } + } + } + print OUT_RFS "#stat: fh with parent = $k, fh without parent = $u\n"; + $u = keys %rootsFHs; + print OUT_RFS "#RFS: root fh list($u)\n"; + foreach $fh (keys %rootsName) { + print OUT_RFS "#RFS: $rootsName{$fh} $fh\n"; + } + $u = keys %discardFHs; + print OUT_RFS "#RFS: discard fh list($u)\n"; + print OUT_RFS join("\n", keys %discardFHs, ""); + + + print $out "#F type state fh path pathcount attrOrig(size,mode,op,atime,mt,ct) attrLast(size,mode,op,at,mt,ct)\n"; + + print $out "#T starttime = $start_time\n"; + foreach $fh ( keys %allfh ) { + printFileInfoOutputFile ($fh, 'A', $out); + } + + + my $numfh2Name = keys %fh2Name; + my $numfh2Attr = keys %fh2Attr; + print OUT_RFS "fh2name has $numfh2Name, fh2Attr has $numfh2Attr\n"; + my $wantfh = "6189010057570100200000000000862077ed3800d24400006189010057570100"; + if ($allfh{$wantfh} == 1) { + print OUT_RFS "JIAWU: found $wantfh\n"; + } else { + print OUT_RFS "JIAWU: NOT found $wantfh\n"; + } + foreach $fh ( keys %allfh ) { + if ( $fh eq $wantfh ) { + print OUT_RFS "JIAWU: found $wantfh\n"; + printFileInfoOutputFile ($fh, 'JIAWU', *OUT_RFS); + last; + } + } + print OUT_RFS "JIAWU: after \n"; + + + $u = $k = 0; + foreach $fh ( keys %allfh ) { + if ( exists $fh2Name{$fh} ) {$k++;} + else {$u++;} + } + print OUT_RFS "#stat: total known fh = $total_known_fh, unknown = $total_unknown_fh\n"; + print OUT_RFS "#stat: total fh with name = $k, without name = $u\n"; + + print OUT_RFS "#RFS\n"; + close OUT_RFS; + +} + +sub printFileInfoOutputFile { + my ($fh, $state, $out) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1"; + + print $out "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +sub printFileInfo { + my ($fh, $state) = @_; + + my ($p, $c) = findPath ($fh); + + if ($c == 0) {$total_unknown_fh++;} + else {$total_known_fh++;} + + my $type = (exists $fhIsDir{$fh}) ? 'D' : 'F'; + my $attr = (exists $fh2Attr{$fh}) ? + $fh2Attr{$fh} : "-1 -1 -1 -1 -1"; + my $attrOrig = (exists $fh2AttrOrig{$fh}) ? + $fh2AttrOrig{$fh} : "-1 -1 -1 -1 -1"; + + print "F $type $state $fh $p $c $attrOrig $attr\n"; +} + +1; diff --git a/TBBT/trace_init/key.pl b/TBBT/trace_init/key.pl new file mode 100755 index 0000000..b4eacb0 --- /dev/null +++ b/TBBT/trace_init/key.pl @@ -0,0 +1,122 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: key.pl,v 1.11 2003/07/26 20:52:04 ellard Exp $ + +package key; + +sub makeKey { + my ($line, $proto, $op, $xid, $client, $now) = @_; + my ($client_id, $fh, $euid, $egid) = ('u', 'u', 'u', 'u'); + my ($uxid) = "$client-$xid"; + + if ($proto eq 'R3' || $proto eq 'R2') { + if (exists $PendingKeyStr{$uxid}) { + return ($PendingKeyStr{$uxid}); + } + else { + return 'u,u,u,u'; + } + } + + if ($main::UseClient) { + $client_id = $client; + $client_id =~ s/\..*//g + } + if ($main::UseFH && $op ne 'null') { + my $tag = ($op eq 'commit') ? 'file' : 'fh'; + + $fh = nfsd::nfsDumpParseLineField ($line, $tag); + if (! defined $fh) { + print STDERR "undefined fh ($line)\n"; + } + + $fh = nfsd::nfsDumpCompressFH ($main::FH_TYPE, $fh); + + } + if ($main::UseUID && $op ne 'null') { + $euid = nfsd::nfsDumpParseLineField ($line, 'euid'); + } + if ($main::UseGID && $op ne 'null') { + $egid = nfsd::nfsDumpParseLineField ($line, 'egid'); + } + + my $key = "$client_id,$fh,$euid,$egid"; + $KeysSeen{$key} = 1; + + $PendingKeyStr{$uxid} = $key; + $PendingKeyTime{$uxid} = $now; + + return ($key); +} + +sub key2str { + my ($key) = @_; + + my ($client_id, $fh, $euid, $egid) = split (/,/, $key); + + if ($client_id ne 'u') { + + # just for aesthetics: + $client_id = sprintf ("%.8x", hex ($client_id)); + $client_id =~ /^(..)(..)(..)(..)/; + $client_id = sprintf ("%d.%d.%d.%d", + hex ($1), hex ($2), + hex ($3), hex ($4)); + $client_id = sprintf ("%-15s", $client_id); + } + + if ($euid ne 'u') { + $euid = hex ($euid); + } + if ($egid ne 'u') { + $egid = hex ($egid); + } + + return ("$client_id $fh $euid $egid"); +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %PendingKeyTime ) { + if ($PendingKeyTime{$uxid} < $when) { + delete $PendingKeyTime{$uxid}; + delete $PendingKeyStr{$uxid}; + } + } + + return ; +} + +1; diff --git a/TBBT/trace_init/latency.pl b/TBBT/trace_init/latency.pl new file mode 100755 index 0000000..0ba1ecd --- /dev/null +++ b/TBBT/trace_init/latency.pl @@ -0,0 +1,159 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: latency.pl,v 1.8 2003/07/28 14:27:16 ellard Exp $ +# +# latency.pl - + +package latency; + +%PendingOps = (); +%OpCount = (); +%OpTime = (); +%KeysSeen = (); + +@OpList = (); + +sub init { + my (@oplist) = @_; + + @OpList = @oplist; +} + +# Bugs: might not recognize the actual response packets. It's an +# approximation. + +sub update { + my ($key, $proto, $op, $xid, $client, $now) = @_; + + my $uxid = "$client-$xid"; + + if ($proto eq 'C3' || $proto eq 'C2') { + $PendingOps{$uxid} = $now; + } + elsif (exists $PendingOps{$uxid}) { + my $elapsed = $now - $PendingOps{$uxid}; + + $KeysSeen{$key} = 1; + + $OpTime{"$key,$op"} += $elapsed; + $OpCount{"$key,$op"}++; + + $OpTime{"$key,TOTAL"} += $elapsed; + $OpCount{"$key,TOTAL"}++; + + $OpTime{"$key,INTERESTING"} += $elapsed; + $OpCount{"$key,INTERESTING"}++; + + delete $PendingOps{$uxid}; + } +} + +sub resetOpCounts { + + my $k; + + foreach $k ( keys %OpTime ) { + $OpTime{$k} = 0.0; + } + foreach $k ( keys %OpCount ) { + $OpCount{$k} = 0; + } + + return ; +} + +sub printTitle { + my $str = "#L time client euid egid fh"; + + foreach my $op ( @OpList ) { + $str .= " $op-cnt $op-lat"; + } + $str .= "\n"; + + print $str; +} + +sub printOps { + my ($start_time, $out) = @_; + my ($k, $str, $op, $nk, $latms, $cnt); + + my @allkeys = sort keys %KeysSeen; + + foreach $k ( @allkeys ) { + my $tot = "$k,TOTAL"; + + if ($main::OMIT_ZEROS && + (! exists $OpCounts{$tot} || $OpCounts{$tot} == 0)) { + next; + } + + $str = sprintf ("L %s %s", $start_time, &key::key2str ($k)); + + foreach $op ( @OpList ) { + $nk = "$k,$op"; + + if (exists $OpCount{$nk}) { + $cnt = $OpCount{"$k,$op"}; + } + else { + $cnt = 0; + } + + if ($cnt > 0) { + $latms = 1000 * $OpTime{$nk} / $cnt; + } + else { + $latms = -1; + } + + $str .= sprintf (" %d %.4f", $cnt, $latms); + } + + print $out "$str\n"; + } +} + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %PendingOps ) { + if ($PendingOps{$uxid} < $when) { + delete $PendingOps{$uxid}; + } + } + + return ; +} + +1; diff --git a/TBBT/trace_init/nfsdump.pl b/TBBT/trace_init/nfsdump.pl new file mode 100755 index 0000000..69eca84 --- /dev/null +++ b/TBBT/trace_init/nfsdump.pl @@ -0,0 +1,338 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: nfsdump.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $ +# +# Utility for dealing with raw nfsdump records. + +package nfsd; + +# If $AllowRisky is set, then allow some optimizations that might be +# "risky" in bizarre situations (but have never been observed to +# actually break anything). By default, no riskiness is permitted. + +$AllowRisky = 0; + +# nfsDumpParseLine -- initializes the global associative array +# %nfsd'nfsDumpLine with information about a record from nfsdump. +# Returns an empty list if anything goes wrong. Otherwise, returns a +# list of the protocol (eg R2, C2, R3, C3), the name of the operation, +# and the xid, the client host ID, the time, and for responses, the +# status (via nfsDumpParseLineHeader). The reason for this particular +# return list is that these are very frequently-accessed values, so it +# can save time to avoid going through the associative array to access +# them. +# +# All records begin with several fixed fields, and then are followed +# by some number of name/value pairs, and finally some diagnostic +# fields (which are mostly ignored by this routine-- the only +# diagnostic this routine cares about is whether the packet as part of +# a jumbo packet or not. If so, then 'truncated' is set.) + +sub nfsDumpParseLine { + my ($line, $total) = @_; + + my (@rl) = &nfsDumpParseLineHeader ($line); + + if (@rl && $total) { + &nfsDumpParseLineBody ($line); + } + + return @rl; +} + +sub nfsDumpParseLineBody { + my ($line) = @_; + my $i; + my $client_id; + my $reseen; + + undef %nfsDumpLine; + + # If the line doesn't start with a digit, then it's certainly + # not properly formed, so bail out immediately. + + if (! ($line =~ /^[0-9]/)) { + return undef; + } + + my @l = split (' ', $line); + my $lineLen = @l; + if ($l[$lineLen - 1] eq 'LONGPKT') { + splice (@l, $lineLen - 1); + $nfsDumpLine{'truncated'} = 1; + $lineLen--; + } + + $nfsDumpLine{'time'} = $l[0]; + $nfsDumpLine{'srchost'} = $l[1]; + $nfsDumpLine{'deshost'} = $l[2]; + $nfsDumpLine{'proto'} = $l[4]; + $nfsDumpLine{'xid'} = $l[5]; + $nfsDumpLine{'opcode'} = $l[6]; + $nfsDumpLine{'opname'} = $l[7]; + + if (($l[4] eq 'R3') || ($l[4] eq 'R2')) { + $nfsDumpLine{'status'} = $l[8]; + + $client_id = $l[2]; + $reseen = 0; + for ($i = 9; $i < $lineLen - 10; $i += 2) { + if (defined $nfsDumpLine{$l[$i]}) { + $reseen = 1; + $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1]; + } + else { + $nfsDumpLine{$l[$i]} = $l[$i + 1]; + } + } + } + else { + $client_id = $l[1]; + $reseen = 0; + for ($i = 8; $i < $lineLen - 6; $i += 2) { + if (defined $nfsDumpLine{$l[$i]}) { + $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1]; + $reseen = 1; + } + else { + $nfsDumpLine{$l[$i]} = $l[$i + 1]; + } + } + } +} + +# Returns an empty list if anything goes wrong. Otherwise, returns a +# list of the protocol (eg R2, C2, R3, C3), the name of the operation, +# and the xid, the client host ID, and time, and the response status. +# (For call messages, the status is 'na'.) + +sub nfsDumpParseLineHeader { + my ($line) = @_; + + # If the line doesn't start with a digit, then it's certainly + # not properly formed, so bail out immediately. + + if (! ($line =~ /^[0-9]/)) { + return (); + } + else { + my $client_id; + my $status; + + my @l = split (' ', $line, 10); + + + if (($l[4] eq 'R3') || ($l[4] eq 'R2')) { + $client_id = $l[2]; + $status = $l[8]; + } + else { + $client_id = $l[1]; + $status = 'na'; + } + + return ($l[4], $l[7], $l[5], $client_id, $l[0], $status); + } +} + +# nfsDumpParseLineFields -- Just return a subset of the fields, +# without parsing the entire line. + +sub nfsDumpParseLineFields { + my ($line, @fields) = @_; + my $i; + + # If the line doesn't start with a digit, then + # it's certainly not properly formed, so bail out + # immediately. + + if (! ($line =~ /^[0-9]/)) { + return (); + } + + my $rest; + if ($AllowRisky) { + $rest = $line; + } + else { + my @foo = split (' ', $line, 9); + $rest = ' ' . $foo[8]; + } + + my $fl = @fields; + my @l = (); + for ($i = 0; $i < $fl; $i++) { + my $field = $fields[$i]; + + $rest =~ /\ $field\ +([^\ ]+)/; + $l[$i] = $1; + } + + return (@l); +} + +# nfsDumpParseLineField -- Just return ONE of the fields, +# without parsing the entire line. + +sub nfsDumpParseLineField { + my ($line, $field) = @_; + + # If the line doesn't start with a digit, then + # it's certainly not properly formed, so bail out + # immediately. + + if (! ($line =~ /^[0-9]/)) { + return undef; + } + + my $rest; + if ($AllowRisky) { + $rest = $line; + } + else { + my @foo = split (' ', $line, 9); + $rest = ' ' . $foo[8]; + } + + $rest =~ /\ $field\ +([^\ ]+)/; + return $1; +} + +# Returns a new file handle that has all the "useful" information as +# the original, but requires less storage space. File handles +# typically contain quite a bit of redundancy or unused bytes. +# +# This routine only knows about the advfs and netapp formats. If +# you're using anything else, just use anything else as the mode, and +# the original file handle will be returned. +# +# If you extend this to handle more file handles, please send the new +# code to me (ellard@eecs.harvard.edu) so I can add it to the +# distribution. + +sub nfsDumpCompressFH { + my ($mode, $fh) = @_; + + if ($mode eq 'advfs') { + + # The fh is a long hex string: + # 8 chars: file system ID + # 8 chars: apparently unused. + # 8 chars: unused. + # 8 chars: inode + # 8 chars: generation + # rest of string: mount point (not interesting). + # So all we do is pluck out the fsid, inode, + # and generation number, and throw the rest away. + + $fh =~ /^(........)(........)(........)(........)(........)/; + + return ("$1-$4-$5"); + } + elsif ($mode eq 'netapp') { + + # Here's the netapp format (from Shane Owara): + # + # 4 bytes mount point file inode number + # 4 bytes mount point file generation number + # + # 2 bytes flags + # 1 byte snapshot id + # 1 byte unused + # + # 4 bytes file inode number + # 4 bytes file generation number + # 4 bytes volume identifier + # + # 4 bytes export point fileid + # 1 byte export point snapshot id + # 3 bytes export point snapshot generation number + # + # The only parts of this that are interesting are + # inode, generation, and volume identifier (and probably + # a lot of the bits of the volume identifier could be + # tossed, since we don't have many volumes...). + + $fh =~ /^(........)(........)(........)(........)(........)(........)(........)/; + + return ("$4-$5-$6-$1"); + } + elsif ($mode eq 'RFSNN') { + + # Here's the netapp format (from Shane Owara): + # + # 4 bytes mount point file inode number + # 4 bytes mount point file generation number + # + # 2 bytes flags + # 1 byte snapshot id + # 1 byte unused + # + # 4 bytes file inode number + # 4 bytes file generation number + # 4 bytes volume identifier + # + # 4 bytes export point fileid + # 1 byte export point snapshot id + # 3 bytes export point snapshot generation number + # + # The only parts of this that are interesting are + # inode, generation, and volume identifier (and probably + # a lot of the bits of the volume identifier could be + # tossed, since we don't have many volumes...). + + # 61890100575701002000000 0009ac710e9ea381 0d24400006189010057570100 + # 61890100575701002000000 0009ac70ed2ea381 0d24400006189010057570100 + # 61890100575701002000000 000479a1e008d782 0d24400006189010057570100 + # Ningning need only 24-39 (or 12-19 bytes) + + $fh =~ /^(........)(........)(........)(........)(........)(........)/; + + return ("$4$5"); + }else { + + return ($fh); + } +} + +sub testMain { + $lineNum = 0; + + while () { + $line = $_; + $lineNum++; + + &nfsDumpParseLine ($line); + } +} + +1; + +# end of nfsdump.pl diff --git a/TBBT/trace_init/nfsscan.pl b/TBBT/trace_init/nfsscan.pl new file mode 100755 index 0000000..fad5216 --- /dev/null +++ b/TBBT/trace_init/nfsscan.pl @@ -0,0 +1,682 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: nfsscan,v 1.18 2003/07/28 14:27:16 ellard Exp $ + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/nfsdump.pl"; +require "$ProgDir/userUtils.pl"; +require "$ProgDir/hier.pl"; +require "$ProgDir/counts.pl"; +require "$ProgDir/latency.pl"; +require "$ProgDir/key.pl"; +require "$ProgDir/common.pl"; + +use Getopt::Std; + +$INTERVAL = 5 * 60; # in seconds (5 minutes) + +%KeysSeen = (); + +@ADD_USERS = (); +@DEL_USERS = (); +@ADD_GROUPS = (); +@DEL_GROUPS = (); +@ADD_CLIENTS = (); +@DEL_CLIENTS = (); + +$DO_COUNTS = 1; +$DO_LATENCY = 0; +$DO_FILES = 0; +$DO_PATHS = 0; +$DO_SQUEEZE = 0; + +$FH_TYPE = 'unknown'; + +$END_TIME = -1; +$START_TIME = -1; +$NOW = -1; +$UseClient = 0; +$UseFH = 0; +$UseUID = 0; +$UseGID = 0; +$OMIT_ZEROS = 0; + +$OutFileBaseName = undef; + +$nextPruneTime = -1; +$PRUNE_INTERVAL = 1 * 60; # One minute. + +# &&& +# Is this really the right default set of operations? + +$DEF_OPLIST = 'read,write,lookup,getattr,access,create,remove'; +@OPLIST = ('TOTAL', 'INTERESTING', + split (/,/, $DEF_OPLIST)); +%OPARRAY = (); + +$Usage =<< "."; + +Usage: $0 [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 $INTERVAL 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. +. + + +main (); + +sub main { + + parseArgs (); + + if ($DO_COUNTS) { + counts::printTitle (*OUT_COUNTS); + } + + if ($DO_LATENCY) { + latency::printTitle (*OUT_LATENCY); + } + + counts::resetOpCounts (); + + my $cmdbuf = 'rm -f noattrdirdiscard noattrdir-root'; + system($cmdbuf); + + readTrace (); +} + +sub parseArgs { + + my $cmdline = "$0 " . join (' ', @ARGV); + + my $Options = "B:dfF:g:G:hlO:o:t:u:U:SR:Z"; + if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); + } + if (defined $opt_h) { + print $Usage; + exit (0); + } + + #RFS: neednot input arguments + $opt_o = "test"; + $opt_f = 1; + $opt_t = 0; + #$opt_F = 'RFSNN'; # advfs or netapp + + if (defined $opt_B) { + $UseClient = ($opt_B =~ /C/); + $UseFH = ($opt_B =~ /F/); + $UseUID = ($opt_B =~ /U/); + $UseGID = ($opt_B =~ /G/); + } + + if (defined $opt_o) { + $OutFileBaseName = $opt_o; + } + + if (defined $opt_O) { + if ($opt_O =~ /^\+(.*)/) { + @OPLIST = (@OPLIST, split (/,/, $1)); + } + else { + @OPLIST = ('TOTAL', 'INTERESTING', split (/,/, $opt_O)); + } + # Error checking? + } + + if (defined $opt_l) { + $DO_LATENCY = 1; + } + + if (defined $opt_t) { + if ($INTERVAL =~ /([0-9]*)([smhd])/) { + my $n = $1; + my $unit = $2; + + if ($unit eq 's') { + $INTERVAL = $opt_t; + } + elsif ($unit eq 'm') { + $INTERVAL = $opt_t * 60; + } + elsif ($unit eq 'h') { + $INTERVAL = $opt_t * 60 * 60; + } + elsif ($unit eq 'd') { + $INTERVAL = $opt_t * 24 * 60 * 60; + } + } + else { + $INTERVAL = $opt_t; + } + } + + $DO_PATHS = (defined $opt_d); + $DO_FILES = (defined $opt_f); + $DO_SQUEEZE = (defined $opt_S); + $OMIT_ZEROS = (defined $opt_Z); + + $TIME_ROUNDING = (defined $opt_R) ? $opt_R : 0; + + if (defined $opt_F) { + $FH_TYPE = $opt_F; + } + + if (defined $opt_c) { + @ADD_CLIENTS = split (/,/, $opt_c); + } + if (defined $opt_C) { + @DEL_CLIENTS = split (/,/, $opt_c); + } + + if (defined $opt_g) { + @ADD_GROUPS = groups2gids (split (/,/, $opt_g)); + } + if (defined $opt_G) { + @DEL_GROUPS = groups2gids (split (/,/, $opt_G)); + } + + if (defined $opt_u) { + @ADD_USERS = logins2uids (split (/,/, $opt_u)); + } + if (defined $opt_U) { + @DEL_USERS = logins2uids (split (/,/, $opt_U)); + } + + + # Now that we know what options the user asked for, initialize + # things accordingly. + + if ($DO_PATHS || $DO_FILES) { + $UseFH = 1; + } + + if ($DO_LATENCY) { + latency::init (@OPLIST); + } + + if ($DO_COUNTS) { + counts::init (@OPLIST); + } + + if (defined $OutFileBaseName) { + if ($DO_COUNTS) { + open (OUT_COUNTS, ">$OutFileBaseName.cnt") || + die "Can't create $OutFileBaseName.cnt."; + print OUT_COUNTS "#cmdline $cmdline\n"; + } + if ($DO_LATENCY) { + open (OUT_LATENCY, ">$OutFileBaseName.lat") || + die "Can't create $OutFileBaseName.lat."; + print OUT_LATENCY "#cmdline $cmdline\n"; + } + if ($DO_FILES) { + open (OUT_FILES, ">$OutFileBaseName.fil") || + die "Can't create $OutFileBaseName.fil."; + print OUT_FILES "#cmdline $cmdline\n"; + } + if ($DO_PATHS) { + open (OUT_PATHS, ">$OutFileBaseName.pat") || + die "Can't create $OutFileBaseName.pat."; + print OUT_PATHS "#cmdline $cmdline\n"; + } + } + else { + *OUT_COUNTS = STDOUT; + *OUT_LATENCY = STDOUT; + *OUT_FILES = STDOUT; + *OUT_PATHS = STDOUT; + + print STDOUT "#cmdline $cmdline\n"; + } + + foreach my $op ( @OPLIST ) { + $OPARRAY{$op} = 1; + } + + return ; +} + +sub readTrace { + my (@args) = @_; + + while (my $line = <>) { + + $hier::rfsLineNum++; + if ( ($hier::rfsLineNum % 1000) eq 0) { + print STDERR "$hier::rfsLineNum\n"; + } + + + if ($line =~ /SHORT\ PACKET/) { + next; + } + + my ($proto, $op, $xid, $client, $now, $response) = + nfsd::nfsDumpParseLineHeader ($line); + $NOW = $now; + + # NOTE: This next bit of logic requires a little + # extra attention. We want to discard lines as + # quickly as we can if they're not "interesting". + # However, different lines are interesting in + # different contexts, so the order of the tests and + # the manner in which they are interspersed with + # subroutine calls to pluck info from the lines is + # very important. + + # Check whether it is a line that we should prune and + # ignore, because of the filters. + + next if (($op eq 'C3' || $op eq 'C2') && + ! pruneCall ($line, $client)); + + if ($DO_PATHS || $DO_FILES) { + hier::processLine ($line, + $proto, $op, $xid, $client, + $now, $response, $FH_TYPE); + } + + my $key = key::makeKey ($line, $proto, $op, + $xid, $client, $now, + $UseClient, $UseFH, $UseUID, $UseGID, + $FH_TYPE); + if (! defined $key) { + next ; + } + $KeysSeen{$key} = 1; + + # Count everything towards the total, but only + # do the rest of the processing for things + # that are "interesting". + + if ($proto eq 'C3' || $proto eq 'C2') { + $counts::OpCounts{"$key,TOTAL"}++; + $counts::KeysSeen{$key} = 1; + + next if (! exists $OPARRAY{$op}); + + $counts::OpCounts{"$key,$op"}++; + $counts::OpCounts{"$key,INTERESTING"}++; + } + + if ($op eq 'read' && exists $OPARRAY{'readM'}) { + doReadSize ($line, $proto, $op, $key, $client, $xid, $response, $now); + } + + if ($op eq 'write' && exists $OPARRAY{'writeM'}) { + doWriteSize ($line, $proto, $op, $key, $client, $xid, $response, $now); + } + + if ($DO_LATENCY) { + latency::update ($key, $proto, $op, + $xid, $client, $now); + } + + if ($END_TIME < 0) { + $START_TIME = findStartTime ($NOW, $TIME_ROUNDING); + $END_TIME = $START_TIME + $INTERVAL; + } + + # Note that this is a loop, because if the interval is + # short enough, or the system is very idle (or there's + # a filter in place that makes it look idle), entire + # intervals can go by without anything happening at + # all. Some tools can get confused if intervals are + # missing from the table, so we emit them anyway. + + while (($INTERVAL > 0) && ($NOW >= $END_TIME)) { + printAll ($START_TIME); + + counts::resetOpCounts (); + latency::resetOpCounts (); + + $START_TIME += $INTERVAL; + $END_TIME = $START_TIME + $INTERVAL; + } + + if ($now > $nextPruneTime) { + key::prunePending ($now - $PRUNE_INTERVAL); + latency::prunePending ($now - $PRUNE_INTERVAL); + + prunePending ($now - $PRUNE_INTERVAL); + + $nextPruneTime = $now + $PRUNE_INTERVAL; + } + } + + # Squeeze out the last little bit, if there's anything that we + # counted but did not emit. If DO_SQUEEZE is true, then + # always do this. Otherwise, only squeeze out the results of + # the last interval if the interval is "almost" complete (ie + # within 10 seconds of the end). + + if (($NOW > $START_TIME) && ($DO_SQUEEZE || (($END_TIME - $NOW) < 10))) { + printAll ($START_TIME); + counts::resetOpCounts (); + } + + print "#T endtime = $NOW\n"; + +} + +sub printAll { + my ($start_time) = @_; + + if ($DO_COUNTS) { + counts::printOps ($start_time, *OUT_COUNTS); + } + + if ($DO_LATENCY) { + latency::printOps ($start_time, *OUT_LATENCY); + } + + if ($DO_FILES) { + hier::printAll ($start_time, *OUT_FILES); + } + + if ($DO_PATHS) { + printPaths ($start_time, *OUT_PATHS); + } +} + +sub pruneCall { + my ($line, $client) = @_; + + if (@ADD_USERS > 0 || @DEL_USERS > 0) { + my $c_uid = nfsd::nfsDumpParseLineField ($line, 'euid'); + if (! defined ($c_uid)) { + return 0; + } + $c_uid = hex ($c_uid); + + if (@ADD_USERS && !grep (/^$c_uid$/, @ADD_USERS)) { + return 0; + } + if (@DEL_USERS && grep (/^$c_uid$/, @DEL_USERS)) { + return 0; + } + } + + if (@ADD_GROUPS > 0 || @DEL_GROUPS > 0) { + my $c_gid = nfsd::nfsDumpParseLineField ($line, 'egid'); + if (! defined ($c_gid)) { + return 0; + } + $c_gid = hex ($c_gid); + + if (@ADD_GROUPS && !grep (/^$c_gid$/, @ADD_GROUPS)) { + return 0; + } + if (@DEL_GROUPS && grep (/^$c_gid$/, @DEL_GROUPS)) { + return 0; + } + } + + if (@ADD_CLIENTS > 0 || @DEL_CLIENTS > 0) { + if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) { + return 0; + } + if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) { + return 0; + } + } + + return 1; +} + +%PathOpCounts = (); +%PathsSeen = (); + +sub buildDirPath { + my ($fh, $key) = @_; + my $pfh; + my $cnt; + + foreach my $op ( @OPLIST ) { + if (exists $counts::OpCounts{"$key,$op"}) { + $cnt = $counts::OpCounts{"$key,$op"}; + } + else { + $cnt = 0; + } + $PathOpCounts{"$fh,$op"} = $cnt; + + $PathsSeen{$fh} = 1; + + $pfh = $fh; + my $len = 0; + while (defined ($pfh = $hier::fh2Parent{$pfh})) { + + if ($len++ > 20) { + print "Really long path ($fh)\n"; + last; + } + + if (exists $PathOpCounts{"$pfh,$op"}) { + $PathOpCounts{"$pfh,$op"} += $cnt; + } + else { + $PathOpCounts{"$pfh,$op"} = $cnt; + } + $PathsSeen{$pfh} = 1; + } + } + + return ; +} + +sub printPaths { + my ($start_time, $out) = @_; + + my $str = "#D time Dir/File dircnt path fh"; + foreach my $op ( @OPLIST ) { + $str .= " $op"; + } + $str .= "\n"; + + print $out $str; + + undef %PathsSeen; + + foreach my $key ( keys %KeysSeen ) { + my ($client_id, $fh, $euid, $egid) = split (/,/, $key); + + buildDirPath ($fh, $key); + } + + foreach my $fh ( keys %PathsSeen ) { + my ($path, $cnt) = hier::findPath ($fh); + + if ($cnt == 0) { + $path = "."; + } + + my $type = (exists $hier::fhIsDir{$fh} && $hier::fhIsDir{$fh}==2) ? 'D' : 'F'; + + my $str = "$cnt $type $path $fh "; + + foreach my $op ( @OPLIST ) { + my $cnt; + + if (exists $PathOpCounts{"$fh,$op"}) { + $cnt = $PathOpCounts{"$fh,$op"}; + } + else { + print "Missing $fh $op\n"; + $cnt = 0; + } + + $str .= " $cnt"; + + $PathOpCounts{"$fh,$op"} = 0; # &&& reset + } + + print $out "D $start_time $str\n"; + } +} + +%uxid2key = (); +%uxid2time = (); + +sub doReadSize { + my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_; + + my $uxid = "$client-$xid"; + + if ($proto eq 'C3' || $proto eq 'C2') { + $uxid2time{$uxid} = $time; + $uxid2key{$uxid} = $key; + } + else { + if (! exists $uxid2key{$uxid}) { + return ; + } + if ($response ne 'OK') { + return ; + } + + $key = $uxid2key{$uxid}; + my $count = nfsd::nfsDumpParseLineField ($line, 'count'); + $count = hex ($count); + + delete $uxid2key{$uxid}; + delete $uxid2time{$uxid}; + + $counts::OpCounts{"$key,readM"} += $count; + } +} + +# Note that we always just assume that writes succeed, because on most +# systems they virtually always do. If you're tracing a system where +# your users are constantly filling up the disk or exceeding their +# quotas, then you will need to fix this. + +sub doWriteSize { + my ($line, $proto, $op, $key, $client, $xid, $response, $time) = @_; + + if ($proto eq 'C3' || $proto eq 'C2') { + + my $tag = ($proto eq 'C3') ? 'count' : 'tcount'; + + my $count = nfsd::nfsDumpParseLineField ($line, $tag); + + if (! $count) { + printf "WEIRD count $line\n"; + } + + $count = hex ($count); + + $counts::OpCounts{"$key,writeM"} += $count; + } +} + + +# Purge all the pending XID records dated earlier than $when (which is +# typically at least $PRUNE_INTERVAL seconds ago). This is important +# because otherwise missing XID records can pile up, eating a lot of +# memory. + +sub prunePending { + my ($when) = @_; + + foreach my $uxid ( keys %uxid2time ) { + if ($uxid2time{$uxid} < $when) { + delete $uxid2key{$uxid}; + delete $uxid2time{$uxid}; + } + } + + return ; +} + diff --git a/TBBT/trace_init/nfsscan.txt b/TBBT/trace_init/nfsscan.txt new file mode 100755 index 0000000..b004339 --- /dev/null +++ b/TBBT/trace_init/nfsscan.txt @@ -0,0 +1,230 @@ +# 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 + +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 . Note that + this is NOT the same as the total of all operations! For + example, if you set 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. + + + - 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 . Note that + this is NOT the same as the total of all operations! For + example, if you set 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. diff --git a/TBBT/trace_init/ns_loc2gmt.pl b/TBBT/trace_init/ns_loc2gmt.pl new file mode 100755 index 0000000..815f68f --- /dev/null +++ b/TBBT/trace_init/ns_loc2gmt.pl @@ -0,0 +1,108 @@ +#!/usr/bin/perl +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_loc2gmt,v 1.6 2003/07/28 14:27:16 ellard Exp $ +# +# A helper application used by ns_quickview to convert from local time +# to GMT. nfsdump records time as localtime, but gnuplot only know +# how to deal with dates expressed as Greenwich Mean Time (which it +# then displays using the local time, for some reason). +# +# This is a fragile tool -- it must be run in the same time zone in +# which the data was collected via nfsdump, or else it will not do the +# proper conversion. Improvements welcomed! +# +# The 'C' (count) and 'L' (latency) records use the second column for +# dates, expressed as seconds.microseconds in localtime. The seconds +# portion is the only part of the data modified by this program. +# Comment lines are passed through unaltered. +# +# There is no error checking. Garbage in, garbage out. +# +# Note - we're throwing the microseconds away. + + +use Getopt::Std; +require 'timelocal.pl'; + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-d secs Time offset, in seconds, to subtract from each time + in the input tables. Note that all times are rounded + down to the nearest second. + +IMPORTANT NOTE: this must be run in the same time zone in which the +data was collected via nfsdump, or else it will not do the proper +conversion unless the -d option is used. + +. + +$Options = "d:"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +$TimeOffset = 0; +if (defined $opt_d) { + $TimeOffset = int ($opt_d); +} + +while ($line = <>) { + if ($line =~ /^#/) { + print $line; + next; + } + + @arr = split (' ', $line); + + ($secs, $usec) = split (/\./, $arr[1]); + $secs -= $TimeOffset; + + $arr[1] = &timegm (localtime ($secs)); + + print join (' ', @arr); + print "\n"; +} + +exit (0); + diff --git a/TBBT/trace_init/ns_quickview.pl b/TBBT/trace_init/ns_quickview.pl new file mode 100755 index 0000000..1460235 --- /dev/null +++ b/TBBT/trace_init/ns_quickview.pl @@ -0,0 +1,245 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_quickview,v 1.9 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +$GNUPLOT_PATH = "gnuplot"; +$GNUPLOT_OPTS = "-persist"; +$LOC2GMT_PATH = "$ProgDir/ns_loc2gmt"; + +$OUTPUT = "qv.ps"; +$GP_OUTPUT = "qv.gp"; +$TERMINAL = "postscript"; +$SIZE = undef; +$EXTRA_CMD = undef; +$TITLE_TEXT = undef; +$PLOT_OPTS = ''; +@OPS = ( 7 ); +@SCALES = (); + +$Usage =<< "."; + +Usage: $0 [options] table1.ns [table2.ns ...] + +At least one table file must be specified. + +Command line options: + +-h Print usage message and exit. + +-c cmd Pass an extra command directly to gnuplot. + +-g Save the gnuplot commands to $GP_OUTPUT instead + of executing the immediately. + +-k Show the key for each line on the plot. The default + is not to show the key. + +-l If more than one input file is specified, superimpose + the plot for each so that they all appear to begin at + the same time as the first row in the first file. + +-o filename Save the plot to the specified file. The default + output file is \"$OUTPUT\". + +-O op1,...,opN Plot the specified columns from the table. The + default is $OPS[0], the total operation count. + Note that the operations must be specified by column + number, not by name. + +-t terminal Create a plot for the given gnuplot terminal specification. + The default is \"$TERMINAL\". See the gnuplot + documentation for more information. + +-T string Use the specified string as the title for the plot. + The default is no title. + +-s sizespec Set the output size specifier. See the gnuplot + documentation for more information. + +-S s1,..,sN Set the scaling factor for each file. This is useful + for plotting files aggregated across different time + intervals on the same scale. + +. + +$Options = "c:ghklo:O:P:t:T:s:S:"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +$EXTRA_CMD = $opt_c if (defined $opt_c); +$OUTPUT = $opt_o if (defined $opt_o); +$TERMINAL = $opt_t if (defined $opt_t); +$SIZE = $opt_s if (defined $opt_s); +$TITLE_TEXT = $opt_T if (defined $opt_T); +$PLOT_OPTS = $opt_P if (defined $opt_P); +@OPS = split (/,/, $opt_O) if (defined $opt_O); +@SCALES = split (/,/, $opt_S) if (defined $opt_S); + +$SAVE_GNUPLOT = defined $opt_g; +$SHOW_KEY = defined $opt_k; +$LAYER = defined $opt_l; + +@FILES = @ARGV; + +if (@FILES == 0) { + print STDERR "No tables to plot?\n"; + exit (1); +} + +&makePlot; +exit (0); + +sub makePlot { + my @starts = (); + my @ends = (); + + my $nfiles = @FILES; + my $nops = @OPS; + + if ($SAVE_GNUPLOT) { + die unless open (PLOT, ">$GP_OUTPUT"); + } + else { + die unless open (PLOT, "|$GNUPLOT_PATH $GNUPLOT_OPTS"); + } + + $preamble = `cat $ProgDir/template.gp`; + print PLOT $preamble; + + print PLOT "set terminal $TERMINAL\n"; + print PLOT "set output \'$OUTPUT\'\n"; + if (defined $SIZE) { + print PLOT "set size $SIZE\n"; + } + + if (defined $TITLE_TEXT) { + print PLOT "set title \"$TITLE_TEXT\"\n"; + } + + if (defined $EXTRA_CMD) { + print PLOT "$EXTRA_CMD\n"; + } + + print PLOT "set key below\n"; + + if ($LAYER) { + for ($i = 0; $i < $nfiles; $i++) { + ($starts [$i], $ends [$i]) = + findFileTimeSpan ($FILES [$i]); + } + } + + print PLOT "plot $PLOT_OPTS \\\n"; + for ($i = 0; $i < $nfiles; $i++) { + my $offset = 0; + my $scale = 1; + if (defined $SCALES[$i]) { + $scale = $SCALES[$i]; + } + + my $file = $FILES [$i]; + + if ($LAYER) { + $offset = $starts [$i] - $starts [0]; + } + + for ($j = 0; $j < @OPS; $j++) { + my $op = $OPS[$j]; + + print PLOT "\t\"< $LOC2GMT_PATH -d $offset $file\" \\\n"; + print PLOT "\t\tusing 2:(\$$op*$scale) \\\n"; + if ($SHOW_KEY) { + print PLOT "\t\ttitle \'$file:$op\'\\\n"; + } + print PLOT "\t\twith lines lw 2"; + if ($i != $nfiles - 1 || $j != $nops - 1) { + print PLOT ",\\\n"; + } + else { + print PLOT "\n"; + } + } + } + print PLOT "\n"; + close PLOT; +} + +# Find the earliest and latest times in a file created by nfsscan, and +# return them as a list. Returns the empty list if anything goes +# wrong. + +sub findFileTimeSpan { + my ($fname) = @_; + my ($smallest, $largest) = (-1, -1); + + if (! open (FF, "<$fname")) { + return (); + } + + while (my $l = ) { + if ($l =~ /^#/) { + next; + } + + @a = split (' ', $l); + + if ($smallest < 0 || $smallest > $a[1]) { + $smallest = $a[1]; + } + if ($largest < 0 || $largest < $a[1]) { + $largest = $a[1]; + } + } + + close FF; + + if ((! defined $smallest) || (! defined $largest)) { + return (); + } + + return ($smallest, $largest); +} + diff --git a/TBBT/trace_init/ns_split.pl b/TBBT/trace_init/ns_split.pl new file mode 100755 index 0000000..b600448 --- /dev/null +++ b/TBBT/trace_init/ns_split.pl @@ -0,0 +1,153 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_split,v 1.4 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/userUtils.pl"; + +@ADD_USERS = (); +@DEL_USERS = (); +@ADD_GROUPS = (); +@DEL_GROUPS = (); +@ADD_CLIENTS = (); +@DEL_CLIENTS = (); + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-c c1[,c2]* Include only activity performed by the specified clients. + +-C c1[,c2]* Exclude activity performed by the specified clients. + +-g g1[,g2]* Include only activity performed by the specified groups. + +-G g1[,g2]* Exclude activity performed by the specified groups. + +-u u1[,u2]* Include only activity performed by the specified users. + +-U u1[,u2]* Exclude activity performed by the specified users. + +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "c:C:g:G:u:U::"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; +exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +if (defined $opt_u) { + @ADD_USERS = &logins2uids ($opt_u); +} +if (defined $opt_U) { + @DEL_USERS = &logins2uids ($opt_U); +} +if (defined $opt_g) { + @ADD_GROUPS = &groups2gids ($opt_g); +} +if (defined $opt_G) { + @DEL_GROUPS = &groups2gids ($opt_G); +} +if (defined $opt_c) { + @ADD_CLIENTS = split (/,/, $opt_c); +} +if (defined $opt_C) { + @DEL_CLIENTS = split (/,/, $opt_C); +} + +print "#cmdline $cmdline\n"; + +while ($l = <>) { + if ($l =~ /^#/) { + print $l; + next; + } + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (saveRecord ($client, $fh, $euid, $egid)) { + print $l; + } +} + +sub saveRecord { + my ($client, $fh, $euid, $egid) = @_; + + if (@ADD_CLIENTS && !grep (/^$client$/, @ADD_CLIENTS)) { + return 0; + } + if (@DEL_CLIENTS && grep (/^$client$/, @DEL_CLIENTS)) { + return 0; + } + + if (@ADD_USERS && !grep (/^$euid$/, @ADD_USERS)) { + return 0; + } + if (@DEL_USERS && grep (/^$euid$/, @DEL_USERS)) { + return 0; + } + + if (@ADD_GROUPS && !grep (/^$egid$/, @ADD_GROUPS)) { + return 0; + } + if (@DEL_GROUPS && grep (/^$egid$/, @DEL_GROUPS)) { + return 0; + } + + return (1); +} + +exit 0; diff --git a/TBBT/trace_init/ns_timeagg.pl b/TBBT/trace_init/ns_timeagg.pl new file mode 100755 index 0000000..9e5e536 --- /dev/null +++ b/TBBT/trace_init/ns_timeagg.pl @@ -0,0 +1,218 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_timeagg,v 1.7 2003/07/28 14:27:17 ellard Exp $ + +use Getopt::Std; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +require "$ProgDir/common.pl"; + +$UseClient = 0; +$UseFH = 0; +$UseUID = 0; +$UseGID = 0; +$NEW_INTERVAL = 300; +$OMIT_ZEROS = 0; + +$Usage =<< "."; + +Usage: $0 [options] [table1.ns [table2.ns ... ]] + +If no table files are specified, then the input is read from stdin. + +Command line options: + +-h Print usage message and exit. + +-B flags Choose fields to NOT aggregate. The fields are: + C - client host ID + U - Effective user ID + G - Effective group ID + F - File handle + +-R secs Round the start time to the closest multiple of + the given number of seconds. This is useful for + dealing with small amounts of clock drift. For example, + if the trace starts at 12:00:01 instead of 12:00:00, + it is probably convenient to pretend that it started + at 12:00:00. + +-t secs Aggregate over the given number of seconds. The default + is $NEW_INTERVAL. If the current interval does not + divide the new interval, warning messages are printed + and the output might not be meaningful. + + If secs is 0, then the total for the entire table is + computed. + +-Z Omit count lines that have a zero total operation count. +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "B:hR:t:Z"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +if (defined $opt_t) { + if ($opt_t <= 0) { + $EndTime = 0; + } + $NEW_INTERVAL = $opt_t; +} + +$TIME_ROUNDING = defined $opt_R ? $opt_R : 0; + +if (defined $opt_B) { + $UseClient = ($opt_B =~ /C/); + $UseUID = ($opt_B =~ /U/); + $UseGID = ($opt_B =~ /G/); + $UseFH = ($opt_B =~ /F/); +} + +# We need to let the measurement of time be a little bit inexact, so +# that a small rounding error or truncation doesn't throw everything +# off. + +$TimerInacc = 0.05; + +# Print out the commandline as a comment in the output. + +print "#cmdline $cmdline\n"; + +# It would be nice to make this do the right thing with Latency lines, +# and perhaps file op counts. + +while ($l = <>) { + if ($l =~ /^#C/) { + print $l; + next; + } + + next if ($l =~ /^#/); + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (!defined $StartTime) { + $StartTime = findStartTime ($time, $TIME_ROUNDING); + if ($NEW_INTERVAL > 0) { + $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc; + } + } + + if ($EndTime > 0 && $time >= $EndTime) { + my $diff = int ($time - $StartTime); + if ($diff > 0) { + if ($NEW_INTERVAL % $diff != 0) { + print STDERR "$0: time interval mismatch "; + print STDERR "(from $diff to $NEW_INTERVAL)\n"; + } + } + + dumpKeys ($StartTime); + + $StartTime += $NEW_INTERVAL; + $EndTime = $StartTime + $NEW_INTERVAL - $TimerInacc; + } + + + # Aggregate across clients, files, users, and groups unless + # specifically asked NOT to. + + $client = $UseClient ? $client : 'u'; + $fh = $UseFH ? $fh : 'u'; + $euid = $UseUID ? $euid : 'u'; + $egid = $UseGID ? $egid : 'u'; + + $key = "$client,$fh,$euid,$egid"; + + if (exists $Totals{$key}) { + my (@tots) = split (' ', $Totals{$key}); + for (my $i = 0; $i < @vals; $i++) { + $tots[$i] += $vals[$i]; + } + $Totals{$key} = join (' ', @tots); + } + else { + $Totals{$key} = join (' ', @vals); + } +} + +if ($EndTime <= 0) { + dumpKeys ($StartTime); +} + +sub dumpKeys { + my ($start) = @_; + my ($i, $k, $ks); + + foreach $k ( keys %Totals ) { + my (@v) = split (' ', $Totals{$k}); + + if ($OMIT_ZEROS && $v[0] == 0) { + next; + } + + $ks = $k; + $ks =~ s/,/\ /g; + + print "C $start $ks"; + + for ($i = 0; $i < @v; $i++) { + print " $v[$i]"; + } + print "\n"; + + for ($i = 0; $i < @v; $i++) { + $v[$i] = 0; + } + $Totals{$k} = join (' ', @v); + } +} + +exit 0; diff --git a/TBBT/trace_init/ns_tsplit.pl b/TBBT/trace_init/ns_tsplit.pl new file mode 100755 index 0000000..e3dd420 --- /dev/null +++ b/TBBT/trace_init/ns_tsplit.pl @@ -0,0 +1,219 @@ +#!/usr/bin/perl -w +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: ns_tsplit,v 1.4 2003/07/26 20:52:04 ellard Exp $ + +use Getopt::Std; +use Time::Local; + +$ProgDir = $0; +$ProgDir =~ /(^.*)\//; +$ProgDir = $1; +if (!$ProgDir) { + $ProgDir = "."; +} + +# Set the default start and end times. + +$DEF_BEGIN_TIME = "20000101:00:00:00"; +$DEF_END_TIME = "20360101:00:00:00"; + +# The range includes everything from the start time up to and less +# than the end time (so if you want one day, it is correct to go +# midnight to midnight, instead of going until 23:59:59). + +$Usage =<< "."; + +Usage: $0 [options] rangeSpec... - [table.ns [table.ns...]] + +Command line options: + +-h Print usage message and exit. + +Any number of time range specifier can be provided. The special +symbol "-" is used to separated the time specifiers from the input +file names. If input is taken from stdin, then the "-" may be +omitted. + +The basic time range specification format is: StartTime-EndTime where +StartTime and EndTime have the form YYMMDD[:HH[:MM[:SS]]]. All +records from the input that have a timestamp greater than or equal to +StartTime and less than EndTime are printed to stdout. + +If StartTime is omitted, $DEF_BEGIN_TIME is used. +If EndTime is omitted, $DEF_END_TIME is used. + +Note that omitting both the StartTime and EndTime results in a rangeSpec +of "-", which is the special symbol that marks the end of the rangeSpecs. +. + +$cmdline = "$0 " . join (' ', @ARGV); +$Options = "h"; +if (! getopts ($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); +} +if (defined $opt_h) { + print $Usage; + exit (0); +} + +@StartTimes = (); +@EndTimes = (); + +while ($ts = shift @ARGV) { + last if ($ts eq '-'); + + my ($ts_s, $ts_e) = split (/-/, $ts); + my $s; + + if ($ts_s eq '') { + $ts_s = $DEF_START_TIME; + } + $s = &ts2secs ($ts_s); + if (! defined $s) { + print STDERR "Failed to translate ($ts_s)\n"; + exit (1); + } + push @StartTimes, $s; + + if ($ts_e eq '') { + $ts_e = $DEF_END_TIME; + } + $s = &ts2secs ($ts_e); + if (! defined $s) { + print STDERR "Failed to translate ($ts_e)\n"; + exit (1); + } + + push @EndTimes, $s; +} + +print "#cmdline $cmdline\n"; + +while ($l = <>) { + if ($l =~ /^#/) { + print $l; + next; + } + + my ($type, $time, $client, $fh, $euid, $egid, @vals) = split (' ', $l); + + next unless ($type eq 'C'); + + # Something wrong with the input? + + next if (@vals == 0); + + if (saveRecord ($time)) { + print $l; + } +} + +exit 0; + +sub saveRecord { + my ($t) = @_; + + for (my $i = 0; $i < @StartTimes ; $i++) { + if (($StartTimes[$i] <= $t) && ($t < $EndTimes[$i])) { + return (1); + } + } + + return (0); +} + +# This is ugly! +# +# The basic time specification format is: YYMMDD[:HH[:MM[:SS]]]. +# +# To make a time range, join two time specs with a -. +# +# The data spec is Y2.01K compliant... I'm assuming that all the +# traces processed by this tool will be gathered before 2010. If +# anyone is still gathering NFSv3 traces in 2010, I'm sorry. + +sub ts2secs { + my ($ts) = @_; + my ($hour, $min, $sec) = (0, 0, 0); + + my (@d) = split (/:/, $ts); + + if ($d[0] =~ /([0-9][0-9])([0-2][0-9])([0-3][0-9])/) { + $year = $1 + 100; + $mon = $2 - 1; + $day = $3; + } + else { + return undef; + } + + if (@d > 1) { + if ($d[1] =~ /([0-2][0-9])/) { + $hour = $1; + } + else { + return undef; + } + } + + if (@d > 2) { + if ($d[2] =~ /([0-5][0-9])/) { + $min = $1; + } + else { + return undef; + } + } + + if (@d > 3) { + if ($d[3] =~ /([0-5][0-9])/) { + $sec = $1; + } + else { + return undef; + } + } + + my $s = timelocal ($sec, $min, $hour, $day, $mon, $year, + undef, undef, undef); + + my ($s2, $m2, $h2, $md2, $mon2, $y2, $isdst) = localtime ($s); + if (($s2 != $sec) || $min != $m2 || $hour != $h2 || $day != $md2) { + print "failed 1!\n"; + } + if (($mon != $mon2) || $year != $y2) { + print "failed 2!\n"; + } + + return ($s); +} + diff --git a/TBBT/trace_init/rfs.pl b/TBBT/trace_init/rfs.pl new file mode 100755 index 0000000..c167df7 --- /dev/null +++ b/TBBT/trace_init/rfs.pl @@ -0,0 +1,243 @@ +#!/usr/bin/perl -w + +use Getopt::Std; +$Usage =<< "EOF"; + +Usage: $0 [options] + +This program runs after nfsscan.pl. It outputs the file system hierarchy (RFSFS) as well as +the file system hierarchy map (stored in three files: fh-path-map, noattrdirdiscard, noattrdir-root). + +This perl program do post processing based on this file + +Command line options: + +-h Print Usage message and exit. + +-S The file in file system hierarchy are all of size 0. + Without -S, the files are written to it full length. + + +EOF + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + +main (); + +sub rfsCreateSymlink() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + my $pathnamebuf = $sizeHexStr; + symlink ($pathnamebuf, $path); + #if (! symlink ($pathnamebuf, $path) ) { + # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n"; + #} +} + + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + # the eight lines below should not be commented out + if (!defined $opt_S) { + my $i = 0; + for ($i = 0; $i < $lp; $i++) { + syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + } + if ($remSize) { + syswrite(RFSTMPWRITE, $writebuf, $remSize); + #print "write ($remSize) byte\n"; + } + close RFSTMPWRITE; + } +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# *** WARNING *** The locale specified by the environment affects sort +# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native +# byte values. + +sub parseArgs { + my $Options = "S"; + if (! getopts($Options)) { + print STDERR "$0: Incorrect usage.\n"; + print STDERR $Usage; + exit (1); + } +} + +sub main { + + parseArgs (); + + my $cmdbuf; + + # format of lines in test.fil: + # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr() + # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14)) + # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22)) + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the test.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e) + $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all'; + print "$cmdbuf\n"; + system $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + + # format output files + $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $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'; + print "$cmdbuf\n"; + system $cmdbuf; + + #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh'; + $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = "cut -d ' ' -f '1 2' fh-path-map > fh-path-map-tmp"; + print "$cmdbuf\n"; + system $cmdbuf; + + $cmdbuf = 'cat noattrdirdiscard noattrdir-root fh-path-map-tmp > fh-path-map-play; rm -rf fh-path-map-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active" || die "open active failed\n"; + while () { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type==2) { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + }elsif ($type==1){ + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + }elsif ($type==5){ + #print STDERR "SYMLINK: $type fh: $fh path: $path\n"; + &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr); + }else { + print STDERR "special file: $type fh: $fh path: $path\n"; + } + + my $line_num=0; + $line_num++; + if ( ($line_num %100)==0 ) { + print STDERR "$line_num\n"; + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/rfs.pl.old b/TBBT/trace_init/rfs.pl.old new file mode 100755 index 0000000..550f2c8 --- /dev/null +++ b/TBBT/trace_init/rfs.pl.old @@ -0,0 +1,138 @@ +#!/usr/bin/perl -w + +$Usage =<< "."; + +This is the rfs program + +After run nfsscan, you will get a file-list file in the output + +This perl program do post processing based on this file + + +. + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + + +main (); + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + my $i = 0; + for ($i = 0; $i < $lp; $i++) { + syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + } + if ($remSize) { + syswrite(RFSTMPWRITE, $writebuf, $remSize); + #print "write ($remSize) byte\n"; + } + close RFSTMPWRITE; +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# + +sub main { + + my $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path_count + $cmdbuf = 'sort -k 5,5 active.fil > active.fil.order'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'sort -n -k 6,6 active.fil > active.fil.order-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order-pathcnt > active-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5 }\' active.fil.order-pathcnt > fh-path-map-pathcnt'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active"; + while () { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type eq "D") { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + } + else { + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/rfs.stub.pl b/TBBT/trace_init/rfs.stub.pl new file mode 100755 index 0000000..a0de62a --- /dev/null +++ b/TBBT/trace_init/rfs.stub.pl @@ -0,0 +1,213 @@ +#!/usr/bin/perl -w + +$Usage =<< "."; + +This is the rfs program + +After run nfsscan, you will get a file-list file in the output + +This perl program do post processing based on this file + + +. + +$writebuf = "R" x 8193; +$writeBlockSize = 8192; + +main (); + +sub rfsCreateSymlink() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + my $pathnamebuf = $sizeHexStr; + symlink ($pathnamebuf, $path); + #if (! symlink ($pathnamebuf, $path) ) { + # print STDERR "JIAWU: WARNING: failed to create symlink: $path -> $pathnamebuf\n"; + #} +} + + +sub rfsCreateFile() +{ + my ($path, $pathcnt, $sizeHexStr) = @_; + $sizeHexStr = "0x".$sizeHexStr; + + my $dir = $path; + my $name = ''; + my $cmdbuf; + + $dir =~ /(^.*)\/(.*)/; + $dir = $1; + $name = $2; + if (!$dir) {$dir = ".";} + die "name is empty\n" if (!$name); + #print "path($path) dir($dir) name($name)\n"; + + if (! -e $dir) { + $cmdbuf = "mkdir -p $dir"; + system $cmdbuf; + print "RFS: Warning: the directory should be created already: $path\n"; + } else { + die "warning: directory name exist but not a directory: $path\n" if (!(-d $dir)); + } + + my $size = hex($sizeHexStr); + my $remSize = $size % $writeBlockSize; + my $lp = ($size - $remSize) / $writeBlockSize; + #print "size($sizeHexStr) lp($lp) rem($remSize)\n"; + + open RFSTMPWRITE, ">$path" || die "RFS: can not open file for write"; + + # the eight lines below should not be commented out + #my $i = 0; + #for ($i = 0; $i < $lp; $i++) { + # syswrite(RFSTMPWRITE, $writebuf, $writeBlockSize); + #} + #if ($remSize) { + # syswrite(RFSTMPWRITE, $writebuf, $remSize); + # #print "write ($remSize) byte\n"; + #} + close RFSTMPWRITE; +} + +# Useful commands: +# sort -n -k a,b -c -u +# +# *** WARNING *** The locale specified by the environment affects sort +# order. Set LC_ALL=C or LC_ALL=POSIX to get the traditional sort order that uses native +# byte values. + +sub main { + + my $cmdbuf; + + # format of lines in test.fil: + # "F(1) $type(2) $state(3) $fh(4) $path(5) $pathcnt(6) $attrOrig(7-14) $attr() + # attrOrig: ($size(7) $mode(8) $op(9) $atime(10) $mtime(11) $ctime(12) $nlink(13) $ts(14)) + # attrLast: ($size(15) $mode(16) $op(17) $atime(18) $mtime(19) $ctime(20) $nlink(21) $ts(22)) + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0") print $0 }\' test.fil > all.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the test.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 all.fil > all.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 15(size_last) 21(nlink_last) 14(ts_s) 22(ts_e) + $cmdbuf = 'gawk \' { print $14, $22, $4, $5, $15, $21}\' all.fil.order > fh-path-map-all'; + print "$cmdbuf\n"; + system $cmdbuf; + + # skip comment lines + # if (path_count ($6) == 0 or original_op($9) == "create" or "mkdir" or "symlink") skip the file + + $cmdbuf = 'gawk \' !/^[#]/ { if ($6 != "0" && $9 != "create" && $9 != "mkdir" && $9 != "symlink") print $0 }\' test.fil > active.fil'; + print "$cmdbuf\n"; + system $cmdbuf; + + # sort the active.fil according to path name + $cmdbuf = 'export LC_ALL=C; sort -k 5,5 active.fil > active.fil.order; echo $LC_ALL'; + print "$cmdbuf\n"; + system $cmdbuf; + + # keep the interested field only + # 2(D/F) 4(FH) 5(path) 6(count) 7(size) 8(mode) 13(nlink) + $cmdbuf = 'gawk \' { print $2, $4, $5, $6, $7, $8 }\' active.fil.order > active'; + print "$cmdbuf\n"; + system $cmdbuf; + $cmdbuf = 'gawk \' { print $4, $5, $7, $13}\' active.fil.order > fh-path-map'; + print "$cmdbuf\n"; + system $cmdbuf; + + # format output files + $cmdbuf = 'export LC_ALL=C; mv noattrdirdiscard noattrdirdiscard-tmp; sort -u -k 1,1 noattrdirdiscard-tmp > noattrdirdiscard; echo $LC_ALL; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + $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'; + print "$cmdbuf\n"; + system $cmdbuf; + + #$cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp missdiscardfh'; + $cmdbuf = 'mv noattrdirdiscard noattrdirdiscard-tmp; cat noattrdirdiscard-tmp missdiscardfh >noattrdirdiscard; rm -f noattrdirdiscard-tmp'; + print "$cmdbuf\n"; + system $cmdbuf; + + #exit(0); + + # so far, you got the following information + # in active: all files/dirs active + # in noattrdir-root: a set of fhs pointing to RFSNN0 + # in rfsinfo: a set of dir fhs refer to RFSNNxxx(>0) + # a set of file fhs should be discard due to short of information + + # create the active fs + # 1. BASEDIR/RFSNN0 + # 2. BASEDIR/active + $cmdbuf = "mkdir -p RFSFS/RFSNN0"; + system $cmdbuf; + open RFS_ACTIVE, "active" || die "open active failed\n"; + while () { + chop; + my ($type, $fh, $path, $pathcnt, $sizeHexStr, $mode) = split (' ', $_, 7); + if ($type==2) { + $cmdbuf = "mkdir -p RFSFS$path"; + system $cmdbuf; + }elsif ($type==1){ + &rfsCreateFile("RFSFS$path", $pathcnt, $sizeHexStr); + }elsif ($type==5){ + #print STDERR "SYMLINK: $type fh: $fh path: $path\n"; + &rfsCreateSymlink("RFSFS$path", $pathcnt, $sizeHexStr); + }else { + print STDERR "special file: $type fh: $fh path: $path\n"; + } + + my $line_num=0; + $line_num++; + if ( ($line_num %100)==0 ) { + print STDERR "$line_num\n"; + } + + } + + # create map table: key (fh), value (path/fn) + + # check whether there is fh that is not mapped to any path/fn in the trace + + # simulate a replay of trace + + return; + +} + +1; diff --git a/TBBT/trace_init/test.cnt b/TBBT/trace_init/test.cnt new file mode 100644 index 0000000..e78f658 --- /dev/null +++ b/TBBT/trace_init/test.cnt @@ -0,0 +1,2 @@ +#cmdline nfsscan.pl +#C time client fh euid egid TOTAL INTERESTING read write lookup getattr access create remove diff --git a/TBBT/trace_init/test.fil b/TBBT/trace_init/test.fil new file mode 100755 index 0000000..429a873 --- /dev/null +++ b/TBBT/trace_init/test.fil @@ -0,0 +1 @@ +#cmdline nfsscan.pl diff --git a/TBBT/trace_init/userUtils.pl b/TBBT/trace_init/userUtils.pl new file mode 100755 index 0000000..4019d5d --- /dev/null +++ b/TBBT/trace_init/userUtils.pl @@ -0,0 +1,87 @@ +# +# Copyright (c) 2002-2003 +# The President and Fellows of Harvard College. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $Id: userUtils.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $ +# +# utils.pl - deal with details like turning login names into uids. +# + +sub logins2uids { + my ($str) = @_; + + my @uids = (); + my @logins = split (/,/, $str); + + my $j = 0; + for (my $i = 0; $i < @logins; $i++) { + if ($logins[$i] =~ /^[0-9]/) { + $uids[$j++] = $logins[$i]; + } + else { + my ($login, $pw, $uid, $gid) = getpwnam ($logins[$i]); + if (defined $uid) { + $uids[$j++] = $uid; + } + else { + # &&& Warning or error? + print STDERR "WARNING: Unrecognized login ($logins[$i])\n"; + $uids[$j++] = $logins[$i]; + } + } + } + + return (@uids); +} + +sub groups2gids { + my ($str) = @_; + + my @gids = (); + my @groups = split (/,/, $str); + + for (my $i = 0; $i < @groups; $i++) { + if ($groups[$i] =~ /^[0-9]/) { + $gids[$j++] = $groups[$i]; + } + else { + my $gid = getgrnam ($groups[$i]); + if (defined $gid) { + $gids[$j++] = $gid; + } + else { + # &&& Warning or error? + print STDERR "WARNING: Unrecognized group ($logins[$i])\n"; + $gids[$j++] = $groups[$i]; + } + } + } + + return (@gids); +} + +1; diff --git a/TBBT/trace_play/Makefile.org b/TBBT/trace_play/Makefile.org new file mode 100644 index 0000000..d019a0b --- /dev/null +++ b/TBBT/trace_play/Makefile.org @@ -0,0 +1,328 @@ +# +# @(#)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: diff --git a/TBBT/trace_play/README b/TBBT/trace_play/README new file mode 100644 index 0000000..e340761 --- /dev/null +++ b/TBBT/trace_play/README @@ -0,0 +1,10 @@ + +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 diff --git a/TBBT/trace_play/README.old b/TBBT/trace_play/README.old new file mode 100644 index 0000000..3f31fff --- /dev/null +++ b/TBBT/trace_play/README.old @@ -0,0 +1,140 @@ +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. diff --git a/TBBT/trace_play/agefs b/TBBT/trace_play/agefs new file mode 100755 index 0000000..bdebf34 Binary files /dev/null and b/TBBT/trace_play/agefs differ diff --git a/TBBT/trace_play/agefs.log b/TBBT/trace_play/agefs.log new file mode 100644 index 0000000..d3fcb00 --- /dev/null +++ b/TBBT/trace_play/agefs.log @@ -0,0 +1,36 @@ + + +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 diff --git a/TBBT/trace_play/frag_collect b/TBBT/trace_play/frag_collect new file mode 100755 index 0000000..773e158 --- /dev/null +++ b/TBBT/trace_play/frag_collect @@ -0,0 +1,15 @@ +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 diff --git a/TBBT/trace_play/frag_collect_cust b/TBBT/trace_play/frag_collect_cust new file mode 100755 index 0000000..24d9c62 --- /dev/null +++ b/TBBT/trace_play/frag_collect_cust @@ -0,0 +1,14 @@ +#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 diff --git a/TBBT/trace_play/frag_count b/TBBT/trace_play/frag_count new file mode 100755 index 0000000..e1cdae0 Binary files /dev/null and b/TBBT/trace_play/frag_count differ diff --git a/TBBT/trace_play/frag_count.c b/TBBT/trace_play/frag_count.c new file mode 100644 index 0000000..a8d8397 --- /dev/null +++ b/TBBT/trace_play/frag_count.c @@ -0,0 +1,311 @@ +#include +#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 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 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); +} diff --git a/TBBT/trace_play/fragment_collect b/TBBT/trace_play/fragment_collect new file mode 100755 index 0000000..9e5b8c9 --- /dev/null +++ b/TBBT/trace_play/fragment_collect @@ -0,0 +1,43 @@ +# 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 diff --git a/TBBT/trace_play/generate_xmgr b/TBBT/trace_play/generate_xmgr new file mode 100755 index 0000000..a44ed33 Binary files /dev/null and b/TBBT/trace_play/generate_xmgr differ diff --git a/TBBT/trace_play/generate_xmgr.c b/TBBT/trace_play/generate_xmgr.c new file mode 100644 index 0000000..354db18 --- /dev/null +++ b/TBBT/trace_play/generate_xmgr.c @@ -0,0 +1,226 @@ +#include +#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 (imax) + 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); + } + } +} diff --git a/TBBT/trace_play/generate_xmgr.c.old b/TBBT/trace_play/generate_xmgr.c.old new file mode 100644 index 0000000..4a12ce2 --- /dev/null +++ b/TBBT/trace_play/generate_xmgr.c.old @@ -0,0 +1,70 @@ +#include +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; + } + } +} + diff --git a/TBBT/trace_play/generic_hash.c b/TBBT/trace_play/generic_hash.c new file mode 100644 index 0000000..1d73a34 --- /dev/null +++ b/TBBT/trace_play/generic_hash.c @@ -0,0 +1,230 @@ +/* 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 +#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;ikey1); + 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; +} diff --git a/TBBT/trace_play/generic_hash.h b/TBBT/trace_play/generic_hash.h new file mode 100644 index 0000000..cf48bd4 --- /dev/null +++ b/TBBT/trace_play/generic_hash.h @@ -0,0 +1,24 @@ +#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); + diff --git a/TBBT/trace_play/hash.h b/TBBT/trace_play/hash.h new file mode 100644 index 0000000..808a95f --- /dev/null +++ b/TBBT/trace_play/hash.h @@ -0,0 +1,87 @@ +#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 +#include +#include +#include +#include +#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 diff --git a/TBBT/trace_play/init_holder.c b/TBBT/trace_play/init_holder.c new file mode 100644 index 0000000..f12d683 --- /dev/null +++ b/TBBT/trace_play/init_holder.c @@ -0,0 +1,57 @@ +#include +#include +#include +#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 - 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 + diff --git a/TBBT/trace_play/profile.c b/TBBT/trace_play/profile.c new file mode 100755 index 0000000..23548c1 --- /dev/null +++ b/TBBT/trace_play/profile.c @@ -0,0 +1,215 @@ +/* + * 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 +#include +#include +#include +#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 ); + } +*/ + } +} diff --git a/TBBT/trace_play/profile.h b/TBBT/trace_play/profile.h new file mode 100755 index 0000000..606652e --- /dev/null +++ b/TBBT/trace_play/profile.h @@ -0,0 +1,27 @@ +#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 +#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 diff --git a/TBBT/trace_play/rfs_3_ops.c b/TBBT/trace_play/rfs_3_ops.c new file mode 100644 index 0000000..f91bf05 --- /dev/null +++ b/TBBT/trace_play/rfs_3_ops.c @@ -0,0 +1,827 @@ +#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 +#include +#include +#include + +#include +#include + +#include +#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; + diff --git a/TBBT/trace_play/rfs_3_ops.c.old b/TBBT/trace_play/rfs_3_ops.c.old new file mode 100644 index 0000000..37a47be --- /dev/null +++ b/TBBT/trace_play/rfs_3_ops.c.old @@ -0,0 +1,739 @@ +#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 +#include +#include +#include + +#include +#include + +#include +#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; + diff --git a/TBBT/trace_play/rfs_assert.h b/TBBT/trace_play/rfs_assert.h new file mode 100644 index 0000000..ec360d5 --- /dev/null +++ b/TBBT/trace_play/rfs_assert.h @@ -0,0 +1,13 @@ +#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 diff --git a/TBBT/trace_play/rfs_c_age.c b/TBBT/trace_play/rfs_c_age.c new file mode 100644 index 0000000..cdf9c6c --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.c @@ -0,0 +1,1393 @@ +/* rfs_age_unit_base.c */ +#include +#include +#include +#include +#include +#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; ifh[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) && (sfhfh_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)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; ifh_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 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 (iactive_fh_max); + for (i=0; iactive_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=min) && (sfhfh[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; ifh[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=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= 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; (i0); 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; i0)) { + 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=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=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> %s", AGELOG_NAME); + system (cmd); + fplog = fopen(AGELOG_NAME, "a"); + RFS_ASSERT (fplog); + for (i=0; i1 && (!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++; + } + } +} + diff --git a/TBBT/trace_play/rfs_c_age.c.trace_base b/TBBT/trace_play/rfs_c_age.c.trace_base new file mode 100644 index 0000000..adeeca5 --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.c.trace_base @@ -0,0 +1,551 @@ +#include +#include +#include +#include +#include +#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; ifh_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)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; ifh_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 +#include +#include +#include +#include +#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; ifh[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) && (sfhfh_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; ifh_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 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 (iactive_fh_max); + for (i=0; iactive_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; ifh_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 diff --git a/TBBT/trace_play/rfs_c_age.obsolete.c b/TBBT/trace_play/rfs_c_age.obsolete.c new file mode 100644 index 0000000..aab4644 --- /dev/null +++ b/TBBT/trace_play/rfs_c_age.obsolete.c @@ -0,0 +1,194 @@ +#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 diff --git a/TBBT/trace_play/rfs_c_dat.c b/TBBT/trace_play/rfs_c_dat.c new file mode 100644 index 0000000..bcd3525 --- /dev/null +++ b/TBBT/trace_play/rfs_c_dat.c @@ -0,0 +1,188 @@ +#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; diff --git a/TBBT/trace_play/rfs_c_def.h b/TBBT/trace_play/rfs_c_def.h new file mode 100644 index 0000000..daeea6c --- /dev/null +++ b/TBBT/trace_play/rfs_c_def.h @@ -0,0 +1,357 @@ +#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)=0) && (dest=0) && (dest 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)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) */ diff --git a/TBBT/trace_play/rpc/auth_none.c b/TBBT/trace_play/rpc/auth_none.c new file mode 100755 index 0000000..4644ac4 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_none.c @@ -0,0 +1,154 @@ +#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 +#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) +{ +} diff --git a/TBBT/trace_play/rpc/auth_unix.c b/TBBT/trace_play/rpc/auth_unix.c new file mode 100755 index 0000000..06f7033 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_unix.c @@ -0,0 +1,345 @@ +#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 +#include +#include +#include +#include + +#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); +} diff --git a/TBBT/trace_play/rpc/auth_unix.h b/TBBT/trace_play/rpc/auth_unix.h new file mode 100755 index 0000000..24147e0 --- /dev/null +++ b/TBBT/trace_play/rpc/auth_unix.h @@ -0,0 +1,95 @@ +/* + * @(#)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; +}; diff --git a/TBBT/trace_play/rpc/authunix_prot.c b/TBBT/trace_play/rpc/authunix_prot.c new file mode 100755 index 0000000..bc18e7b --- /dev/null +++ b/TBBT/trace_play/rpc/authunix_prot.c @@ -0,0 +1,86 @@ +#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); +} + diff --git a/TBBT/trace_play/rpc/bindresvport.c b/TBBT/trace_play/rpc/bindresvport.c new file mode 100755 index 0000000..32927fe --- /dev/null +++ b/TBBT/trace_play/rpc/bindresvport.c @@ -0,0 +1,103 @@ +#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 +#include +#include +#include +#include +#include "rpc/rpc.h" +#include +#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); +} diff --git a/TBBT/trace_play/rpc/clnt.h b/TBBT/trace_play/rpc/clnt.h new file mode 100755 index 0000000..3288d5b --- /dev/null +++ b/TBBT/trace_play/rpc/clnt.h @@ -0,0 +1,384 @@ +/* + * @(#)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_*/ diff --git a/TBBT/trace_play/rpc/clnt_generic.c b/TBBT/trace_play/rpc/clnt_generic.c new file mode 100755 index 0000000..16538ef --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_generic.c @@ -0,0 +1,133 @@ +#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 +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include + +/* + * 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); +} diff --git a/TBBT/trace_play/rpc/clnt_perror.c b/TBBT/trace_play/rpc/clnt_perror.c new file mode 100755 index 0000000..71d1502 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_perror.c @@ -0,0 +1,316 @@ +#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 +#include +#include + +#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); +} diff --git a/TBBT/trace_play/rpc/clnt_simple.c b/TBBT/trace_play/rpc/clnt_simple.c new file mode 100755 index 0000000..ab5ae9a --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_simple.c @@ -0,0 +1,141 @@ +#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 +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include + +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); +} diff --git a/TBBT/trace_play/rpc/clnt_tcp.c b/TBBT/trace_play/rpc/clnt_tcp.c new file mode 100755 index 0000000..4054f01 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_tcp.c @@ -0,0 +1,522 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include +#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); +} + diff --git a/TBBT/trace_play/rpc/clnt_udp.c b/TBBT/trace_play/rpc/clnt_udp.c new file mode 100755 index 0000000..d362f10 --- /dev/null +++ b/TBBT/trace_play/rpc/clnt_udp.c @@ -0,0 +1,510 @@ +#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 +#include +#include +#ifndef FreeBSD +#include +#endif /* ndef Free BSD */ +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include +#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); +} diff --git a/TBBT/trace_play/rpc/get_myaddress.c b/TBBT/trace_play/rpc/get_myaddress.c new file mode 100755 index 0000000..37bb930 --- /dev/null +++ b/TBBT/trace_play/rpc/get_myaddress.c @@ -0,0 +1,169 @@ +#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 +#include +#include +#ifndef FreeBSD +#include +#endif /* ndef FreeBSD */ +#include +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/osdep.h" +#include + +#ifdef FreeBSD +#include +#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 +} diff --git a/TBBT/trace_play/rpc/getrpcent.c b/TBBT/trace_play/rpc/getrpcent.c new file mode 100755 index 0000000..2649af6 --- /dev/null +++ b/TBBT/trace_play/rpc/getrpcent.c @@ -0,0 +1,250 @@ +#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 +#include +#include +#include +#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); +} diff --git a/TBBT/trace_play/rpc/getrpcport.c b/TBBT/trace_play/rpc/getrpcport.c new file mode 100755 index 0000000..6434082 --- /dev/null +++ b/TBBT/trace_play/rpc/getrpcport.c @@ -0,0 +1,77 @@ +#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 +#include +#include "rpc/rpc.h" +#include +#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)); +} diff --git a/TBBT/trace_play/rpc/netdb.h b/TBBT/trace_play/rpc/netdb.h new file mode 100755 index 0000000..a3a96fc --- /dev/null +++ b/TBBT/trace_play/rpc/netdb.h @@ -0,0 +1,63 @@ +/* + * @(#)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 */ + +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); diff --git a/TBBT/trace_play/rpc/osdep.h b/TBBT/trace_play/rpc/osdep.h new file mode 100755 index 0000000..67598d0 --- /dev/null +++ b/TBBT/trace_play/rpc/osdep.h @@ -0,0 +1,218 @@ +/* + * @(#)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 +#endif +#include +#include +#include +#include +#include +#include +#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__ */ diff --git a/TBBT/trace_play/rpc/pmap_clnt.c b/TBBT/trace_play/rpc/pmap_clnt.c new file mode 100755 index 0000000..b2de53f --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_clnt.c @@ -0,0 +1,138 @@ +#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 +#include +#include +#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); +} diff --git a/TBBT/trace_play/rpc/pmap_clnt.h b/TBBT/trace_play/rpc/pmap_clnt.h new file mode 100755 index 0000000..9d7b74e --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_clnt.h @@ -0,0 +1,97 @@ +/* + * @(#)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 */ diff --git a/TBBT/trace_play/rpc/pmap_getmaps.c b/TBBT/trace_play/rpc/pmap_getmaps.c new file mode 100755 index 0000000..ab1ce75 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_getmaps.c @@ -0,0 +1,105 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/pmap_prot.h" +#include "rpc/pmap_clnt.h" +#include "rpc/osdep.h" +#include + +#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); +} diff --git a/TBBT/trace_play/rpc/pmap_getport.c b/TBBT/trace_play/rpc/pmap_getport.c new file mode 100755 index 0000000..2457475 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_getport.c @@ -0,0 +1,110 @@ +#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 +#include +#include +#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); +} diff --git a/TBBT/trace_play/rpc/pmap_prot.c b/TBBT/trace_play/rpc/pmap_prot.c new file mode 100755 index 0000000..242e846 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot.c @@ -0,0 +1,78 @@ +#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); +} diff --git a/TBBT/trace_play/rpc/pmap_prot.h b/TBBT/trace_play/rpc/pmap_prot.h new file mode 100755 index 0000000..adbd407 --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot.h @@ -0,0 +1,114 @@ +/* + * @(#)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(); diff --git a/TBBT/trace_play/rpc/pmap_prot2.c b/TBBT/trace_play/rpc/pmap_prot2.c new file mode 100755 index 0000000..9cb238a --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_prot2.c @@ -0,0 +1,138 @@ +#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); + } +} diff --git a/TBBT/trace_play/rpc/pmap_rmt.c b/TBBT/trace_play/rpc/pmap_rmt.c new file mode 100755 index 0000000..a834cfd --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_rmt.c @@ -0,0 +1,419 @@ +#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 +#include +#include +#ifndef FreeBSD +#include +#endif /* ndef FreeBSD */ +#include +#include +#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); +} + diff --git a/TBBT/trace_play/rpc/pmap_rmt.h b/TBBT/trace_play/rpc/pmap_rmt.h new file mode 100755 index 0000000..40a770a --- /dev/null +++ b/TBBT/trace_play/rpc/pmap_rmt.h @@ -0,0 +1,74 @@ +/* + * @(#)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(); diff --git a/TBBT/trace_play/rpc/rpc.h b/TBBT/trace_play/rpc/rpc.h new file mode 100755 index 0000000..43935d6 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc.h @@ -0,0 +1,93 @@ +/* + * @(#)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__ */ diff --git a/TBBT/trace_play/rpc/rpc_callmsg.c b/TBBT/trace_play/rpc/rpc_callmsg.c new file mode 100755 index 0000000..e7bfdc8 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_callmsg.c @@ -0,0 +1,214 @@ +#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 +#include +#include +#include + +#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); +} + diff --git a/TBBT/trace_play/rpc/rpc_commondata.c b/TBBT/trace_play/rpc/rpc_commondata.c new file mode 100755 index 0000000..c2df017 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_commondata.c @@ -0,0 +1,62 @@ +#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; diff --git a/TBBT/trace_play/rpc/rpc_dtablesize.c b/TBBT/trace_play/rpc/rpc_dtablesize.c new file mode 100755 index 0000000..8c1d9f9 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_dtablesize.c @@ -0,0 +1,114 @@ +#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 + +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 + +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 + +int +_rpc_dtablesize(void) +{ + return (NOFILE); +} +#endif diff --git a/TBBT/trace_play/rpc/rpc_msg.h b/TBBT/trace_play/rpc/rpc_msg.h new file mode 100755 index 0000000..fde7b93 --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_msg.h @@ -0,0 +1,215 @@ +/* + * @(#)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 */ diff --git a/TBBT/trace_play/rpc/rpc_prot.c b/TBBT/trace_play/rpc/rpc_prot.c new file mode 100755 index 0000000..623ee7a --- /dev/null +++ b/TBBT/trace_play/rpc/rpc_prot.c @@ -0,0 +1,307 @@ +#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; + } +} diff --git a/TBBT/trace_play/rpc/sfs_ctcp.c b/TBBT/trace_play/rpc/sfs_ctcp.c new file mode 100755 index 0000000..d6a19f0 --- /dev/null +++ b/TBBT/trace_play/rpc/sfs_ctcp.c @@ -0,0 +1,727 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include +#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)); +} diff --git a/TBBT/trace_play/rpc/sfs_cudp.c b/TBBT/trace_play/rpc/sfs_cudp.c new file mode 100755 index 0000000..1dee989 --- /dev/null +++ b/TBBT/trace_play/rpc/sfs_cudp.c @@ -0,0 +1,604 @@ +#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 +#include +#include +#ifndef FreeBSD +#include +#endif /* ndef FreeBSD */ +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include +#include "rpc/pmap_clnt.h" +#include +#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)); +} diff --git a/TBBT/trace_play/rpc/svc.c b/TBBT/trace_play/rpc/svc.c new file mode 100755 index 0000000..9a8d905 --- /dev/null +++ b/TBBT/trace_play/rpc/svc.c @@ -0,0 +1,479 @@ +#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 +#include +#include +#include +#include +#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); + } + } +} diff --git a/TBBT/trace_play/rpc/svc.h b/TBBT/trace_play/rpc/svc.h new file mode 100755 index 0000000..497a6ca --- /dev/null +++ b/TBBT/trace_play/rpc/svc.h @@ -0,0 +1,314 @@ +/* + * @(#)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__ */ diff --git a/TBBT/trace_play/rpc/svc_auth.c b/TBBT/trace_play/rpc/svc_auth.c new file mode 100755 index 0000000..2352630 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth.c @@ -0,0 +1,136 @@ +#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); +} diff --git a/TBBT/trace_play/rpc/svc_auth.h b/TBBT/trace_play/rpc/svc_auth.h new file mode 100755 index 0000000..c765e9f --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth.h @@ -0,0 +1,62 @@ +/* + * @(#)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(); diff --git a/TBBT/trace_play/rpc/svc_auth_unix.c b/TBBT/trace_play/rpc/svc_auth_unix.c new file mode 100755 index 0000000..83ed327 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_auth_unix.c @@ -0,0 +1,156 @@ +#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 +#include +#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); +} diff --git a/TBBT/trace_play/rpc/svc_raw.c b/TBBT/trace_play/rpc/svc_raw.c new file mode 100755 index 0000000..4b85782 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_raw.c @@ -0,0 +1,196 @@ +#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 +#include +#include +#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) +{ +} diff --git a/TBBT/trace_play/rpc/svc_run.c b/TBBT/trace_play/rpc/svc_run.c new file mode 100755 index 0000000..0c787b4 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_run.c @@ -0,0 +1,93 @@ +#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 + +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); + } + } +} diff --git a/TBBT/trace_play/rpc/svc_simple.c b/TBBT/trace_play/rpc/svc_simple.c new file mode 100755 index 0000000..ec22a47 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_simple.c @@ -0,0 +1,171 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include + +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); +} + diff --git a/TBBT/trace_play/rpc/svc_tcp.c b/TBBT/trace_play/rpc/svc_tcp.c new file mode 100755 index 0000000..2b8b71a --- /dev/null +++ b/TBBT/trace_play/rpc/svc_tcp.c @@ -0,0 +1,481 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include + +/* + * 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); +} diff --git a/TBBT/trace_play/rpc/svc_udp.c b/TBBT/trace_play/rpc/svc_udp.c new file mode 100755 index 0000000..0a801b2 --- /dev/null +++ b/TBBT/trace_play/rpc/svc_udp.c @@ -0,0 +1,507 @@ +#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 +#include +#include +#include +#include "rpc/rpc.h" +#include "rpc/osdep.h" +#include + + +#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); +} + diff --git a/TBBT/trace_play/rpc/types.h b/TBBT/trace_play/rpc/types.h new file mode 100755 index 0000000..0854cc6 --- /dev/null +++ b/TBBT/trace_play/rpc/types.h @@ -0,0 +1,111 @@ +/* + * @(#)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 + */ +#ifndef __TYPES_RPC_HEADER__ +#define __TYPES_RPC_HEADER__ + +#include +#include + +#if defined(__INTTYPES_INCLUDED) || defined(_SYS_INT_TYPES_H) +#define HAVE_INTTYPES +#endif + +#if defined(USE_INTTYPES) +#include +#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__ */ diff --git a/TBBT/trace_play/rpc/xdr.c b/TBBT/trace_play/rpc/xdr.c new file mode 100755 index 0000000..3a1fc88 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr.c @@ -0,0 +1,685 @@ +#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 +#include +#include + +#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); +} diff --git a/TBBT/trace_play/rpc/xdr.h b/TBBT/trace_play/rpc/xdr.h new file mode 100755 index 0000000..2ccc5d2 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr.h @@ -0,0 +1,308 @@ +/* + * @(#)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 + +/* + * 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; + * *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__ */ diff --git a/TBBT/trace_play/rpc/xdr_array.c b/TBBT/trace_play/rpc/xdr_array.c new file mode 100755 index 0000000..d448022 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_array.c @@ -0,0 +1,175 @@ +#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 +#include +#include +#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); +} + diff --git a/TBBT/trace_play/rpc/xdr_float.c b/TBBT/trace_play/rpc/xdr_float.c new file mode 100755 index 0000000..9048665 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_float.c @@ -0,0 +1,288 @@ +#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 + +#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); +} diff --git a/TBBT/trace_play/rpc/xdr_mem.c b/TBBT/trace_play/rpc/xdr_mem.c new file mode 100755 index 0000000..6b7fe4d --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_mem.c @@ -0,0 +1,205 @@ +#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 +#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); +} diff --git a/TBBT/trace_play/rpc/xdr_rec.c b/TBBT/trace_play/rpc/xdr_rec.c new file mode 100755 index 0000000..0047c44 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_rec.c @@ -0,0 +1,608 @@ +#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 +#include +#include +#include +#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)); +} diff --git a/TBBT/trace_play/rpc/xdr_reference.c b/TBBT/trace_play/rpc/xdr_reference.c new file mode 100755 index 0000000..56fc99c --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_reference.c @@ -0,0 +1,156 @@ +#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 +#include +#include +#include +#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)); +} diff --git a/TBBT/trace_play/rpc/xdr_stdio.c b/TBBT/trace_play/rpc/xdr_stdio.c new file mode 100755 index 0000000..8240089 --- /dev/null +++ b/TBBT/trace_play/rpc/xdr_stdio.c @@ -0,0 +1,210 @@ +#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 +#include +#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); +} diff --git a/TBBT/trace_play/sem.c b/TBBT/trace_play/sem.c new file mode 100644 index 0000000..3079f9b --- /dev/null +++ b/TBBT/trace_play/sem.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +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 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 +(where 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 diff --git a/TBBT/trace_play/sfs3.full_speed b/TBBT/trace_play/sfs3.full_speed new file mode 100755 index 0000000..5237e99 Binary files /dev/null and b/TBBT/trace_play/sfs3.full_speed differ diff --git a/TBBT/trace_play/sfs_2_ops.c b/TBBT/trace_play/sfs_2_ops.c new file mode 100644 index 0000000..f50f353 --- /dev/null +++ b/TBBT/trace_play/sfs_2_ops.c @@ -0,0 +1,2131 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_2_vld.c b/TBBT/trace_play/sfs_2_vld.c new file mode 100644 index 0000000..45e24d9 --- /dev/null +++ b/TBBT/trace_play/sfs_2_vld.c @@ -0,0 +1,1747 @@ +#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 +#include +#include +#include +#include + +#include +#include + +#include + +#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; + } + } +} + + + + + + + + diff --git a/TBBT/trace_play/sfs_2_xdr.c b/TBBT/trace_play/sfs_2_xdr.c new file mode 100644 index 0000000..7c84899 --- /dev/null +++ b/TBBT/trace_play/sfs_2_xdr.c @@ -0,0 +1,735 @@ +#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 +#include +#include +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_3_ops.c b/TBBT/trace_play/sfs_3_ops.c new file mode 100644 index 0000000..19aef00 --- /dev/null +++ b/TBBT/trace_play/sfs_3_ops.c @@ -0,0 +1,2746 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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; icall_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 */ diff --git a/TBBT/trace_play/sfs_3_ops.c.org b/TBBT/trace_play/sfs_3_ops.c.org new file mode 100644 index 0000000..57cbb57 --- /dev/null +++ b/TBBT/trace_play/sfs_3_ops.c.org @@ -0,0 +1,2701 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_3_vld.c b/TBBT/trace_play/sfs_3_vld.c new file mode 100644 index 0000000..8ede891 --- /dev/null +++ b/TBBT/trace_play/sfs_3_vld.c @@ -0,0 +1,2126 @@ +#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 +#include +#include +#include +#include + +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_3_xdr.c b/TBBT/trace_play/sfs_3_xdr.c new file mode 100644 index 0000000..01a69fb --- /dev/null +++ b/TBBT/trace_play/sfs_3_xdr.c @@ -0,0 +1,1720 @@ +#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 +#include +#include +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_bio.c b/TBBT/trace_play/sfs_c_bio.c new file mode 100644 index 0000000..ed35c42 --- /dev/null +++ b/TBBT/trace_play/sfs_c_bio.c @@ -0,0 +1,1023 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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 */ + diff --git a/TBBT/trace_play/sfs_c_bio.org b/TBBT/trace_play/sfs_c_bio.org new file mode 100644 index 0000000..88c54f5 --- /dev/null +++ b/TBBT/trace_play/sfs_c_bio.org @@ -0,0 +1,1012 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_chd.2thread.c b/TBBT/trace_play/sfs_c_chd.2thread.c new file mode 100644 index 0000000..48ab54e --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.2thread.c @@ -0,0 +1,3509 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; iflag==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=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; i80)) { + 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])80) && (strlen(line_buf[i])=0 && proc 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=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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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)80) && (strlen(buf)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)80) && (strlen(buf)= 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; i10000)) + 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= 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 ("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]); + (j0); + 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; itimeout.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= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc = 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; icl_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; ikey3].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; itrace_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; ilock = 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 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=0) && (desttail = 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; iflag==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 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 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])80) && (strlen(line_buf[i])=0 && proc 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=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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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)80) && (strlen(buf)= 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; i10000)) + 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= 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 ("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]); + (j0); + 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; itimeout.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= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc = 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; icl_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; ikey3].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; itrace_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; ilock = 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 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=0) && (desttail = 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; iflag==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; i80) && (strlen(buf)80) && (strlen(buf)= 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; i10000)) + 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= 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; jlock) { + 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 ("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]); + (j0); + 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; itimeout.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= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc = 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_indexmax_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; i0) + 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; icl_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; ifh_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; ikey3].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; itrace_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; ilock = 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; iresults.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 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_index0)) { + + 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 */ diff --git a/TBBT/trace_play/sfs_c_chd.c.old.2 b/TBBT/trace_play/sfs_c_chd.c.old.2 new file mode 100644 index 0000000..a8c71f7 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c.old.2 @@ -0,0 +1,2294 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; iflag==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; i80) && (strlen(buf)80) && (strlen(buf)= 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; i10000)) + 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; ilock) { + 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 ("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]); + (j0); + 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; itimeout.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= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc = 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_indexmax_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; i0) + 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; icl_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; ifh_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; itrace_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; ilock = 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; iresults.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 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_index0)) { + + 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 */ diff --git a/TBBT/trace_play/sfs_c_chd.c.org b/TBBT/trace_play/sfs_c_chd.c.org new file mode 100644 index 0000000..27a122c --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.c.org @@ -0,0 +1,2866 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_chd.work b/TBBT/trace_play/sfs_c_chd.work new file mode 100644 index 0000000..195beb0 --- /dev/null +++ b/TBBT/trace_play/sfs_c_chd.work @@ -0,0 +1,3815 @@ +/* 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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; iflag==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 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 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])80) && (strlen(line_buf[i])=0 && proc 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=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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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 && nfs3proc80) && (strlen(buf)80) && (strlen(buf) 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)80) && (strlen(buf)= 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; i10000)) + 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= 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 ("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]); + (j0); + 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; itimeout.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= NFS3_PROCEDURE_COUNT) { + fprintf(stderr, "proc %d line %s\n", nfsproc, line); + + } + RFS_ASSERT (nfsproc = 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; icl_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; ikey3].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; itrace_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; ilock = 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 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=0) && (desttail = 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_clnt.c b/TBBT/trace_play/sfs_c_clnt.c new file mode 100644 index 0000000..6c18e0d --- /dev/null +++ b/TBBT/trace_play/sfs_c_clnt.c @@ -0,0 +1,108 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include +#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); +} diff --git a/TBBT/trace_play/sfs_c_dat.c b/TBBT/trace_play/sfs_c_dat.c new file mode 100644 index 0000000..a5fc22e --- /dev/null +++ b/TBBT/trace_play/sfs_c_dat.c @@ -0,0 +1,339 @@ +#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 +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_def.h b/TBBT/trace_play/sfs_c_def.h new file mode 100644 index 0000000..e35e73d --- /dev/null +++ b/TBBT/trace_play/sfs_c_def.h @@ -0,0 +1,634 @@ +#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 + +#include + +#if !defined(_XOPEN_SOURCE) +/* + * Some non-XOPEN_compilent systems are missing these definitions + */ +#if !defined(SVR4) /* Assume BSD */ +#include +#endif /* SVR4 */ +#endif /* !_XOPEN_SOURCE */ + +#include + +#if defined(SVR4) +#include +#endif + +#include "rpc/rpc.h" + +#if (defined (SVR4) || defined (AIX)) +#include +#endif + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_dmp.c b/TBBT/trace_play/sfs_c_dmp.c new file mode 100644 index 0000000..82d9a38 --- /dev/null +++ b/TBBT/trace_play/sfs_c_dmp.c @@ -0,0 +1,298 @@ +#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 +#include +#include +#include + +#include +#include + +#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 + * 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); +} diff --git a/TBBT/trace_play/sfs_c_man.c b/TBBT/trace_play/sfs_c_man.c new file mode 100644 index 0000000..33bf28a --- /dev/null +++ b/TBBT/trace_play/sfs_c_man.c @@ -0,0 +1,1998 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +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 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 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 */ + diff --git a/TBBT/trace_play/sfs_c_mnt.c b/TBBT/trace_play/sfs_c_mnt.c new file mode 100644 index 0000000..8f60387 --- /dev/null +++ b/TBBT/trace_play/sfs_c_mnt.c @@ -0,0 +1,573 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_nfs.h b/TBBT/trace_play/sfs_c_nfs.h new file mode 100644 index 0000000..eb34652 --- /dev/null +++ b/TBBT/trace_play/sfs_c_nfs.h @@ -0,0 +1,1470 @@ +#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 */ diff --git a/TBBT/trace_play/sfs_c_pnt.c b/TBBT/trace_play/sfs_c_pnt.c new file mode 100644 index 0000000..cab4f43 --- /dev/null +++ b/TBBT/trace_play/sfs_c_pnt.c @@ -0,0 +1,1163 @@ +#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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include +#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 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 */ diff --git a/TBBT/trace_play/sfs_c_rnd.c b/TBBT/trace_play/sfs_c_rnd.c new file mode 100644 index 0000000..b3a0174 --- /dev/null +++ b/TBBT/trace_play/sfs_c_rnd.c @@ -0,0 +1,247 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_sig.c b/TBBT/trace_play/sfs_c_sig.c new file mode 100644 index 0000000..7826377 --- /dev/null +++ b/TBBT/trace_play/sfs_c_sig.c @@ -0,0 +1,170 @@ +#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 +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_c_sub.c b/TBBT/trace_play/sfs_c_sub.c new file mode 100644 index 0000000..7f91b89 --- /dev/null +++ b/TBBT/trace_play/sfs_c_sub.c @@ -0,0 +1,198 @@ +#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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_ext_mon b/TBBT/trace_play/sfs_ext_mon new file mode 100755 index 0000000..841e9a1 --- /dev/null +++ b/TBBT/trace_play/sfs_ext_mon @@ -0,0 +1,58 @@ +#!/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 diff --git a/TBBT/trace_play/sfs_m_def.h b/TBBT/trace_play/sfs_m_def.h new file mode 100644 index 0000000..bd01ec6 --- /dev/null +++ b/TBBT/trace_play/sfs_m_def.h @@ -0,0 +1,59 @@ +/* + * @(#)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 *); diff --git a/TBBT/trace_play/sfs_m_msg.c b/TBBT/trace_play/sfs_m_msg.c new file mode 100644 index 0000000..80fbeec --- /dev/null +++ b/TBBT/trace_play/sfs_m_msg.c @@ -0,0 +1,103 @@ +#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 +#include +#include +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_m_prm.c b/TBBT/trace_play/sfs_m_prm.c new file mode 100644 index 0000000..5cc5ac3 --- /dev/null +++ b/TBBT/trace_play/sfs_m_prm.c @@ -0,0 +1,1545 @@ +#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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "sfs_c_def.h" +#include "sfs_m_def.h" + +#if !defined(_XOPEN_SOURCE) +#include +#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 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] [...]\n"); + + + (void) fflush(stderr); + (int) signal_sfs_clients("PRIME_STOP"); + exit(64); + /* NOTREACHED */ +} /* prog_usage */ + +/* sfs_m_prm.c */ diff --git a/TBBT/trace_play/sfs_m_snc.c b/TBBT/trace_play/sfs_m_snc.c new file mode 100644 index 0000000..8a8f2e2 --- /dev/null +++ b/TBBT/trace_play/sfs_m_snc.c @@ -0,0 +1,456 @@ +#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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_m_xdr.c b/TBBT/trace_play/sfs_m_xdr.c new file mode 100644 index 0000000..b2cf930 --- /dev/null +++ b/TBBT/trace_play/sfs_m_xdr.c @@ -0,0 +1,103 @@ +#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 +#include +#include +#include + +#include +#include + +#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 */ diff --git a/TBBT/trace_play/sfs_mcr b/TBBT/trace_play/sfs_mcr new file mode 100755 index 0000000..c24fb3e --- /dev/null +++ b/TBBT/trace_play/sfs_mcr @@ -0,0 +1,170 @@ +#! /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 +# +# 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 " > /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 diff --git a/TBBT/trace_play/sfs_mgr b/TBBT/trace_play/sfs_mgr new file mode 100755 index 0000000..c1203a4 --- /dev/null +++ b/TBBT/trace_play/sfs_mgr @@ -0,0 +1,1093 @@ +#! /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 ] [-s ] [-v ] +# +# 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. +# Log of multi-client run placed in sfslog. +# Individual client result(s) placed in sfs. +# +# + +# --------------- 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 ] [-s ] [-v ]" + +# ----------------- 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 " > /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 > $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 > $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 > $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 >$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 >$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 /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 > $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 > "$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 diff --git a/TBBT/trace_play/sfs_rc b/TBBT/trace_play/sfs_rc new file mode 100755 index 0000000..fcf8cfd --- /dev/null +++ b/TBBT/trace_play/sfs_rc @@ -0,0 +1,149 @@ +############################################################################## +# +# @(#)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. diff --git a/TBBT/trace_play/sfs_suchown b/TBBT/trace_play/sfs_suchown new file mode 100755 index 0000000..49ac031 --- /dev/null +++ b/TBBT/trace_play/sfs_suchown @@ -0,0 +1,71 @@ +#!/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 <; /* message type, hard coded */ + string clnt_transaction; /* transaction id */ + string clnt_data; /* results strings */ +}; + +program SFS { + version SFS { + int + SIGNAL_NULLPROC (void) = 0; + int + SIGNAL_SFS (sync_string) = 1; + } = 1; +} = 100500; diff --git a/TBBT/trace_play/t b/TBBT/trace_play/t new file mode 100755 index 0000000..9686a68 Binary files /dev/null and b/TBBT/trace_play/t differ diff --git a/TBBT/trace_play/t.c b/TBBT/trace_play/t.c new file mode 100644 index 0000000..45baab2 --- /dev/null +++ b/TBBT/trace_play/t.c @@ -0,0 +1,6 @@ +main() +{ + int i = 1; + i <<= 2; + printf("%d\n", i); +}