diff options
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | utils/Makefile.am | 1 | ||||
-rw-r--r-- | utils/nhfsstone/DISCLAIMER | 33 | ||||
-rw-r--r-- | utils/nhfsstone/Makefile.am | 14 | ||||
-rw-r--r-- | utils/nhfsstone/README | 111 | ||||
-rw-r--r-- | utils/nhfsstone/README.linux | 11 | ||||
-rwxr-xr-x | utils/nhfsstone/nhfsgraph | 23 | ||||
-rw-r--r-- | utils/nhfsstone/nhfsgraph.man | 29 | ||||
-rwxr-xr-x | utils/nhfsstone/nhfsnums | 22 | ||||
-rw-r--r-- | utils/nhfsstone/nhfsnums.man | 24 | ||||
-rwxr-xr-x | utils/nhfsstone/nhfsrun | 59 | ||||
-rw-r--r-- | utils/nhfsstone/nhfsrun.man | 16 | ||||
-rw-r--r-- | utils/nhfsstone/nhfsstone.c | 1820 | ||||
-rw-r--r-- | utils/nhfsstone/nhfsstone.man | 381 |
14 files changed, 0 insertions, 2545 deletions
diff --git a/configure.in b/configure.in index 0ccc6b2..7cec002 100644 --- a/configure.in +++ b/configure.in @@ -333,7 +333,6 @@ AC_CONFIG_FILES([ utils/mountd/Makefile utils/nfsd/Makefile utils/nfsstat/Makefile - utils/nhfsstone/Makefile utils/rquotad/Makefile utils/showmount/Makefile utils/statd/Makefile]) diff --git a/utils/Makefile.am b/utils/Makefile.am index 9cdb4ea..723657c 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -24,7 +24,6 @@ SUBDIRS = \ mountd \ nfsd \ nfsstat \ - nhfsstone \ showmount \ statd \ $(OPTDIRS) diff --git a/utils/nhfsstone/DISCLAIMER b/utils/nhfsstone/DISCLAIMER deleted file mode 100644 index afde6a3..0000000 --- a/utils/nhfsstone/DISCLAIMER +++ /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 index 43e9fda..0000000 --- a/utils/nhfsstone/Makefile.am +++ /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 index f13dde5..0000000 --- a/utils/nhfsstone/README +++ /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 index e9b7899..0000000 --- a/utils/nhfsstone/README.linux +++ /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 index 56e2c77..0000000 --- a/utils/nhfsstone/nhfsgraph +++ /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 index 4ce080f..0000000 --- a/utils/nhfsstone/nhfsgraph.man +++ /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 index aae625d..0000000 --- a/utils/nhfsstone/nhfsnums +++ /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 index 28e9332..0000000 --- a/utils/nhfsstone/nhfsnums.man +++ /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 index ce77ace..0000000 --- a/utils/nhfsstone/nhfsrun +++ /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 index 63fdf7f..0000000 --- a/utils/nhfsstone/nhfsrun.man +++ /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 index 463bcb5..0000000 --- a/utils/nhfsstone/nhfsstone.c +++ /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 index 10a5f71..0000000 --- a/utils/nhfsstone/nhfsstone.man +++ /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 |