From fd1b52710313d01f537a1afcba1a7a4d67ab8ac7 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Tue, 19 Dec 2006 09:44:17 +1100 Subject: [PATCH] nhfsstone remove due to license uncertainty. 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 --- configure.in | 1 - utils/Makefile.am | 1 - utils/nhfsstone/DISCLAIMER | 33 - utils/nhfsstone/Makefile.am | 14 - utils/nhfsstone/README | 111 -- utils/nhfsstone/README.linux | 11 - utils/nhfsstone/nhfsgraph | 23 - utils/nhfsstone/nhfsgraph.man | 29 - utils/nhfsstone/nhfsnums | 22 - utils/nhfsstone/nhfsnums.man | 24 - utils/nhfsstone/nhfsrun | 59 -- utils/nhfsstone/nhfsrun.man | 16 - utils/nhfsstone/nhfsstone.c | 1820 --------------------------------- utils/nhfsstone/nhfsstone.man | 381 ------- 14 files changed, 2545 deletions(-) delete mode 100644 utils/nhfsstone/DISCLAIMER delete mode 100644 utils/nhfsstone/Makefile.am delete mode 100644 utils/nhfsstone/README delete mode 100644 utils/nhfsstone/README.linux delete mode 100755 utils/nhfsstone/nhfsgraph delete mode 100644 utils/nhfsstone/nhfsgraph.man delete mode 100755 utils/nhfsstone/nhfsnums delete mode 100644 utils/nhfsstone/nhfsnums.man delete mode 100755 utils/nhfsstone/nhfsrun delete mode 100644 utils/nhfsstone/nhfsrun.man delete mode 100644 utils/nhfsstone/nhfsstone.c delete mode 100644 utils/nhfsstone/nhfsstone.man 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 ... -# -# 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 -.SH DESCRIPTION -Produce a PostScript graph of nhfsstone numbers. -The input -.B -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 ... -# -# 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 -.SH DESCRIPTION -Converts raw numbers from nhfsstone output into -.BR plot (5) -format. The -.B -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. -# - -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 -.SH DESCRIPTION -Runs -.B nhfsstone -with a range of different loads and put results in a file called -.BR run. . -.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 - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef BSD -#include -#define dirent direct -#else -#include -#endif -#include - -#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 /testdir -(where 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 -- 2.39.2