]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
nhfsstone remove due to license uncertainty.
authorNeil Brown <neilb@suse.de>
Mon, 18 Dec 2006 22:44:17 +0000 (09:44 +1100)
committerNeil Brown <neilb@suse.de>
Mon, 18 Dec 2006 22:44:17 +0000 (09:44 +1100)
It isn't clear the the License (See DISCLAIMER) is GPLv2 compatable,
and it is believed that the code is largely unused, so get rid
of nhfsstone just to be safe.

Signed-off-by: NeilBrown <neilb@suse.de>
14 files changed:
configure.in
utils/Makefile.am
utils/nhfsstone/DISCLAIMER [deleted file]
utils/nhfsstone/Makefile.am [deleted file]
utils/nhfsstone/README [deleted file]
utils/nhfsstone/README.linux [deleted file]
utils/nhfsstone/nhfsgraph [deleted file]
utils/nhfsstone/nhfsgraph.man [deleted file]
utils/nhfsstone/nhfsnums [deleted file]
utils/nhfsstone/nhfsnums.man [deleted file]
utils/nhfsstone/nhfsrun [deleted file]
utils/nhfsstone/nhfsrun.man [deleted file]
utils/nhfsstone/nhfsstone.c [deleted file]
utils/nhfsstone/nhfsstone.man [deleted file]

index 0ccc6b201d65053377be2d752a81ce35d5d45e8a..7cec0020cb60b6085c84aafa780403c2206c1d13 100644 (file)
@@ -333,7 +333,6 @@ AC_CONFIG_FILES([
        utils/mountd/Makefile
        utils/nfsd/Makefile
        utils/nfsstat/Makefile
        utils/mountd/Makefile
        utils/nfsd/Makefile
        utils/nfsstat/Makefile
-       utils/nhfsstone/Makefile
        utils/rquotad/Makefile
        utils/showmount/Makefile
        utils/statd/Makefile])
        utils/rquotad/Makefile
        utils/showmount/Makefile
        utils/statd/Makefile])
index 9cdb4ea55cb5dcb1d73dfc1c29896711a653864b..723657c7c69463d1fd047f9fd319a40064232e16 100644 (file)
@@ -24,7 +24,6 @@ SUBDIRS = \
        mountd \
        nfsd \
        nfsstat \
        mountd \
        nfsd \
        nfsstat \
-       nhfsstone \
        showmount \
        statd \
        $(OPTDIRS)
        showmount \
        statd \
        $(OPTDIRS)
diff --git a/utils/nhfsstone/DISCLAIMER b/utils/nhfsstone/DISCLAIMER
deleted file mode 100644 (file)
index afde6a3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-@(#)DISCLAIMER 1.4 89/07/07 Legato Systems, Inc.
-
-       IMPORTANT. READ BEFORE USING. USE OF THE PROGRAM WILL
-       CONSTITUTE ACCEPTANCE OF THE FOLLOWING LICENSE TERMS.
-
-Legato nhfsstone source code is a copyrighted product of Legato
-Systems, Inc. and is provided for unrestricted use and distribution of
-the binary program derived from it.
-
-You may copy Legato nhfsstone source, object code and related
-documentation as necessary, but are not authorized to license it to
-anyone else. Legato nhfsstone may be modified only for the purpose of
-porting. If the basic algorithms are changed the resulting program may
-not be called nhfsstone.
-
-Legato nhfsstone is provided with no support and without any obligation
-on the part of Legato Systems, Inc. to assist in its use, correction,
-modification or enhancement.
-
-LEGATO NHFSSTONE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
-INCLUDING THE WARRANTIES OF DESIGN, MERCHANTIBILITY, FITNESS FOR A
-PARTICULAR PURPOSE OR NONINFRINGEMENT, OR ARISING FROM A COURSE OF
-DEALING, USAGE OR TRADE PRACTICE.
-
-LEGATO SYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
-INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY LEGATO
-NHFSSTONE, ANY PART THEREOF OR THE USE THEREOF.
-
-IN NO EVENT WILL LEGATO SYSTEMS, INC. BE LIABLE UNDER ANY CONTRACT,
-NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY FOR ANY LOST REVENUE OR
-PROFITS OR OTHER SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR COST OF
-PROCUREMENT OF SUBSTITUTE GOODS OR TECHNOLOGY, EVEN IF LEGATO HAS BEEN
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/utils/nhfsstone/Makefile.am b/utils/nhfsstone/Makefile.am
deleted file mode 100644 (file)
index 43e9fda..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-man8_MANS      = nhfsstone.man nhfsrun.man nhfsnums.man nhfsgraph.man
-EXTRA_DIST     = $(man8_MANS) DISCLAIMER README.linux
-
-dist_sbin_SCRIPTS = nhfsrun nhfsnums nhfsgraph
-
-sbin_PROGRAMS  = nhfsstone
-nhfsstone_SOURCES = nhfsstone.c
-nhfsstone_LDADD = ../../support/export/libexport.a \
-                 ../../support/nfs/libnfs.a \
-                 ../../support/misc/libmisc.a
-
-MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/nhfsstone/README b/utils/nhfsstone/README
deleted file mode 100644 (file)
index f13dde5..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-@(#)README 1.6 89/07/07 Legato Systems, Inc.
-
-This directory contains the source for the nhfsstone (pronounced
-n-f-s-stone, the "h" is silent) NFS load generating program. This
-version of the program can only be compiled on 4.x BSD based UNIX
-systems.
-
-nhfsstone is used on an NFS client to generate an artificial load
-with a particular mix of NFS operations. It reports the average
-response time of the server in milliseconds per call and the load in
-calls per second.  The program adjusts its calling patterns based on
-the client's kernel NFS statistics and the elapsed time.  Load can be
-generated over a given time or number of NFS calls. See the "nhfsstone.1"
-manual page for more details.
-
-The files in this directory are:
-
-       DISCLAIMER      legal requirements
-       Makefile        Makefile used to build nhfsstone
-       README          This file
-       nhfsstone.c     source file
-       nhfsstone.1     manual page
-       nhfsrun         shell script to run nhfsstone over multiple loads
-       nhfsnums        shell script to convert nhfsrun output to plot(5)
-       nhfsgraph       shell script to create a graph from nhfsnums output
-
-The file "nhfsstone.1" is a manual page that describes how to use the
-nhfsstone program. To look at it type "nroff -man nhfsstone.1".
-
-To build an executable type "make nhfsstone". To install it, become
-super-user and then type "make install". This will strip the
-executable, set the group to "kmem" and set the setgid bit. If your
-site requires different installation of programs that read /dev/kmem
-you may have to use different ownership or permissions. Make install
-will also set the execute bits on the shell scripts nhfsrun, nhfsnums
-and nhfsgraph.
-
-To run an nhfsstone test, create a parent test directory on a filesystem
-that is NFS mounted, cd to that directory and type "nhfsstone". This will
-do a run with the default settings, load = 30 calls/sec, 5000 calls,
-and 7 sub-processes.
-
-If you want to spread the load across several server disks, first
-figure out on the server which disk partitions are exported as which
-filesystems. If you don't already have more than one of these
-filesystems mounted on your test client you can mount them in temporary
-locations, like /mnt. Create test directories on these filesystems so
-that the load will be distributed according to the simulation that you
-want to run (for example, you might put 4 test directories on the
-filesystem where the diskless client's root and swap live, and 2 on the
-home directories filesystem, and one on the executables filesystem).
-Now create a parent test directory cd to it, and make symbolic links
-with the names testdir0, testdir1, ... testdir6, that point to the
-real test directories. Finally, run nhfsstone from the parent test
-directory.
-
-If you are doing the test from a diskless machine, putting half of the
-test directories in /tmp or /usr/tmp and running the test from your
-home directory will simulate real diskless load patterns fairly well.
-
-To do a run over multiple load levels, edit the shell script "nhfsrun" and
-set the shell variables "START", "END", and "INCR" to be the correct
-starting and ending loads, and load increment. The script will iterate
-from START to END with an increment of INCR, run nhfsstone at each
-load level, and put the output in the file "run.out". The output file
-name can be changed by editing the nhfsrun script and changing the
-"OUTFILE" variable or by passing a file name suffix on the command line:
-
-       nhfsrun xysd
-
-This produces the output file "run.xysd".
-
-The script "nhfsnums" takes the output from nhfsrun and converts it
-into plot(5) format so that it can be graphed using graph(1) and other
-tools. It takes its input either from files given on the command line
-or from standard in:
-
-       nhfsnums [numsfile] ...
-
-If file names are given, the suffix of each name (the part after the
-".") is used as the line label for the set of numbers produced (see
-plot(5)).
-
-"nhfsgraph" takes the output from nhfsnums and passes it to graph(1)
-with the right arguments to produce PostScript output for a labeled
-graph. The nhfsgraph script can be used as a filter:
-
-       nhfsnums run.* | nhfsgraph | lpr
-
-
-
-
-This program is provided free of charge to anyone who wants it provided
-certain conditions are met (see DISCLAIMER file for more details).
-
-If you would like to receive regular information and bug fixes please
-send your name, and both your Email and U.S. mail addresses to:
-
-       Legato Systems, Inc.
-       Nhfsstone
-       260 Sheridan Avenue
-       Palo Alto, California  94306
-
-       nhfsstone-request@legato.com or uunet!legato.com!nhfsstone-request
-
-and we will add your name to the nhfsstone mailing list. Comments and bug
-reports should be sent to:
-
-       nhfsstone@legato.com or uunet!legato.com!nhfsstone
-
-
diff --git a/utils/nhfsstone/README.linux b/utils/nhfsstone/README.linux
deleted file mode 100644 (file)
index e9b7899..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- This is my port of nhfsstone to Linux. As a benchmark, it has been
- superseded by LADDIS (but unfortunately, LADDIS comes with a 1200 buck
- price tag), but it's quite good at catching NFS bugs :-)
-
- Of course, this port does not work with the old NFS client code, as
- it does not collect RPC stats.
-
- Olaf
-
diff --git a/utils/nhfsstone/nhfsgraph b/utils/nhfsstone/nhfsgraph
deleted file mode 100755 (executable)
index 56e2c77..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-#
-# @(#)nhfsgraph.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
-#
-# See DISCLAIMER file for restrictions
-#
-
-#
-# Usage: nhfsgraph <graphfile> ...
-#
-# Produce a PostScript graph of nhfsstone numbers.
-# Graphfile is a file with number pairs in plot(5) format derived
-# from runs of nhfsstone at different loads (see "nhfsrun" and "nhfsnums"
-# scripts.
-#
-# If you want something other than PostScript output replace "psplot"
-# with "plot". See plot(1) for more details.
-#
-
-LABEL="x=Load (calls/sec) y=Response (msec/call)"
-
-cat $* \
-    | graph -b -u .1 -h 1.2 -g 2 -l "$LABEL" -x 10 80 10 | psplot
diff --git a/utils/nhfsstone/nhfsgraph.man b/utils/nhfsstone/nhfsgraph.man
deleted file mode 100644 (file)
index 4ce080f..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-.TH NHFSGRAPH 8 "26 Feb 2000"
-.SH NAME
-nhfsgraph \- Run
-.B nhfsstone
-over multiple loads
-.SH SYNOPSIS
-.B nhfsgraph <plotfile>
-.SH DESCRIPTION
-Produce a PostScript graph of nhfsstone numbers.
-The input
-.B <plotfile>
-must contain
-.BR plot (5)
-number pairs derived from runs of
-.B nhfsstone 
-at different loads.
-.PP
-If you want something other than PostScript output, edit the
-script and replace
-.B psplot
-with
-.BR plot .
-.SH SEE ALSO
-.BR nhfsstone (8),
-.BR nhfsrun (8),
-.BR nhfsnums (8),
-.BR plot (5),
-.BR plot (1),
-.BR psplot (1)
diff --git a/utils/nhfsstone/nhfsnums b/utils/nhfsstone/nhfsnums
deleted file mode 100755 (executable)
index aae625d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-#
-# @(#)nhfsnums.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
-#
-# See DISCLAIMER file for restrictions
-#
-
-#
-# Usage: nhfsnums <numsfile> ...
-#
-# Collect raw numbers from nhfsstone output and print in plot(5) format.
-# The nums files should be named "run.xxx" where xxx is a name related
-# to the numbers gathered. Each file will produce one line with a label
-# that is the file suffix (the part following the dot.)
-#
-
-for i in $*; do
-       RUNNAME=`echo $i | sed -e 's/.*\\.//'`
-       awk '{ print $5 "       " $7 }' $i \
-           | sort -n\
-           | sed -e "\$s/\$/   \"$RUNNAME\"/"
-done
diff --git a/utils/nhfsstone/nhfsnums.man b/utils/nhfsstone/nhfsnums.man
deleted file mode 100644 (file)
index 28e9332..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-.TH NHFSNUMS 8 "26 Feb 2000"
-.SH NAME
-nhfsnums \- Convert
-.B nhfsrun
-output to
-.BR plot (5)
-.SH SYNOPSIS
-.B nhfsnums <numsfiles>
-.SH DESCRIPTION
-Converts raw numbers from nhfsstone output into
-.BR plot (5)
-format.  The
-.B <numsfiles>
-should be named 
-.BR run.xxx ,
-where
-.B xxx
-is a name related to the numbers gathered.
-Each file will produce one line with a label
-that is the file suffix (the part following the dot).
-.SH SEE ALSO
-.BR nhfsstone (8),
-.BR nhfsrun (8),
-.BR nhfsgraph (8)
diff --git a/utils/nhfsstone/nhfsrun b/utils/nhfsstone/nhfsrun
deleted file mode 100755 (executable)
index ce77ace..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/bin/sh
-#
-# @(#)nhfsrun.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
-#
-# See DISCLAIMER file for restrictions
-#
-
-#
-# Usage: nhfsrun [suffix]
-#
-# Run nhfsstone with a range of different loads and put
-# results in a file called run.<suffix>
-#
-
-if [ $# -gt 1 ]; then
-       echo "usage: $0 [suffix]"
-       exit 1
-fi
-
-#
-# Output file
-#
-if [ $# -eq 1 ]; then
-       OUTFILE=run.$1
-else
-       OUTFILE=run.out
-fi
-
-#
-# Starting load
-#
-START=10
-
-#
-# Ending load
-#
-END=80
-
-#
-# Load increment
-#
-INCR=10
-
-#
-# Catch SIGUSR1 and ignore it.
-# SIGUSR1 is used by nhfsstone to synchronize child processes.
-#
-nothing() { echo -n ""; }
-trap nothing USR1
-
-rm -f $OUTFILE
-
-LOAD=$START
-while [ $LOAD -le $END ]; do
-       echo nhfsstone -l $LOAD
-       nhfsstone -l $LOAD >> $OUTFILE
-       tail -n 1 $OUTFILE
-       LOAD=`expr $LOAD + $INCR`
-done
diff --git a/utils/nhfsstone/nhfsrun.man b/utils/nhfsstone/nhfsrun.man
deleted file mode 100644 (file)
index 63fdf7f..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-.TH NHFSRUN 8 "26 Feb 2000"
-.SH NAME
-nhfsrun \- Run
-.B nhfsstone
-over multiple loads
-.SH SYNOPSIS
-.B nhfsrun <suffix>
-.SH DESCRIPTION
-Runs
-.B nhfsstone
-with a range of different loads and put results in a file called
-.BR run.<suffix> .
-.SH SEE ALSO
-.BR nhfsstone (8),
-.BR nhfsnums (8),
-.BR nhfsgraph (8)
diff --git a/utils/nhfsstone/nhfsstone.c b/utils/nhfsstone/nhfsstone.c
deleted file mode 100644 (file)
index 463bcb5..0000000
+++ /dev/null
@@ -1,1820 +0,0 @@
-#if 0
-static char sccsid[] = "@(#)nhfsstone.c 1.22 90/05/08 Copyright (c) 1990, Legato Systems Inc";
-#endif
-
-/*
- * Copyright (c) 1990 Legato Systems Inc.
- *
- * See DISCLAIMER file for restrictions
- *
- * Ported to Linux by Olaf Kirch <okir@monad.swb.de>
- */
-
-#include "config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <sys/time.h>
-#include <sys/vfs.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#ifdef BSD
-#include <sys/dir.h>
-#define        dirent  direct
-#else
-#include <dirent.h>
-#endif
-#include <signal.h>
-
-#ifndef NULL
-#define NULL   0
-#endif
-
-/*
- * Usage: nhfsstone [-v] [[-t secs] | [-c calls]] [-l load] [-p nprocs]
- *                                             [-m mixfile] [dir]...
- *
- * Generates an artifical NFS client load based on a given mix of
- * operations.
- *
- * 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 difference
- * between the current ops percentages (derived from kernel NFS stats)
- * and a set of default percentages or a mix supplied by the caller.
- *
- * The generator routines try very hard to guess how many NFS operations
- * they are generating so that the calling routine can keep a running
- * estimate of the number of calls and the mix to avoid having to get
- * the NFS statistics from the kernel too often.
- *
- * The operations are done in a directory that has a set of file names
- * that are long enough that they won't be cached by the name cache
- * in the kernel. The "lookup" operation steps through the names and
- * creates a file if that name does not exist, or closes and reopens it
- * if it does. This generates a table of open file descriptors. Most of the
- * other operations are done on random descriptors in the table. The "getattr"
- * operation tries to avoid the kernel attribute cache by doing "fstat"
- * system calls on random descriptors in the table. There must be enough
- * files in the directory so that, on average, the getattr operation hits
- * any file less often than once each 6 seconds (the default timeout for
- * the attributes cache).
- *
- * 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
- * statics and prints them out.
- *
- * This code will only compile and run on 4.X BSD based systems.
- */
-
-#define        DEFAULT_LOAD    30              /* default calls per sec */
-#define        DEFAULT_CALLS   5000            /* default number of calls */
-#define        NFILES          40              /* number of test files/dir */
-#define        BUFSIZE         8192            /* block size for read and write */
-#define        MAXFILESIZE     32              /* size, in blocks, of large file */
-#define        SAMPLETIME      5               /* secs between samples of NFS stats */
-#define        NPROCS          7               /* number of children to run */
-
-
-/*
- * The names of NFS operations
- */
-char *Opnames[] = {
-       "null", "getattr", "setattr", "root", "lookup", "readlink", "read",
-       "wrcache", "write", "create", "remove", "rename", "link", "symlink",
-       "mkdir", "rmdir", "readdir", "fsstat",
-};
-
-/*
- * NFS operation numbers
- *
- * Used to index the Opnames, Mix and statistics arrays.
- */
-#define        NOPS            18              /* number of NFS ops */
-#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
-
-/*
- * Operations counts
- */
-struct count {
-       int             total;
-       int             calls[NOPS];
-};
-
-/*
- * Software development mix for server with about 50/50 mix of
- * diskless and diskful clients running SunOS 4.0.
- */
-int    Mix[NOPS] = {
-       0,              /* null */
-       13,             /* getattr */
-       1,              /* setattr */
-       0,              /* root */
-       34,             /* lookup */
-       8,              /* readlink */
-       22,             /* read */
-       0,              /* wrcache */
-       15,             /* write */
-       2,              /* create */
-       1,              /* remove */
-       0,              /* rename */
-       0,              /* link */
-       0,              /* symlink */
-       0,              /* mkdir */
-       0,              /* rmdir */
-       3,              /* readdir */
-       1,              /* fsstat */
-};
-
-/* Prototype decls */
-int    setmix(FILE *fp);
-void   usage(void);
-void   init_logfile(void);
-void   init_counters(void);
-void   get_delta(struct count *start, struct count *cur);
-void   init_testdir(int dirnum, char *parentdir);
-void   do_op(int rpct);
-void   op(int opnum);
-void   nextfile(void);
-int    createfile(void);
-int    openfile(void);
-int    writefile(void);
-void   collect_counters(void);
-int    check_counters(void);
-void   print(void);
-void   msec_sleep(int msecs);
-void   get_opct(struct count *count);
-int    substr(char *sp, char *subsp);
-int    check_access(struct stat statb);
-void   error(char *str);
-
-/*
- * NFS operations generator routines
- */
-int    op_null();
-int    op_getattr();
-int    op_setattr();
-int    op_root();
-int    op_lookup();
-int    op_readlink();
-int    op_read();
-int    op_wrcache();
-int    op_write();
-int    op_create();
-int    op_remove();
-int    op_rename();
-int    op_link();
-int    op_symlink();
-int    op_mkdir();
-int    op_rmdir();
-int    op_readdir();
-int    op_fsstat();
-
-/*
- * Operations generator vector
- */
-struct op_vect {
-       int     (*funct)();     /* op */
-} Op_vect[NOPS] = {
-       { op_null },
-       { op_getattr },
-       { op_setattr },
-       { op_root },
-       { op_lookup },
-       { op_readlink },
-       { op_read },
-       { op_wrcache },
-       { op_write },
-       { op_create },
-       { op_remove },
-       { op_rename },
-       { op_link },
-       { op_symlink },
-       { op_mkdir },
-       { op_rmdir },
-       { op_readdir },
-       { op_fsstat },
-};
-
-/*
- * Name sub-strings
- */
-#define        DIRSTR  "dir"                   /* directory */
-#define        SYMSTR  "sym"                   /* symbolic link */
-#define        LINSTR  "lin"                   /* hard link */
-
-struct timeval Optime[NOPS+1];         /* cumulative running time for ops */
-struct count Curct;                    /* total number ops called */
-int    Openfd[NFILES];                 /* open file descriptors */
-int    Curnum;                         /* current file number */
-int    Symnum;                         /* current symlink file number */
-int    Linknum;                        /* current link file number */
-int    Dirnum;                         /* current directory number */
-DIR    *Testdir;                       /* my test directory */
-char   Testdirname[MAXNAMLEN*2];       /* my test directory name */
-char   Curname[MAXNAMLEN];             /* current file name */
-char   Dirname[MAXNAMLEN];             /* current directory name */
-char   Symname[MAXNAMLEN];             /* symlink file name */
-char   Linkname[MAXNAMLEN];            /* link file name */
-char   *Otherspec = "%s/%03d";         /* sprintf spec for other names */
-char   *Rename1 = "rename1";           /* first name of rename pair */
-char   *Rename2 = "rename2";           /* second name of rename pair */
-char   *Symlinkpath = "./symlinknamelongstuff";
-                                       /* symlink file data */
-char   *Myname;                        /* name program invoked under */
-char   Namebuf[MAXNAMLEN];             /* unique name for this program */
-int    Log;                            /* synchronization log */
-char   Logname[MAXNAMLEN];             /* synchronization log name */
-int    Kmem;                           /* /dev/kmem file descriptor */
-off_t  Statoffset;                     /* offset to op count in NFS stats */
-int    Nprocs;                         /* sub-processes started */
-int    Verbose;                        /* print more info */
-int    Testop = -1;                    /* operation to test */
-int    Saveerrno;                      /* place to save errno */
-
-#define        subtime(t1, t2) {if ((t1.tv_usec -= t2.tv_usec) >= 1000000) {\
-                               t1.tv_sec += (t1.tv_usec / 1000000); \
-                               t1.tv_usec %= 1000000; \
-                        } else if (t1.tv_usec < 0) { \
-                               t1.tv_usec += 1000000; \
-                               t1.tv_sec--; \
-                        } \
-                        t1.tv_sec -= t2.tv_sec; \
-                       }
-
-#define        addtime(t1, t2) {if ((t1.tv_usec += t2.tv_usec) >= 1000000) {\
-                               t1.tv_sec += (t1.tv_usec / 1000000); \
-                               t1.tv_usec %= 1000000; \
-                        } else if (t1.tv_usec < 0) { \
-                               t1.tv_usec += 1000000; \
-                               t1.tv_sec--; \
-                        } \
-                        t1.tv_sec += t2.tv_sec; \
-                       }
-
-/*
- * Used to catch the parent's "start" signal
- */
-void
-startup()
-{
-
-       return;
-}
-
-/*
- * Clean up and exit
- */
-void
-cleanup()
-{
-
-       (void) unlink(Logname);
-       exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
-       int runtime;            /* length of run, in seconds */
-       int load;               /* load factor, in client loads */
-       int ncalls;             /* total number of calls to make */
-       int avgmspc;            /* average millisec per call */
-       int mspc;               /* millisec per call */
-       int wantcalls;          /* ncalls that should have happend by now */
-       int pid;                /* process id */
-       int delay;              /* msecs since last checked current time */
-       int randnum;            /* a random number */
-#if HAVE_SIGPROCMASK
-       sigset_t oldmask;       /* saved signal mask */
-#else
-       int oldmask;            /* saved signal mask */
-#endif
-       int sampletime;         /* secs between reading kernel stats */
-       char *opts;             /* option parsing */
-       int pct;
-       int procnum;
-       FILE *fp;
-       struct timeval curtime;
-       struct timeval starttime;
-       struct count startct;
-       struct stat statb;
-       char workdir[MAXPATHLEN];
-       char *getwd();
-
-       Myname = argv[0];
-
-       argc--;
-       argv++;
-
-       load = DEFAULT_LOAD;
-       ncalls = 0;
-       runtime = 0;
-       Nprocs = NPROCS;
-       pid = 0;
-
-       (void) umask(0);
-
-       /*
-        * Parse options
-        */
-       while (argc && **argv == '-') {
-               opts = &argv[0][1];
-               while (*opts) {
-                       switch (*opts) {
-
-                       case 'c':
-                               /*
-                                * Set number of calls
-                                */
-                               if (!isdigit(argv[1][0])) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal calls value %s\n",
-                                           Myname, argv[1]);
-                                       exit(1);
-                               }
-                               ncalls = atoi(argv[1]);
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 'l':
-                               /*
-                                * Set load
-                                */
-                               if (!isdigit(argv[1][0])) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal load value %s\n",
-                                           Myname, argv[1]);
-                                       exit(1);
-                               }
-                               load = atoi(argv[1]);
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 'm':
-                               /*
-                                * Set mix from a file
-                                */
-                               if ((fp = fopen(argv[1], "r")) == NULL) {
-                                       Saveerrno = errno;
-                                       (void) fprintf(stderr,
-                                           "%s: bad mix file", Myname);
-                                       errno = Saveerrno;
-                                       perror("");
-                                       exit(1);
-                               }
-                               if (setmix(fp) < 0) {
-                                       exit(1);
-                               }
-                               (void) fclose(fp);
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 'p':
-                               /*
-                                * Set number of child processes
-                                */
-                               if (!isdigit(argv[1][0])) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal procs value %s\n",
-                                           Myname, argv[1]);
-                                       exit(1);
-                               }
-                               Nprocs = atoi(argv[1]);
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 'T':
-                               /*
-                                * Set test mode, number following is opnum
-                                */
-                               if (!isdigit(argv[1][0])) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal test value %s\n",
-                                           Myname, argv[1]);
-                                       exit(1);
-                               }
-                               Testop = atoi(argv[1]);
-                               if (Testop >= NOPS) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal test value %d\n",
-                                           Myname, Testop);
-                                       exit(1);
-                               }
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 't':
-                               /*
-                                * Set running time
-                                */
-                               if (!isdigit(argv[1][0])) {
-                                       (void) fprintf(stderr,
-                                           "%s: illegal time value %s\n",
-                                           Myname, argv[1]);
-                                       exit(1);
-                               }
-                               runtime = atoi(argv[1]);
-                               argv++;
-                               argc--;
-                               break;
-
-                       case 'v':
-                               /*
-                                * Set verbose mode
-                                */
-                               Verbose++;
-                               break;
-
-                       default:
-                               usage();
-                               exit(1);
-
-                       }
-                       opts++;
-               }
-               argv++;
-               argc--;
-       }
-
-       init_logfile();         /* Set up synchronizatin log file */
-
-       if (getcwd(workdir, sizeof(workdir)) == (char *) 0) {
-               Saveerrno = errno;
-               (void) fprintf(stderr,
-                   "%s: can't find current directory ", Myname);
-               errno = Saveerrno;
-               perror("");
-               exit(1);
-       }
-
-       (void) signal(SIGINT, cleanup);
-       (void) signal(SIGUSR1, startup);
-#if HAVE_SIGPROCMASK
-       {
-               sigset_t mask;
-               sigemptyset(&mask);
-               sigaddset(&mask, SIGUSR1);
-               sigprocmask(SIG_BLOCK, &mask, &oldmask);
-       }
-#else
-       /*
-        * sigblock() is marked deprecated in modern
-        * glibc and hence generates a warning.
-        */
-       oldmask = sigblock(sigmask(SIGUSR1));
-#endif
-
-       if (ncalls == 0) {
-               if (runtime == 0) {
-                       ncalls = DEFAULT_CALLS;
-               } else {
-                       ncalls = runtime * load;
-               }
-       }
-       avgmspc = Nprocs * 1000 / load;
-
-       /*
-        * Fork kids
-        */
-       for (procnum = 0; procnum < Nprocs; procnum++) {
-               if ((pid = fork()) == -1) {
-                       Saveerrno = errno;
-                       (void) fprintf(stderr, "%s: can't fork ", Myname);
-                       errno = Saveerrno;
-                       perror("");
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-               /*
-                * Kids go initialize
-                */
-               if (pid == 0) {
-                       break;
-               }
-       }
-
-       /*
-        * Parent: wait for kids to get ready, start them, wait for them to
-        * finish, read and accumulate results.
-        */
-       if (pid != 0) {
-               /*
-                * wait for kids to initialize
-                */
-               do {
-                       sleep(1);
-                       if (fstat(Log, &statb) == -1) {
-                               (void) fprintf(stderr, "%s: can't stat log %s",
-                                   Myname, Logname);
-                               (void) kill(0, SIGINT);
-                               exit(1);
-                       }
-               } while (statb.st_size != Nprocs);
-
-               if (ftruncate(Log, 0L) == -1) {
-                       (void) fprintf(stderr, "%s: can't truncate log %s",
-                           Myname, Logname);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               sync();
-               sleep(3);
-
-               /*
-                * Be sure there isn't something else going on
-                */
-               get_opct(&startct);
-               msec_sleep(2000);
-               get_delta(&startct, &Curct);
-               if (Curct.total > 20) {
-                       (void) fprintf(stderr,
-                           "%s: too much background activity (%d calls/sec)\n",
-                           Myname, Curct.total);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               /*
-                * get starting stats
-                */
-               get_opct(&startct);
-
-               /*
-                * Start kids
-                */
-               (void) kill(0, SIGUSR1);
-
-               /*
-                * Kids started, wait for first one to finish, signal the
-                * rest and wait for them to finish.
-                */
-               if (wait((union wait *) 0) != -1) {
-                       (void) kill(0, SIGUSR1);
-                       while (wait((union wait *) 0) != -1)
-                               /* nothing */;
-               }
-
-               /*
-                * Initialize and sum up counters
-                */
-               init_counters();
-               get_delta(&startct, &Curct);
-               collect_counters();
-               if (check_counters() == -1) {
-                       Verbose = 1;
-               }
-               print();
-
-               (void) close(Log);
-               (void) unlink(Logname);
-
-               exit(0);
-       }
-
-       /*
-        * 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.
-        */
-
-       /*
-        * Change my name for error logging
-        */
-       (void) sprintf(Namebuf, "%s%d", Myname, procnum);
-       Myname = Namebuf;
-
-       /*
-        * Initialize and cd to test directory
-        */
-       if (argc != 0) {
-               init_testdir(procnum, argv[procnum % argc]);
-       } else {
-               init_testdir(procnum, ".");
-       }
-       if ((Testdir = opendir(".")) == NULL) {
-               Saveerrno = errno;
-               (void) fprintf(stderr,
-                   "%s: can't open test directory ", Myname);
-               errno = Saveerrno;
-               perror(Testdirname);
-               exit(1);
-       }
-
-       init_counters();
-       srandom(procnum+1);
-
-       /*
-        * Tell parent I'm ready then wait for go ahead
-        */
-       if (write(Log, " ", 1) != 1) {
-               (void) fprintf(stderr, "%s: can't write sync file %s",
-                   Myname, Logname);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-
-#if HAVE_SIGPROCMASK
-       sigsuspend(&oldmask);
-#else
-       sigpause(oldmask);
-#endif
-
-       /*
-        * Initialize counters
-        */
-       get_opct(&startct);
-       (void) gettimeofday(&starttime, (struct timezone *)NULL);
-       sampletime = starttime.tv_sec + ((int) random()) % (2 * SAMPLETIME);
-       curtime = starttime;
-
-       /*
-        * Do pseudo NFS operations and adapt to dynamic changes in load
-        * by adjusting the sleep time between operations based on the
-        * number of calls that should have occured since starttime and
-        * the number that have actually occured.  A delay is used to avoid
-        * doing gettimeofday calls too often, and a sampletime is
-        * used to avoid reading kernel NFS stats too often.
-        * If parent interrupts, get out and clean up.
-        */
-       delay = 0;
-       mspc = avgmspc;
-       for (;;) {
-               randnum = (int) random();
-               if (mspc > 0) {
-                       msec_sleep(randnum % (mspc << 1));
-               }
-
-               /*
-                * Do the NFS operation
-                * We use a random number from 0-199 to avoid starvation
-                * of the operations at the end of the mix.
-                */
-               do_op(randnum % 200);
-
-               /*
-                * Do a gettimeofday call only once per second
-                */
-               delay += mspc;
-               if (delay > 1000 || Curct.total >= ncalls) {
-                       delay = 0;
-                       (void) gettimeofday(&curtime, (struct timezone *)NULL);
-
-                       /*
-                        * If sample time is up, check the kernel stats
-                        * and adjust our parameters to either catch up or
-                        * slow down.
-                        */
-                       if (curtime.tv_sec > sampletime ||
-                           Curct.total >= ncalls) {
-                               sampletime = curtime.tv_sec + SAMPLETIME;
-                               get_delta(&startct, &Curct);
-                               if (Curct.total >= ncalls) {
-                                       break;
-                               }
-                               wantcalls =
-                                   ((curtime.tv_sec - starttime.tv_sec) * 1000
-                                   +(curtime.tv_usec-starttime.tv_usec) / 1000)
-                                   * Nprocs / avgmspc;
-                               pct = 1000 * (Curct.total - wantcalls) / ncalls;
-                               mspc = avgmspc + avgmspc * pct / 20;
-                               if (mspc <= 0) {
-                                       /*
-                                        * mspc must be positive or we will
-                                        * never advance time.
-                                        */
-                                       mspc = 10;
-                               }
-                       }
-               }
-       }
-
-       /*
-        * Store total time in last slot of counts array
-        */
-       Optime[NOPS].tv_sec = curtime.tv_sec - starttime.tv_sec;
-       Optime[NOPS].tv_usec = curtime.tv_usec - starttime.tv_usec;
-
-       /*
-        * write stats to log file (append mode)
-        */
-       if (write(Log, (char *)Optime, sizeof (Optime)) == -1) {
-               Saveerrno = errno;
-               (void) fprintf(stderr, "%s: can't write log ", Myname);
-               errno = Saveerrno;
-               perror("");
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-       (void) close(Log);
-
-       exit(0);
-}
-
-/*
- * Initialize test directory
- *
- * If the directory already exists, check to see that all of the
- * files exist and we can write them.  If directory doesn't exist
- * create it and fill it using the LOOKUP and WRITE ops.
- * Chdir to the directory.
- */
-void
-init_testdir(int dirnum, char *parentdir)
-{
-       int i;
-       int fd;
-       char cmd[256];
-       struct stat statb;
-
-       (void) sprintf(Testdirname, "%s/testdir%d", parentdir, dirnum);
-       if (stat(Testdirname, &statb) == -1) {
-               if (mkdir(Testdirname, 0777) == -1) {
-                       Saveerrno = errno;
-                       (void) fprintf(stderr,
-                           "%s: can't create test directory ", Myname);
-                       errno = Saveerrno;
-                       perror(Testdirname);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-               if (chdir(Testdirname) == -1) {
-                       Saveerrno = errno;
-                       (void) fprintf(stderr,
-                           "%s: can't cd to test directory ", Myname);
-                       errno = Saveerrno;
-                       perror(Testdirname);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               /*
-                * create some files with long names and average size
-                */
-               for (i = 0; i < NFILES; i++) {
-                       nextfile();
-                       (void) createfile();
-                       if (Openfd[Curnum] == 0 || writefile() == 0) {
-                               Saveerrno = errno;
-                               (void) fprintf(stderr,
-                                   "%s: can't create test file '%s'\n",
-                                   Myname, Curname);
-                               errno = Saveerrno;
-                               perror(Testdirname);
-                               (void) kill(0, SIGINT);
-                               exit(1);
-                       }
-               }
-       } else {
-               if (chdir(Testdirname) == -1) {
-                       Saveerrno = errno;
-                       (void) fprintf(stderr,
-                           "%s: can't cd to test directory ", Myname);
-                       errno = Saveerrno;
-                       perror(Testdirname);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               /*
-                * Verify that we can read and write the test dir
-                */
-               if (check_access(statb) == -1) {
-                       (void) fprintf(stderr,
-                           "%s: wrong permissions on test dir %s\n",
-                           Myname, Testdirname);
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               /*
-                * Verify that we can read and write all the files
-                */
-               for (i = 0; i < NFILES; i++) {
-                       nextfile();
-                       if (stat(Curname, &statb) == -1 || statb.st_size == 0) {
-                               /*
-                                * File doesn't exist or is 0 size
-                                */
-                               (void) createfile();
-                               if (Openfd[Curnum] == 0 || writefile() == 0) {
-                                       (void) kill(0, SIGINT);
-                                       exit(1);
-                               }
-                       } else if (check_access(statb) == -1) {
-                               /*
-                                * should try to remove and recreate it
-                                */
-                               (void) fprintf(stderr,
-                                   "%s: wrong permissions on testfile %s\n",
-                                   Myname, Curname);
-                               (void) kill(0, SIGINT);
-                               exit(1);
-                       } else if (Openfd[Curnum] == 0) {
-                               (void) openfile();
-                               if (Openfd[Curnum] == 0) {
-                                       (void) kill(0, SIGINT);
-                                       exit(1);
-                               }
-                       }
-               }
-       }
-
-       /*
-        * Start with Rename1 and no Rename2 so the
-        * rename op can ping pong back and forth.
-        */
-       (void) unlink(Rename2);
-       if ((fd = open(Rename1, O_CREAT|O_TRUNC|O_RDWR, 0666)) == -1) {
-               Saveerrno = errno;
-               (void) fprintf(stderr, "%s: can't create rename file ", Myname);
-               errno = Saveerrno;
-               perror(Rename1);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-
-       /*
-        * Remove and recreate the test sub-directories
-        * for mkdir symlink and hard link.
-        */
-       (void) sprintf(cmd, "rm -rf %s %s %s", DIRSTR, SYMSTR, LINSTR);
-       if (system(cmd) != 0) {
-               (void) fprintf(stderr, "%s: can't %s\n", Myname, cmd);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-
-       if (mkdir(DIRSTR, 0777) == -1) {
-               (void) fprintf(stderr,
-                   "%s: can't create subdir %s\n", Myname, DIRSTR);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-
-       if (mkdir(SYMSTR, 0777) == -1) {
-               (void) fprintf(stderr,
-                   "%s: can't create subdir %s\n", Myname, SYMSTR);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-       op(SYMLINK);
-
-       if (mkdir(LINSTR, 0777) == -1) {
-               (void) fprintf(stderr, "%s: can't create subdir %s\n", Myname,
-                   LINSTR);
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-
-       (void) close(fd);
-}
-
-/*
- * 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 number
- * of OTW calls they think they have generated.
- *
- * An array of open file descriptors is kept for the files in each
- * test directory. The open fd's are used to get access to the files
- * without generating lookups. An fd value of 0 mean the corresponding
- * file name is closed.  Ops that need a name use Curname.
- */
-
-/*
- * Call an op based on a random number and the current
- * op calling weights. Op weights are derived from the
- * mix percentage and the current NFS stats mix percentage.
- */
-void
-do_op(int rpct)
-{
-       int opnum;
-       int weight;
-       int oppct;
-
-       if (Testop != -1) {
-               nextfile();
-               op(Testop);
-               return;
-       }
-       for (opnum = rpct % NOPS; rpct >= 0; opnum = (opnum + 1) % NOPS) {
-               if (Curct.total) {
-                       oppct = (Curct.calls[opnum] * 100) / Curct.total;
-               } else {
-                       oppct = 0;
-               }
-               /*
-                * Weight is mix percent - (how far off we are * fudge)
-                * fudge factor is required because some ops (read, write)
-                * generate many NFS calls for a single op call
-                */
-               weight = Mix[opnum] - ((oppct - Mix[opnum]) << 4);
-               if (weight <= 0) {
-                       continue;
-               }
-               rpct -= weight;
-               if (rpct < 0) {
-                       if (opnum == RMDIR && Dirnum == 0) {
-                               op(MKDIR);
-                       } else if (opnum != CREATE && opnum != LOOKUP &&
-                           opnum != REMOVE) {
-                               nextfile();
-                       }
-                       op(opnum);
-                       if (Openfd[Curnum] == 0) {
-                               op(CREATE);
-#ifdef XXX
-                               op(WRITE);
-#endif /* XXX */
-                       }
-                       return;
-               }
-       }
-}
-
-/*
- * Call an op generator and keep track of its running time
- */
-void
-op(int opnum)
-{
-       struct timeval start;
-       struct timeval stop;
-       int nops;
-
-       (void) gettimeofday(&start, (struct timezone *)NULL);
-       nops = (*Op_vect[opnum].funct)();
-       (void) gettimeofday(&stop, (struct timezone *)NULL);
-       stop.tv_sec -= start.tv_sec;
-       stop.tv_usec -= start.tv_usec;
-
-#ifdef SUNOS4
-       /*
-        * SunOS 4.0 does a lookup and a getattr on each open
-        * so we have to account for that in the getattr op
-        */
-       if (opnum == GETATTR && nops == 2) {
-               nops = 1;
-               stop.tv_sec /= 2;
-               stop.tv_usec /= 2;
-               Curct.total += Nprocs;
-               Curct.calls[LOOKUP] += Nprocs;
-               addtime(Optime[LOOKUP], stop);
-       }
-#endif
-
-       nops *= Nprocs;
-       Curct.total += nops;
-       Curct.calls[opnum] += nops;
-       addtime(Optime[opnum], stop);
-}
-
-/*
- * Advance file number (Curnum) and name (Curname)
- */
-void
-nextfile(void)
-{
-       static char *numpart = NULL;
-       int num;
-
-       Curnum = (Curnum + 1) % NFILES;
-       if (numpart == NULL) {
-               (void) sprintf(Curname, "%03dabcdefghijklmn", Curnum);
-               numpart = Curname;
-       } else {
-               num = Curnum;
-               numpart[0] = '0' + num / 100;
-               num %= 100;
-               numpart[1] = '0' + num / 10;
-               num %= 10;
-               numpart[2] = '0' + num;
-       }
-}
-
-int
-createfile(void)
-{
-       int ret;
-       int fd;
-
-       ret = 0;
-       fd = Openfd[Curnum];
-
-       if ((fd && close(fd) == -1) ||
-           (fd = open(Curname, O_CREAT|O_RDWR|O_TRUNC, 0666)) == -1) {
-               fd = 0;
-               ret = -1;
-               error("create");
-       }
-       Openfd[Curnum] = fd;
-       return (ret);
-}
-
-int
-openfile(void)
-{
-       int ret;
-       int fd;
-
-       ret = 0;
-       fd = Openfd[Curnum];
-       if (fd == 0 && (fd = open(Curname, O_RDWR, 0666)) == -1) {
-               fd = 0;
-               ret = -1;
-               error("open");
-       }
-       Openfd[Curnum] = fd;
-       return (ret);
-}
-
-int
-writefile(void)
-{
-       int fd;
-       int wrote;
-       int bufs;
-       int size;
-       int randnum;
-       char buf[BUFSIZE];
-
-       fd = Openfd[Curnum];
-
-       if (lseek(fd, 0L, 0) == (off_t) -1) {
-               error("write: lseek");
-               return (-1);
-       }
-
-       randnum = (int) random();
-       bufs = randnum % 100;   /* using this for distribution desired */
-       /*
-        * Attempt to create a distribution of file sizes
-        * to reflect reality.  Most files are small,
-        * but there are a few files that are very large.
-        *
-        * The sprite paper (USENIX 198?) claims :
-        *      50% of all files are < 2.5K
-        *      80% of all file accesses are to files < 10K
-        *      40% of all file I/O is to files > 25K
-        *
-        * static examination of the files in our file system
-        * seems to support the claim that 50% of all files are
-        * smaller than 2.5K
-        */
-       if (bufs < 50)  {
-               bufs = (randnum % 3) + 1;
-               size = 1024;
-       } else if (bufs < 97) {
-               bufs = (randnum % 6) + 1;
-               size = BUFSIZE;
-       } else {
-               bufs = MAXFILESIZE;
-               size = BUFSIZE;
-       }
-
-       for (wrote = 0; wrote < bufs; wrote++) {
-               if (write(fd, buf, size) == -1) {
-                       error("write");
-                       break;
-               }
-       }
-
-       return (wrote);
-}
-
-int
-op_null(void)
-{
-
-       return (1);
-}
-
-
-/*
- * Generate a getattr call by fstat'ing the current file
- * or by closing and re-opening it. This helps to keep the
- * attribute cache cold.
- */
-int
-op_getattr(void)
-{
-       struct stat statb;
-
-       if ((random() % 2) == 0) {
-               (void) close(Openfd[Curnum]);
-               Openfd[Curnum] = 0;
-               if (openfile() == -1) {
-                       return (0);
-               }
-               return (2);
-       }
-       if (fstat(Openfd[Curnum], &statb) == -1) {
-               error("getattr");
-       }
-       return (1);
-}
-
-
-int op_setattr(void)
-{
-
-       if (fchmod(Openfd[Curnum], 0666) == -1) {
-               error("setattr");
-       }
-       return (1);
-}
-
-
-int op_root(void)
-{
-
-       error("root");
-       return (0);
-}
-
-
-/*
- * Generate a lookup by stat'ing the current name.
- */
-int op_lookup(void)
-{
-       struct stat statb;
-
-       if (stat(Curname, &statb) == -1) {
-               error("lookup");
-       }
-       return (1);
-}
-
-
-int op_read(void)
-{
-       int got;
-       int bufs;
-       int fd;
-       char buf[BUFSIZE];
-
-       bufs = 0;
-       fd = Openfd[Curnum];
-
-       if (lseek(fd, 0L, 0) == (off_t) -1) {
-               error("read: lseek");
-               return (0);
-       }
-
-       while ((got = read(fd, buf, sizeof (buf))) > 0) {
-               bufs++;
-       }
-
-       if (got == -1) {
-               error("read");
-       } else {
-               bufs++;         /* did one extra read to find EOF */
-       }
-       return (bufs);
-}
-
-
-int op_wrcache(void)
-{
-       error("wrcache");
-       return 0;
-}
-
-
-int op_write(void)
-{
-       int bufs;
-
-       bufs = writefile();
-       if (bufs == 0) {
-               return (0);
-       }
-       (void) fsync(Openfd[Curnum]);
-
-       return (bufs + 2);
-}
-
-
-int op_create(void)
-{
-
-       if (createfile() == -1) {
-               return (0);
-       }
-       return (1);
-}
-
-
-int op_remove(void)
-{
-       int fd;
-       int got;
-
-       if (Linknum > 0) {
-               got = unlink(Linkname);
-               Linknum--;
-               (void) sprintf(Linkname, Otherspec, LINSTR, Linknum);
-       } else if (Symnum > 1) {
-               got = unlink(Symname);
-               Symnum--;
-               (void) sprintf(Symname, Otherspec, SYMSTR, Symnum);
-       } else {
-               fd = Openfd[Curnum];
-
-               if (fd && (close(fd) == -1)) {
-                       error("remove: close");
-               }
-               Openfd[Curnum] = 0;
-               got = unlink(Curname);
-       }
-       if (got == -1) {
-               error("remove");
-       }
-       return (1);
-}
-
-
-int toggle = 0;
-
-int op_rename(void)
-{
-       int got;
-
-       if (toggle++ & 01) {
-               got = rename(Rename2, Rename1);
-       } else {
-               got = rename(Rename1, Rename2);
-       }
-       if (got == -1) {
-               error("rename");
-       }
-       return (1);
-}
-
-
-int op_link(void)
-{
-
-       Linknum++;
-       (void) sprintf(Linkname, Otherspec, LINSTR, Linknum);
-       if (link(Curname, Linkname) == -1) {
-               error("link");
-       }
-       return (1);
-}
-
-
-int op_readlink(void)
-{
-       char    buf[MAXPATHLEN];
-
-       if (Symnum == 0) {
-               error("readlink");
-               return (0);
-       }
-       if (readlink(Symname, buf, sizeof (buf)) == -1) {
-               error("readlink");
-       }
-       return (1);
-}
-
-
-int op_symlink(void)
-{
-
-       Symnum++;
-       (void) sprintf(Symname, Otherspec, SYMSTR, Symnum);
-       if (symlink(Symlinkpath, Symname) == -1) {
-               error("symlink");
-       }
-       return (1);
-}
-
-
-int op_mkdir(void)
-{
-
-       Dirnum++;
-       (void) sprintf(Dirname, Otherspec, DIRSTR, Dirnum);
-       if (mkdir(Dirname, 0777) == -1) {
-               error("mkdir");
-       }
-       return (1);
-}
-
-
-int op_rmdir(void)
-{
-
-       if (Dirnum == 0) {
-               error("rmdir");
-               return (0);
-       }
-       if (rmdir(Dirname) == -1) {
-               error("rmdir");
-       }
-       Dirnum--;
-       (void) sprintf(Dirname, Otherspec, DIRSTR, Dirnum);
-       return (1);
-}
-
-
-int op_readdir(void)
-{
-
-       rewinddir(Testdir);
-       while (readdir(Testdir) != (struct dirent *)NULL)
-               /* nothing */;
-       return (1);
-}
-
-
-int op_fsstat(void)
-{
-       struct statfs statfsb;
-
-       if (statfs(".", &statfsb) == -1) {
-               error("statfs");
-       }
-       return (1);
-}
-
-
-/*
- * Utility routines
- */
-
-/*
- * Read counter arrays out of log file and accumulate them in "Optime"
- */
-void
-collect_counters(void)
-{
-       int i;
-       int j;
-
-       (void) lseek(Log, 0L, 0);
-
-       for (i = 0; i < Nprocs; i++) {
-               struct timeval buf[NOPS+1];
-
-               if (read(Log, (char *)buf, sizeof (buf)) == -1) {
-                       Saveerrno = errno;
-                       (void) fprintf(stderr, "%s: can't read log ", Myname);
-                       errno = Saveerrno;
-                       perror("");
-                       (void) kill(0, SIGINT);
-                       exit(1);
-               }
-
-               for (j = 0; j < NOPS+1; j++) {
-                       addtime(Optime[j], buf[j]);
-               }
-       }
-}
-
-/*
- * Check consistance of results
- */
-int
-check_counters(void)
-{
-       int i;
-       int mixdiff;
-       int got;
-       int want;
-
-       mixdiff = 0;
-       for (i = 0; i < NOPS; i++) {
-               got = Curct.calls[i] * 10000 / Curct.total;
-               want = Mix[i] * 100;
-               if (got > want) {
-                       mixdiff += got - want;
-               } else {
-                       mixdiff += want - got;
-               }
-       }
-       if (mixdiff > 1000) {
-               (void) fprintf(stdout,
-                   "%s: INVALID RUN, mix generated is off by %d.%02d%%\n",
-                   Myname, mixdiff / 100, mixdiff % 100);
-               return (-1);
-       }
-       return (0);
-}
-
-/*
- * Print results
- */
-void
-print(void)
-{
-       int totalmsec;
-       int runtime;
-       int msec;
-       int i;
-
-       totalmsec = 0;
-       for (i = 0; i < NOPS; i++) {
-               totalmsec += Optime[i].tv_sec * 1000;
-               totalmsec += Optime[i].tv_usec / 1000;
-       }
-
-       if (Verbose) {
-          const char *format = sizeof (Optime[0].tv_sec) == sizeof (long)
-            ? "%-10s%3d%%    %2d.%02d%%   %6d   %4ld.%02ld    %4d.%02d    %2d.%02d%%\n"
-            : "%-10s%3d%%    %2d.%02d%%   %6d   %4d.%02d    %4d.%02d    %2d.%02d%%\n";
-               (void) fprintf(stdout,
-"op        want       got    calls      secs  msec/call    time %%\n");
-               for (i = 0; i < NOPS; i++) {
-                       msec = Optime[i].tv_sec * 1000
-                           + Optime[i].tv_usec / 1000;
-                       (void) fprintf(stdout, format,
-                           Opnames[i], Mix[i],
-                           Curct.calls[i] * 100 / Curct.total,
-                           (Curct.calls[i] * 100 % Curct.total)
-                               * 100 / Curct.total,
-                           Curct.calls[i],
-                           Optime[i].tv_sec, Optime[i].tv_usec / 10000,
-                           Curct.calls[i]
-                               ? msec / Curct.calls[i]
-                               : 0,
-                           Curct.calls[i]
-                               ? (msec % Curct.calls[i]) * 100 / Curct.calls[i]
-                               : 0,
-                           msec * 100 / totalmsec,
-                           (msec * 100 % totalmsec) * 100 / totalmsec);
-               }
-       }
-
-       runtime = Optime[NOPS].tv_sec / Nprocs;
-       (void) fprintf(stdout,
-           "%d sec %d calls %d.%02d calls/sec %d.%02d msec/call\n",
-           runtime, Curct.total,
-           Curct.total / runtime,
-           ((Curct.total % runtime) * 100) / runtime,
-           totalmsec / Curct.total,
-           ((totalmsec % Curct.total) * 100) / Curct.total);
-}
-
-/*
- * Use select to sleep for some number of milliseconds
- * granularity is 20 msec
- */
-void
-msec_sleep(int msecs)
-{
-       struct timeval sleeptime;
-
-       if (msecs < 20) {
-               return;
-       }
-       sleeptime.tv_sec = msecs / 1000;
-       sleeptime.tv_usec = (msecs % 1000) * 1000;
-
-       if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &sleeptime) == -1){
-               Saveerrno = errno;
-               (void) fprintf(stderr, "%s: select failed ", Myname);
-               errno = Saveerrno;
-               perror("");
-               (void) kill(0, SIGINT);
-               exit(1);
-       }
-}
-
-/*
- * Open the synchronization file with append mode
- */
-void
-init_logfile(void)
-{
-
-       (void) sprintf(Logname, "/tmp/nhfsstone%d", getpid());
-       if ((Log = open(Logname, O_RDWR|O_CREAT|O_TRUNC|O_APPEND, 0666)) == -1){
-               Saveerrno = errno;
-               (void) fprintf(stderr,
-                   "%s: can't open log file %s ", Myname, Logname);
-               errno = Saveerrno;
-               perror("");
-               exit(1);
-       }
-}
-
-/*
- * Zero counters
- */
-void
-init_counters(void)
-{
-       int i;
-
-       Curct.total = 0;
-       for (i = 0; i < NOPS; i++) {
-               Curct.calls[i] = 0;
-               Optime[i].tv_sec = 0;
-               Optime[i].tv_usec = 0;
-       }
-       Optime[NOPS].tv_sec = 0;
-       Optime[NOPS].tv_usec = 0;
-}
-
-/*
- * Set cur = cur - start
- */
-void
-get_delta(struct count *start, struct count *cur)
-{
-       int i;
-
-       get_opct(cur);
-       cur->total -= start->total;
-       for (i = 0; i < NOPS; i++) {
-               cur->calls[i] -= start->calls[i];
-       }
-}
-
-/*
- * Read kernel stats
- */
-void
-get_opct(struct count *count)
-{
-       static FILE     *fp = NULL;
-       char            buffer[256];
-       int i;
-
-       if (fp == NULL && !(fp = fopen("/proc/net/rpc/nfs", "r"))) {
-               perror("/proc/net/rpc/nfs");
-               (void) kill(0, SIGINT);
-               exit(1);
-       } else {
-               fflush(fp);
-               rewind(fp);
-       }
-
-       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
-               char    *sp, *line = buffer;
-
-               if ((sp = strchr(line, '\n')) != NULL)
-                       *sp = '\0';
-               if (!(sp = strtok(line, " \t")) || strcmp(line, "proc2"))
-                       continue;
-               if (!(sp = strtok(NULL, " \t")))
-                       goto bummer;
-               count->total = 0;
-               for (i = 0; i < 18; i++) {
-                       if (!(sp = strtok(NULL, " \t")))
-                               goto bummer;
-                       /* printf("call %d -> %s\n", i, sp); */
-                       count->calls[i] = atoi(sp);
-                       count->total += count->calls[i];
-               }
-               /* printf("total calls %d\n", count->total); */
-               break;
-       }
-
-       return;
-
-bummer:
-       fprintf(stderr, "parse error in /proc/net/rpc/nfs!\n");
-       kill(0, SIGINT);
-       exit(1);
-}
-
-#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
-
-/*
- * Mix file parser.
- * 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 ncalls      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
- */
-int
-setmix(FILE *fp)
-{
-       int state;
-       int got;
-       int opnum;
-       int calls;
-       int len;
-       char line[LINELEN];
-
-       state = MIX_START;
-       opnum = 0;
-
-       while (state != MIX_DONE && fgets(line, LINELEN, fp)) {
-
-               switch (state) {
-
-               case MIX_START:
-                       len = strlen(line);
-                       if (len >= 4 && substr(line, "nfs:")) {
-                               if (fgets(line, LINELEN, fp) == NULL) {
-                                       (void) fprintf(stderr,
-"%s: bad mix format: unexpected EOF after 'nfs:'\n", Myname);
-                                       return (-1);
-                               }
-                               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", Myname, got);
-                               return (-1);
-                       }
-                       if (fgets(line, LINELEN, fp) == NULL) {
-                               (void) fprintf(stderr,
-"%s: bad mix format: unexpected EOF after 'calls'\n", Myname);
-                               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%%",
-       &Mix[opnum], &Mix[opnum+1], &Mix[opnum+2], &Mix[opnum+3],
-       &Mix[opnum+4], &Mix[opnum+5], &Mix[opnum+6]);
-                       if (got == 4 && opnum == 14) {
-                               /*
-                                * looks like the last line
-                                */
-                               state = MIX_DONE;
-                       } else if (got == 7) {
-                               opnum += 7;
-                               if (fgets(line, LINELEN, fp) == NULL) {
-                                       (void) fprintf(stderr,
-"%s: bad mix format: unexpected EOF after 'calls'\n", Myname);
-                                       return (-1);
-                               }
-                       } else {
-                               (void) fprintf(stderr,
-"%s: bad mix format: can't find %d op values\n", Myname, got);
-                               return (-1);
-                       }
-                       break;
-               default:
-                       (void) fprintf(stderr,
-                           "%s: unknown state %d\n", Myname, state);
-                       return (-1);
-               }
-       }
-       if (state != MIX_DONE) {
-               (void) fprintf(stderr,
-                   "%s: bad mix format: unexpected EOF\n", Myname);
-               return (-1);
-       }
-       for (opnum = 0; opnum < NOPS; opnum++) {
-               Mix[opnum] = Mix[opnum] * 100 / calls
-                   + ((Mix[opnum] * 1000 / calls % 10) >= 5);
-       }
-       return (0);
-}
-
-/*
- * return true if sp contains the substring subsp, false otherwise
- */
-int
-substr(char *sp, char *subsp)
-{
-       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++;
-                       s2++;
-                       found++;
-               }
-               if (found == want) {
-                       return (1);
-               }
-       }
-       return (0);
-}
-
-/*
- * check to make sure that we have
- * both read and write permissions
- * for this file or directory.
- */
-int
-check_access(struct stat statb)
-{
-       int gidsetlen;
-       gid_t gidset[NGROUPS];
-       int i;
-
-       if (statb.st_uid == getuid()) {
-               if ((statb.st_mode & 0200) && (statb.st_mode & 0400)) {
-                       return 1;
-               } else {
-                       return -1;
-               }
-       }
-
-       gidsetlen = NGROUPS;
-
-       if (getgroups(gidsetlen, gidset) == -1) {
-               perror("getgroups");
-               return -1;
-       }
-
-       for (i = 0; i < NGROUPS; i++) {
-               if (statb.st_gid == gidset[i]) {
-                       if ((statb.st_mode & 020) && (statb.st_mode & 040)) {
-                               return 1;
-                       } else {
-                               return -1;
-                       }
-               }
-       }
-
-       if ((statb.st_mode & 02) && (statb.st_mode & 04)) {
-               return 1;
-       } else {
-               return -1;
-       }
-}
-
-void
-usage(void)
-{
-
-       (void) fprintf(stderr, "usage: %s [-v] [[-t secs] | [-c calls]] [-l load] [-p nprocs] [-m mixfile] [dir]...\n", Myname);
-}
-
-void
-error(char *str)
-{
-
-       Saveerrno = errno;
-       (void) fprintf(stderr, "%s: op failed: %s ", Myname, str);
-       errno = Saveerrno;
-       perror("");
-}
diff --git a/utils/nhfsstone/nhfsstone.man b/utils/nhfsstone/nhfsstone.man
deleted file mode 100644 (file)
index 10a5f71..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-.\" @(#)nhfsstone.man 1.13 89/10/05 Copyright (c) 1989, Legato Systems Inc
-.\" See DISCLAIMER file for restrictions
-.TH NHFSSTONE 8 "4 October 1989"
-.SH NAME
-nhfsstone \- Network File System benchmark program
-.SH SYNOPSIS
-.B nhfsstone
-[
-.B \-v
-] [[
-.B \-t secs
-] | [
-.B -c calls
-]] [
-.B \-l load
-] [
-.B \-p nprocs
-] [
-.B \-m mixfile
-] [
-.B dir
-]...
-.SH DESCRIPTION
-.B nhfsstone
-(pronounced n\-f\-s\-stone, the "h" is silent)
-is used on a
-.SM NFS
-client to generate an artificial load with a particular mix of
-.SM NFS
-operations. It reports the average response time of the server in
-milliseconds per call and the load in calls per second.
-The program adjusts its calling patterns based on the client's kernel
-.SM NFS
-statistics and the elapsed time.
-Load can be generated over a given time or number of
-.SM NFS
-calls.
-.LP
-Because it uses the kernel
-.SM NFS
-statistics to monitor its progress,
-.B nhfsstone
-cannot be used to measure the performance of non\-NFS filesystems.
-.LP
-The
-.B nhfsstone
-program uses file and directory manipulation in an attempt to generate
-particular
-.SM NFS
-operations in response to particular system calls.
-To do this it uses several tricks
-that are based on a knowledge of the implementation of the
-.SM NFS
-client side reference port.
-For example, it uses long file names to circumvent the kernel name lookup
-cache so that a
-.BR stat (2)
-system call generates an
-.SM NFS
-lookup operation.
-.LP
-The mix of
-.SM NFS
-operations can be set with a mix file, which is the output of the
-.BR nfsstat (8C)
-command (see the "\-m" option below).
-The percentages taken from
-the mix file are calculated based on the number of
-.SM NFS
-calls, not on the percentages printed by nfsstat. Operations with
-0% in the mix will never get called by
-.BR nhfsstone .
-In a real server load mix, even though the percentage of call for
-a particular
-.SM NFS
-operation may be zero, the number of calls is often nonzero.
-.B Nhfsstone
-makes the assumption that the number of calls to these 0 percent
-operations will have an insignificant effect on server response.
-.LP
-Normally
-.B nhfsstone
-should be given a list of two or more test directories to use
-(default is to use the current directory).
-The test directories used should be located on different disks and
-partitions on the server to realistically simulate typical server loads.
-Each
-.B nhfsstone
-process looks for a directory
-.B <dir>/testdir<n>
-(where <n> is a number from 0 to
-.B nprocs
-\- 1).
-If a process directory name already exists,
-it is checked for the correct set of test files.
-Otherwise the directory is created and populated.
-.SH OPTIONS
-.TP 12
-.B \-v
-Verbose output.
-.TP
-.B \-t secs
-Sets
-.B calls
-based on the given running time (in seconds) and the load.
-.TP
-.B \-c calls
-Total number of
-.SM NFS
-calls to generate (default is 5000).
-.TP
-.B \-l load
-Load to generate in
-.SM NFS
-calls per second (default is 30).
-.TP
-.B \-p nprocs
-Number of load generating sub\-processes to fork (default is 7).
-This can be used to maximize the amount of load a single machine can generate.
-On a small client machine (slow CPU or small amount of memory)
-fewer processes might be used to avoid swapping.
-.TP
-.B \-m mixfile
-Mix of
-.SM NFS
-operations to generate.
-The format of
-.B mixfile
-is the same as the output of the
-.BR nfsstat (8C)
-program.
-A mix file can be created on a server by typing "nfsstat \-s > mixfile".
-The default mix of operations is: 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%.
-.SH USING NHFSSTONE
-As with all benchmarks,
-.B nhfsstone
-can only provide numbers that are useful if experiments that use it are
-set up carefully.
-Since it is measuring servers, it should be run on a client
-that will not limit the generation of
-.SM NFS
-requests.
-This means it should have a fast CPU,
-a good ethernet interface and the machine
-should not be used for anything else during testing.
-A Sun\-3/50 can generate about 60
-.SM NFS
-calls per second before it runs out of CPU.
-.LP
-.B Nhfsstone
-assumes that all
-.SM NFS
-calls generated on the client are going to a single server, and that
-all of the
-.SM NFS
-load on that server is due to this client.
-To make this assumption hold,
-both the client and server should be as quiescent as possible during tests.
-.LP
-If the network is heavily utilized the delays due to collisions
-may hide any changes in server performance.
-High error rates on either the client or server can also
-cause delays due to retransmissions of lost or damaged packets.
-.BR netstat (8C)
-.B \-i
-can be used to measure the error and collision rates on the client and server.
-.LP
-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 of the
-disk partitions that the server exports and the partitions should be
-as far apart as possible. The
-.BR dkinfo (8)
-command can be used to find the physical geometry of disk on BSD based systems.
-.SM NFS
-operations tend to randomize
-access the whole disk so putting all of the
-.B nhfsstone
-test directories on a single partition or on
-two partitions that are close together will not show realistic results.
-.LP
-On all tests it is a good idea to run the tests repeatedly and compare results.
-The number of calls can be increased
-(with the
-.B \-c
-option) until the variance in milliseconds per call is acceptably small.
-If increasing the number of calls does not help there may be something
-wrong with the experimental setup.
-One common problem is too much memory on the client
-test machine. With too much memory,
-.B nhfsstone
-is not able to defeat the client caches and the
-.SM NFS
-operations do not end up going to the server at all. If you suspect that
-there is a caching problem you can use the
-.B -p
-option to increase the number of processes.
-.LP
-The numbers generated by
-.B nhfsstone
-are most useful for comparison if the test setup on the client machine
-is the same between different server configurations. 
-Changing
-.B nhfsstone
-parameters between runs will produce numbers that can not be
-meaningfully compared.
-For example, 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.
-While
-.B nhfsstone
-tries to compensate for differences in client configurations
-by sampling the actual
-.SM NFS
-statistics and adjusting both the load and mix of operations, some changes
-are not reflected in either the load or the mix. For example, installing
-a faster CPU or mounting different
-.SM NFS
-filesystems may effect the response time without changing either the
-load or the mix.
-.LP
-To do a comparison of different server configurations, first set up the
-client test directories and do
-.B nhfsstone
-runs at different loads to be sure that the variability is
-reasonably low. Second, run
-.B nhfsstone
-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 nhfsstone
-loads again and compare the results.
-.SH SEE ALSO
-.LP
-The
-.B nhfsstone.c
-source file has comments that describe in detail the operation of
-of the program.
-.SH ERROR MESSAGES
-.TP
-.B "illegal calls value"
-The 
-.B calls
-argument following the
-.B \-c
-flag on the command line is not a positive number.
-.TP
-.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 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 mixfile
-file argument following the
-.B \-m
-flag on the command line could not be accessed.
-.TP
-.B "can't find current directory"
-The parent process couldn't find the pathname of the current directory.
-This usually indicates a permission problem.
-.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 filesystem.
-.TP
-.PD 0
-.B "bad mix format: unexpected EOF after 'nfs:'"
-.TP
-.B "bad mix format: can't find 'calls' value"
-.TP
-.B "bad mix format: unexpected EOF after 'calls'"
-.TP
-.B "bad mix format: can't find %d op values"
-.TP
-.B "bad mix format: unexpected EOF"
-.PD
-A problem occurred while parsing the
-.B mix
-file. The expected format of the file is the same as the output of
-the
-.BR nfsstat (8C)
-command when run with the "\-s" option.
-.TP
-.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
-.LP
-Running
-.B nhfsstone
-on a non\-NFS filesystem can cause the program to run forever because it
-uses the kernel NFS statistics to determine when enough calls have been made.
-.LP
-.B Nhfsstone
-uses many file descriptors. The kernel on the client may
-have to be reconfigured to increase the number of available file table entries.
-.LP
-Shell scripts that used
-.B nhfsstone
-will have to catch and ignore SIGUSR1 (see
-.BR signal (3)).
-This signal is
-used to synchronize the test processes. If the signal is not caught
-the shell that is running the script will be killed.
-.SH FILES
-.PD 0
-.TP 20
-.B /vmunix
-system namelist
-.TP
-.B /dev/kmem
-kernel virtual memory
-.TP
-.B ./testdir*
-per process test directory
-.TP
-.B /tmp/nhfsstone%d
-process synchronization log file
-.PD