From adc8816a09e5b6be2e58f4a7c28d2418a74cce9c Mon Sep 17 00:00:00 2001 From: Michael Vrable Date: Fri, 23 Apr 2010 12:48:14 -0700 Subject: [PATCH] Import TBBT (NFS trace replay). Code downloaded from http://www.ecsl.cs.sunysb.edu/TBBT/. --- TBBT/README | 50 + TBBT/trace_init/EXAMPLES | 170 ++ TBBT/trace_init/INSTALL | 61 + TBBT/trace_init/NOTES.TXT | 17 + TBBT/trace_init/commands | 20 + TBBT/trace_init/common.pl | 61 + TBBT/trace_init/counts.pl | 103 + TBBT/trace_init/extract-hierarchy | 4 + TBBT/trace_init/hier.pl | 715 +++++ TBBT/trace_init/hier.pl.old | 550 ++++ TBBT/trace_init/key.pl | 122 + TBBT/trace_init/latency.pl | 159 + TBBT/trace_init/nfsdump.pl | 338 +++ TBBT/trace_init/nfsscan.pl | 682 +++++ TBBT/trace_init/nfsscan.txt | 230 ++ TBBT/trace_init/ns_loc2gmt.pl | 108 + TBBT/trace_init/ns_quickview.pl | 245 ++ TBBT/trace_init/ns_split.pl | 153 + TBBT/trace_init/ns_timeagg.pl | 218 ++ TBBT/trace_init/ns_tsplit.pl | 219 ++ TBBT/trace_init/rfs.pl | 243 ++ TBBT/trace_init/rfs.pl.old | 138 + TBBT/trace_init/rfs.stub.pl | 213 ++ TBBT/trace_init/test.cnt | 2 + TBBT/trace_init/test.fil | 1 + TBBT/trace_init/userUtils.pl | 87 + TBBT/trace_play/Makefile.org | 328 ++ TBBT/trace_play/README | 10 + TBBT/trace_play/README.old | 140 + TBBT/trace_play/agefs | Bin 0 -> 83979 bytes TBBT/trace_play/agefs.log | 36 + TBBT/trace_play/frag_collect | 15 + TBBT/trace_play/frag_collect_cust | 14 + TBBT/trace_play/frag_count | Bin 0 -> 26012 bytes TBBT/trace_play/frag_count.c | 311 ++ TBBT/trace_play/fragment_collect | 43 + TBBT/trace_play/generate_xmgr | Bin 0 -> 26083 bytes TBBT/trace_play/generate_xmgr.c | 226 ++ TBBT/trace_play/generate_xmgr.c.old | 70 + TBBT/trace_play/generic_hash.c | 230 ++ TBBT/trace_play/generic_hash.h | 24 + TBBT/trace_play/hash.h | 87 + TBBT/trace_play/init_holder.c | 57 + TBBT/trace_play/nfsd_nfsfh_cust.h | 114 + TBBT/trace_play/profile.c | 215 ++ TBBT/trace_play/profile.h | 27 + TBBT/trace_play/rfs_3_ops.c | 827 +++++ TBBT/trace_play/rfs_3_ops.c.old | 739 +++++ TBBT/trace_play/rfs_assert.h | 13 + TBBT/trace_play/rfs_c_age.c | 1393 +++++++++ TBBT/trace_play/rfs_c_age.c.trace_base | 551 ++++ TBBT/trace_play/rfs_c_age.c.unit_base | 915 ++++++ TBBT/trace_play/rfs_c_age.obsolete.c | 194 ++ TBBT/trace_play/rfs_c_dat.c | 188 ++ TBBT/trace_play/rfs_c_def.h | 357 +++ TBBT/trace_play/rpc/auth.h | 185 ++ TBBT/trace_play/rpc/auth_none.c | 154 + TBBT/trace_play/rpc/auth_unix.c | 345 +++ TBBT/trace_play/rpc/auth_unix.h | 95 + TBBT/trace_play/rpc/authunix_prot.c | 86 + TBBT/trace_play/rpc/bindresvport.c | 103 + TBBT/trace_play/rpc/clnt.h | 384 +++ TBBT/trace_play/rpc/clnt_generic.c | 133 + TBBT/trace_play/rpc/clnt_perror.c | 316 ++ TBBT/trace_play/rpc/clnt_simple.c | 141 + TBBT/trace_play/rpc/clnt_tcp.c | 522 ++++ TBBT/trace_play/rpc/clnt_udp.c | 510 ++++ TBBT/trace_play/rpc/get_myaddress.c | 169 ++ TBBT/trace_play/rpc/getrpcent.c | 250 ++ TBBT/trace_play/rpc/getrpcport.c | 77 + TBBT/trace_play/rpc/netdb.h | 63 + TBBT/trace_play/rpc/osdep.h | 218 ++ TBBT/trace_play/rpc/pmap_clnt.c | 138 + TBBT/trace_play/rpc/pmap_clnt.h | 97 + TBBT/trace_play/rpc/pmap_getmaps.c | 105 + TBBT/trace_play/rpc/pmap_getport.c | 110 + TBBT/trace_play/rpc/pmap_prot.c | 78 + TBBT/trace_play/rpc/pmap_prot.h | 114 + TBBT/trace_play/rpc/pmap_prot2.c | 138 + TBBT/trace_play/rpc/pmap_rmt.c | 419 +++ TBBT/trace_play/rpc/pmap_rmt.h | 74 + TBBT/trace_play/rpc/rpc.h | 93 + TBBT/trace_play/rpc/rpc_callmsg.c | 214 ++ TBBT/trace_play/rpc/rpc_commondata.c | 62 + TBBT/trace_play/rpc/rpc_dtablesize.c | 114 + TBBT/trace_play/rpc/rpc_msg.h | 215 ++ TBBT/trace_play/rpc/rpc_prot.c | 307 ++ TBBT/trace_play/rpc/sfs_ctcp.c | 727 +++++ TBBT/trace_play/rpc/sfs_cudp.c | 604 ++++ TBBT/trace_play/rpc/svc.c | 479 +++ TBBT/trace_play/rpc/svc.h | 314 ++ TBBT/trace_play/rpc/svc_auth.c | 136 + TBBT/trace_play/rpc/svc_auth.h | 62 + TBBT/trace_play/rpc/svc_auth_unix.c | 156 + TBBT/trace_play/rpc/svc_raw.c | 196 ++ TBBT/trace_play/rpc/svc_run.c | 93 + TBBT/trace_play/rpc/svc_simple.c | 171 ++ TBBT/trace_play/rpc/svc_tcp.c | 481 +++ TBBT/trace_play/rpc/svc_udp.c | 507 ++++ TBBT/trace_play/rpc/types.h | 111 + TBBT/trace_play/rpc/xdr.c | 685 +++++ TBBT/trace_play/rpc/xdr.h | 308 ++ TBBT/trace_play/rpc/xdr_array.c | 175 ++ TBBT/trace_play/rpc/xdr_float.c | 288 ++ TBBT/trace_play/rpc/xdr_mem.c | 205 ++ TBBT/trace_play/rpc/xdr_rec.c | 608 ++++ TBBT/trace_play/rpc/xdr_reference.c | 156 + TBBT/trace_play/rpc/xdr_stdio.c | 210 ++ TBBT/trace_play/sem.c | 149 + TBBT/trace_play/sfs.1 | 842 ++++++ TBBT/trace_play/sfs3.full_speed | Bin 0 -> 1004158 bytes TBBT/trace_play/sfs_2_ops.c | 2131 +++++++++++++ TBBT/trace_play/sfs_2_vld.c | 1747 +++++++++++ TBBT/trace_play/sfs_2_xdr.c | 735 +++++ TBBT/trace_play/sfs_3_ops.c | 2746 +++++++++++++++++ TBBT/trace_play/sfs_3_ops.c.org | 2701 +++++++++++++++++ TBBT/trace_play/sfs_3_vld.c | 2126 +++++++++++++ TBBT/trace_play/sfs_3_xdr.c | 1720 +++++++++++ TBBT/trace_play/sfs_c_bio.c | 1023 +++++++ TBBT/trace_play/sfs_c_bio.org | 1012 +++++++ TBBT/trace_play/sfs_c_chd.2thread.c | 3509 ++++++++++++++++++++++ TBBT/trace_play/sfs_c_chd.3thread.c | 3815 ++++++++++++++++++++++++ TBBT/trace_play/sfs_c_chd.c | 1 + TBBT/trace_play/sfs_c_chd.c.old | 2457 +++++++++++++++ TBBT/trace_play/sfs_c_chd.c.old.2 | 2294 ++++++++++++++ TBBT/trace_play/sfs_c_chd.c.org | 2866 ++++++++++++++++++ TBBT/trace_play/sfs_c_chd.work | 3815 ++++++++++++++++++++++++ TBBT/trace_play/sfs_c_clk.c | 309 ++ TBBT/trace_play/sfs_c_clnt.c | 108 + TBBT/trace_play/sfs_c_dat.c | 339 +++ TBBT/trace_play/sfs_c_def.h | 634 ++++ TBBT/trace_play/sfs_c_dmp.c | 298 ++ TBBT/trace_play/sfs_c_man.c | 1998 +++++++++++++ TBBT/trace_play/sfs_c_mnt.c | 573 ++++ TBBT/trace_play/sfs_c_nfs.h | 1470 +++++++++ TBBT/trace_play/sfs_c_pnt.c | 1163 ++++++++ TBBT/trace_play/sfs_c_rnd.c | 247 ++ TBBT/trace_play/sfs_c_sig.c | 170 ++ TBBT/trace_play/sfs_c_sub.c | 198 ++ TBBT/trace_play/sfs_ext_mon | 58 + TBBT/trace_play/sfs_m_def.h | 59 + TBBT/trace_play/sfs_m_msg.c | 103 + TBBT/trace_play/sfs_m_prm.c | 1545 ++++++++++ TBBT/trace_play/sfs_m_snc.c | 456 +++ TBBT/trace_play/sfs_m_xdr.c | 103 + TBBT/trace_play/sfs_mcr | 170 ++ TBBT/trace_play/sfs_mgr | 1093 +++++++ TBBT/trace_play/sfs_rc | 149 + TBBT/trace_play/sfs_suchown | 71 + TBBT/trace_play/sfs_sync.x | 25 + TBBT/trace_play/t | Bin 0 -> 11546 bytes TBBT/trace_play/t.c | 6 + 152 files changed, 71768 insertions(+) create mode 100644 TBBT/README create mode 100755 TBBT/trace_init/EXAMPLES create mode 100755 TBBT/trace_init/INSTALL create mode 100755 TBBT/trace_init/NOTES.TXT create mode 100755 TBBT/trace_init/commands create mode 100755 TBBT/trace_init/common.pl create mode 100755 TBBT/trace_init/counts.pl create mode 100755 TBBT/trace_init/extract-hierarchy create mode 100755 TBBT/trace_init/hier.pl create mode 100755 TBBT/trace_init/hier.pl.old create mode 100755 TBBT/trace_init/key.pl create mode 100755 TBBT/trace_init/latency.pl create mode 100755 TBBT/trace_init/nfsdump.pl create mode 100755 TBBT/trace_init/nfsscan.pl create mode 100755 TBBT/trace_init/nfsscan.txt create mode 100755 TBBT/trace_init/ns_loc2gmt.pl create mode 100755 TBBT/trace_init/ns_quickview.pl create mode 100755 TBBT/trace_init/ns_split.pl create mode 100755 TBBT/trace_init/ns_timeagg.pl create mode 100755 TBBT/trace_init/ns_tsplit.pl create mode 100755 TBBT/trace_init/rfs.pl create mode 100755 TBBT/trace_init/rfs.pl.old create mode 100755 TBBT/trace_init/rfs.stub.pl create mode 100644 TBBT/trace_init/test.cnt create mode 100755 TBBT/trace_init/test.fil create mode 100755 TBBT/trace_init/userUtils.pl create mode 100644 TBBT/trace_play/Makefile.org create mode 100644 TBBT/trace_play/README create mode 100644 TBBT/trace_play/README.old create mode 100755 TBBT/trace_play/agefs create mode 100644 TBBT/trace_play/agefs.log create mode 100755 TBBT/trace_play/frag_collect create mode 100755 TBBT/trace_play/frag_collect_cust create mode 100755 TBBT/trace_play/frag_count create mode 100644 TBBT/trace_play/frag_count.c create mode 100755 TBBT/trace_play/fragment_collect create mode 100755 TBBT/trace_play/generate_xmgr create mode 100644 TBBT/trace_play/generate_xmgr.c create mode 100644 TBBT/trace_play/generate_xmgr.c.old create mode 100644 TBBT/trace_play/generic_hash.c create mode 100644 TBBT/trace_play/generic_hash.h create mode 100644 TBBT/trace_play/hash.h create mode 100644 TBBT/trace_play/init_holder.c create mode 100644 TBBT/trace_play/nfsd_nfsfh_cust.h create mode 100755 TBBT/trace_play/profile.c create mode 100755 TBBT/trace_play/profile.h create mode 100644 TBBT/trace_play/rfs_3_ops.c create mode 100644 TBBT/trace_play/rfs_3_ops.c.old create mode 100644 TBBT/trace_play/rfs_assert.h create mode 100644 TBBT/trace_play/rfs_c_age.c create mode 100644 TBBT/trace_play/rfs_c_age.c.trace_base create mode 100644 TBBT/trace_play/rfs_c_age.c.unit_base create mode 100644 TBBT/trace_play/rfs_c_age.obsolete.c create mode 100644 TBBT/trace_play/rfs_c_dat.c create mode 100644 TBBT/trace_play/rfs_c_def.h create mode 100755 TBBT/trace_play/rpc/auth.h create mode 100755 TBBT/trace_play/rpc/auth_none.c create mode 100755 TBBT/trace_play/rpc/auth_unix.c create mode 100755 TBBT/trace_play/rpc/auth_unix.h create mode 100755 TBBT/trace_play/rpc/authunix_prot.c create mode 100755 TBBT/trace_play/rpc/bindresvport.c create mode 100755 TBBT/trace_play/rpc/clnt.h create mode 100755 TBBT/trace_play/rpc/clnt_generic.c create mode 100755 TBBT/trace_play/rpc/clnt_perror.c create mode 100755 TBBT/trace_play/rpc/clnt_simple.c create mode 100755 TBBT/trace_play/rpc/clnt_tcp.c create mode 100755 TBBT/trace_play/rpc/clnt_udp.c create mode 100755 TBBT/trace_play/rpc/get_myaddress.c create mode 100755 TBBT/trace_play/rpc/getrpcent.c create mode 100755 TBBT/trace_play/rpc/getrpcport.c create mode 100755 TBBT/trace_play/rpc/netdb.h create mode 100755 TBBT/trace_play/rpc/osdep.h create mode 100755 TBBT/trace_play/rpc/pmap_clnt.c create mode 100755 TBBT/trace_play/rpc/pmap_clnt.h create mode 100755 TBBT/trace_play/rpc/pmap_getmaps.c create mode 100755 TBBT/trace_play/rpc/pmap_getport.c create mode 100755 TBBT/trace_play/rpc/pmap_prot.c create mode 100755 TBBT/trace_play/rpc/pmap_prot.h create mode 100755 TBBT/trace_play/rpc/pmap_prot2.c create mode 100755 TBBT/trace_play/rpc/pmap_rmt.c create mode 100755 TBBT/trace_play/rpc/pmap_rmt.h create mode 100755 TBBT/trace_play/rpc/rpc.h create mode 100755 TBBT/trace_play/rpc/rpc_callmsg.c create mode 100755 TBBT/trace_play/rpc/rpc_commondata.c create mode 100755 TBBT/trace_play/rpc/rpc_dtablesize.c create mode 100755 TBBT/trace_play/rpc/rpc_msg.h create mode 100755 TBBT/trace_play/rpc/rpc_prot.c create mode 100755 TBBT/trace_play/rpc/sfs_ctcp.c create mode 100755 TBBT/trace_play/rpc/sfs_cudp.c create mode 100755 TBBT/trace_play/rpc/svc.c create mode 100755 TBBT/trace_play/rpc/svc.h create mode 100755 TBBT/trace_play/rpc/svc_auth.c create mode 100755 TBBT/trace_play/rpc/svc_auth.h create mode 100755 TBBT/trace_play/rpc/svc_auth_unix.c create mode 100755 TBBT/trace_play/rpc/svc_raw.c create mode 100755 TBBT/trace_play/rpc/svc_run.c create mode 100755 TBBT/trace_play/rpc/svc_simple.c create mode 100755 TBBT/trace_play/rpc/svc_tcp.c create mode 100755 TBBT/trace_play/rpc/svc_udp.c create mode 100755 TBBT/trace_play/rpc/types.h create mode 100755 TBBT/trace_play/rpc/xdr.c create mode 100755 TBBT/trace_play/rpc/xdr.h create mode 100755 TBBT/trace_play/rpc/xdr_array.c create mode 100755 TBBT/trace_play/rpc/xdr_float.c create mode 100755 TBBT/trace_play/rpc/xdr_mem.c create mode 100755 TBBT/trace_play/rpc/xdr_rec.c create mode 100755 TBBT/trace_play/rpc/xdr_reference.c create mode 100755 TBBT/trace_play/rpc/xdr_stdio.c create mode 100644 TBBT/trace_play/sem.c create mode 100755 TBBT/trace_play/sfs.1 create mode 100755 TBBT/trace_play/sfs3.full_speed create mode 100644 TBBT/trace_play/sfs_2_ops.c create mode 100644 TBBT/trace_play/sfs_2_vld.c create mode 100644 TBBT/trace_play/sfs_2_xdr.c create mode 100644 TBBT/trace_play/sfs_3_ops.c create mode 100644 TBBT/trace_play/sfs_3_ops.c.org create mode 100644 TBBT/trace_play/sfs_3_vld.c create mode 100644 TBBT/trace_play/sfs_3_xdr.c create mode 100644 TBBT/trace_play/sfs_c_bio.c create mode 100644 TBBT/trace_play/sfs_c_bio.org create mode 100644 TBBT/trace_play/sfs_c_chd.2thread.c create mode 100644 TBBT/trace_play/sfs_c_chd.3thread.c create mode 120000 TBBT/trace_play/sfs_c_chd.c create mode 100644 TBBT/trace_play/sfs_c_chd.c.old create mode 100644 TBBT/trace_play/sfs_c_chd.c.old.2 create mode 100644 TBBT/trace_play/sfs_c_chd.c.org create mode 100644 TBBT/trace_play/sfs_c_chd.work create mode 100644 TBBT/trace_play/sfs_c_clk.c create mode 100644 TBBT/trace_play/sfs_c_clnt.c create mode 100644 TBBT/trace_play/sfs_c_dat.c create mode 100644 TBBT/trace_play/sfs_c_def.h create mode 100644 TBBT/trace_play/sfs_c_dmp.c create mode 100644 TBBT/trace_play/sfs_c_man.c create mode 100644 TBBT/trace_play/sfs_c_mnt.c create mode 100644 TBBT/trace_play/sfs_c_nfs.h create mode 100644 TBBT/trace_play/sfs_c_pnt.c create mode 100644 TBBT/trace_play/sfs_c_rnd.c create mode 100644 TBBT/trace_play/sfs_c_sig.c create mode 100644 TBBT/trace_play/sfs_c_sub.c create mode 100755 TBBT/trace_play/sfs_ext_mon create mode 100644 TBBT/trace_play/sfs_m_def.h create mode 100644 TBBT/trace_play/sfs_m_msg.c create mode 100644 TBBT/trace_play/sfs_m_prm.c create mode 100644 TBBT/trace_play/sfs_m_snc.c create mode 100644 TBBT/trace_play/sfs_m_xdr.c create mode 100755 TBBT/trace_play/sfs_mcr create mode 100755 TBBT/trace_play/sfs_mgr create mode 100755 TBBT/trace_play/sfs_rc create mode 100755 TBBT/trace_play/sfs_suchown create mode 100755 TBBT/trace_play/sfs_sync.x create mode 100755 TBBT/trace_play/t create mode 100644 TBBT/trace_play/t.c 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 0000000000000000000000000000000000000000..bdebf34bc10dcc1235dc4fb5a3415a95b7d6ecbd GIT binary patch literal 83979 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%0DkCKlZX))p%JLRy7aofn{+!^Hg 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.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 0000000000000000000000000000000000000000..e1cdae0c203dbc002dfef9101cd677150860f3ae GIT binary patch literal 26012 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a44ed3393dc5a21d850301df1506f676c35a7b2c GIT binary patch literal 26083 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 +#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 0000000000000000000000000000000000000000..5237e99301bbf36d98a995526c3757787e6c530d GIT binary patch literal 1004158 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 E3lf{3xc~qF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9686a681a22296fbe1d13ccd3f8077f0af398055 GIT binary patch literal 11546 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 Pi$$Cl;B3eL-R%DWqfDn` literal 0 HcmV?d00001 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); +} -- 2.20.1

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