Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / TBBT / trace_play / sfs_c_man.c
1 #ifndef lint
2 static char sccsid_hp[] = "@(#)sfs_c_man.c      2.1     97/10/23";
3 #endif
4
5 /*
6  *   Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation
7  *      All rights reserved.
8  *              Standard Performance Evaluation Corporation (SPEC)
9  *              6585 Merchant Place, Suite 100
10  *              Warrenton, VA 20187
11  *
12  *      This product contains benchmarks acquired from several sources who
13  *      understand and agree with SPEC's goal of creating fair and objective
14  *      benchmarks to measure computer performance.
15  *
16  *      This copyright notice is placed here only to protect SPEC in the
17  *      event the source is misused in any manner that is contrary to the
18  *      spirit, the goals and the intent of SPEC.
19  *
20  *      The source code is provided to the user or company under the license
21  *      agreement for the SPEC Benchmark Suite for this product.
22  */
23
24 /*
25  * Generates an artifical NFS client load based on a given mix of operations,
26  * and block transfer distribution.
27  *
28  * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time]
29  *               [-m mix_file] [-B block_size] [-b blocksz_file]
30  *               [-f file_set_delta] [-a access_pnct]
31  *               [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]
32  *               [-d debug_level] [-i] [-P] [-T op_num]
33  *               [-V validation_level] [-z] [-Q]
34  *               [-R biod_reads] [-W biod_writes]
35  *               [-M prime_client_hostname] [-N client_cnt]
36  *
37  * NOTE: REFER TO SFS MAN PAGE (sfs.1) FOR A DESCRIPTION OF ALL
38  *       SFS OPTIONS
39  *
40  *
41  * Single Client Options
42  *
43  * option          description                                      default
44  * --------------- ------------------------------------------------ -----------
45  * -a access_pcnt  % of file set to access                          20% access
46  * -A append_pcnt  % of writes that append rather than overwrite    70% append
47  * -b blocksz_file file specifying distribution of block xfer sizes (see below)
48  * -B block_size   # of KB in block, up to 8 KB                     8 KB
49  * -Q              Do TCP connection for NFS rather than UDP        off
50  * -d debug_level  debug level (higher number gives more output)    off
51  * -D dir_cnt      # directories used for directory operations      20 dirs
52  * -f fileset_delta % change in file set size allowed               10%
53  * -F file_cnt     # files used for read and write operations       100 files
54  * -i              interactive; wait for input before starting test off
55  * -l load         # NFS calls/second to generate from each client  60 calls/sec
56  * -m mix_file     file specifying NFS call distribution            (see below)
57  * -p procs        # processes used to generate load on each client 7 procs
58  * -P              populate test directories, but don't run a test  off
59  * -R biod_reads   max # of outstanding read requests at one time   2 reqs
60  * -S symlink_cnt  # symbolic links used for symlink operations     20 symlinks
61  * -t time         # seconds to generate load for the timed test    600 secs
62  * -T op_num       test the NFS operation specified one time        off
63  * -V              validate correctness of server's NFS             off
64  * -W biod_writes   max # of outstanding writes req at one time     2 reqs
65  * -w warmup       # secs to generate load before starting test     60 secs
66  * -z              If specified, collect and dump raw data.         off
67  *
68  *
69  * Multi Client Options
70  *
71  * option                       description                         default
72  * ------------------------     ----------------------------------- -----------
73  * -M prime_client_hostname     hostname of prime client            no default
74  * -N client_cnt                # client machines in test           no default
75  *
76  *
77  *
78  * Block Transfer Size Distribution
79  *
80  * The block transfer size distribution is specified by a table of values.
81  * The first column gives the percent of operations that will be a
82  * specific block transfer size.  The second column gives the number of
83  * blocks units that will be transferred.  Normally the block unit size
84  * is 8KB.  The third column is a boolean specifying whether a trailing
85  * fragment block should be transferred.  The fragment size for each transfer
86  * is a random multiple of 1 KB, up to the block size - 1 KB.  Two tables
87  * are needed, one for read operation and one for write operations.  The
88  * following table gives the default distributions.
89  *
90  *      Read  - Default Block Transfer Size Distribution Table
91  * percent   block count   fragment     resulting transfer (8KB block size)
92  * -------   -----------   --------     -----------------------------------
93  *    0        0             0           0%    1 -   7 KB
94  *   85        1             0          85%    9 -  15 KB
95  *    8        2             1           8%   17 -  23 KB
96  *    4        4             1           4%   33 -  39 KB
97  *    2        8             1           2%   65 -  71 KB
98  *    1       16             1           1%  129 - 135 KB
99  *
100  *      Write  - Default Block Transfer Size Distribution Table
101  * percent   block count   fragment     resulting transfer (8KB block size)
102  * -------   -----------   --------     -----------------------------------
103  *   49        0             1          49%    1 -   7 KB
104  *   36        1             1          36%    9 -  15 KB
105  *    8        2             1           8%   17 -  23 KB
106  *    4        4             1           4%   33 -  39 KB
107  *    2        8             1           2%   65 -  71 KB
108  *    1       16             1           1%  129 - 135 KB
109  *
110  * The user may specify a a different distribution by using the '-b' option.
111  * The format for the block size distribution file consists of the first
112  * three columns given above: percent, block count, and fragment.  Read
113  * and write distribution tables are identified by the keywords "Read" and
114  * "Write".  An example input file, using the default values, is given below:
115  *
116  *              Read
117  *               0  0  0
118  *              85  1  0
119  *               8  2  1
120  *               4  4  1
121  *               2  8  1
122  *               1 16  1
123  *              Write
124  *              49  0  1
125  *              36  1  1
126  *               8  2  1
127  *               4  4  1
128  *               2  8  1
129  *               1 16  1
130  *
131  * A second parameter controlled by the block transfer size distribution
132  * table is ethernet packet size.  The distribution tables define the
133  * relative proportion of full blocks packets to fragment packets.  For
134  * instance, the default tables have been constructed to produce a specific
135  * distribution of ethernet packet sizes for i/o operations by controlling
136  * the amount of data in each packet.  The write packets produced consist
137  * of 50% 8-KB packets, and 50% 1-7 KB packets.  The read packets consist
138  * of 85% 8-KB packets, and 15% 1-7 KB packets.  These figures are
139  * determined by multiplying the percentage for the type of transfer by
140  * the number of blocks and fragments generated, and adding the totals.
141  * These conmputations are performed below for the default block size
142  * distribution tables:
143  *
144  *              Read            blocks          fragments
145  *               0  0  0          0               0
146  *              85  1  0         85               0
147  *               8  2  1         16               8
148  *               4  4  1         16               4
149  *               2  8  1         16               2
150  *               1 16  1         16               1
151  *                              ---             ---
152  *                              149 (90%)        15 (10%)
153  *
154  *              Write
155  *              49  0  1          0              49
156  *              36  1  1         36              36
157  *               8  2  1         16               8
158  *               4  4  1         16               4
159  *               2  8  1         16               2
160  *               1 16  1         16               1
161  *                              ---             ---
162  *                              100 (50%)       100 (50%)
163  *
164  *
165  *
166  *
167  *
168  * NFS Operation Mix
169  *
170  * The operation mix is described assigning a percentage to each type
171  * of NFS operation.  The default mix of operations is:
172  *
173  * operation    percent
174  * ---------    -------
175  * null          0
176  * getattr      13
177  * setattr       1
178  * root          0
179  * lookup       34
180  * readlink      8
181  * read         22
182  * wrcache       0
183  * write        15
184  * create        2
185  * remove        1
186  * rename        0
187  * link          0
188  * symlink       0
189  * mkdir         0
190  * rmdir         0
191  * readdir       3
192  * fsstat        1
193  *
194  * The user may specify a a different operation mix by using the '-m' option.
195  * The format for the mix file consists of the output from an nfsstat(1)
196  * command, which lists an operation count and percentage for each NFS
197  * operation.
198  *
199  *
200  * File Set Size
201  *
202  * !!! needs to be re-written - mew 8/24
203  * This still needs to be rewritten
204  *   - The default number of i/o files is based on load level.
205  *   - For non I/O files, the number of initialized versus empty
206  *     slots is hardcoded
207  *  - dr 2/8/94
208  *
209  * The file set used by SFS is determined by 2 factors. First,
210  * SFS creates a base set of files.  By default this consists of
211  * 100 files for i/o operations, 100 files for non-i/o operations, 20
212  * directories, and 20 symbolic links.  These default values can be
213  * changed from the command line by using the "-F", "-D", and "-S" options.
214  * This file set is divided evenly among the set of processes used to
215  * generate load.
216  *
217  * Then, enough resources are allocated to allow for the eventual creation
218  * of new files, directories and symlinks by the creat, link, mkdir,
219  * and symlink operations.  The number of extra slots allocated depends
220  * on the mix percentages assigned to each of the create and deletion
221  * operations, multiplied by an estimate of the total number of operations
222  * to be performed.  For instance, the default number of extra files names
223  * allocated for non-i/o operations is computed as follows:
224  *      300 secs * 60 ops/sec = 18000 total operations
225  *      2% create * 18000 ops = 360 create ops
226  *      1% remove * 18000 ops = 180 remove ops
227  *      260 creates ops - 180 remove ops = 180 extra files to be created.
228  * These 90 files are distributed evenly among the processes used to
229  * generate load.  With the default settings, no extra directories are
230  * created or removed, so no extra allocation is done for directories
231  * beyond the base file set.  The same is true for symbolic links.
232  *
233  * Thus, the total default file set size is:
234  *      100 files for i/o operations
235  *      280 files for non-i/o operations
236  *       20 directories
237  *       20 symlinks
238  *
239  * By allocating all required space before the test begins, any space
240  * allocation problems encountered by SFS are discovered before the
241  * test is started.
242  *
243  *
244  * Program Control
245  *
246  * Strategy: loop for some number of NFS calls doing a random sleep
247  * followed by a call to one of the op generator routines. The routines
248  * are called based on a weighting factor determined by the set of
249  * default percentages or a mix supplied by the user.
250  *
251  * The generator routines are able to keep an accurate count of the
252  * NFS operations they are generating by using the NFS protocol
253  * directly and not going through the kernel.  This eliminates the
254  * effects of kernel name caches and retry mechanisms that
255  * complicate control of what actually hits the wire.  The calling
256  * routine benefits by avoiding having to get the NFS statistics
257  * from the kernel because they KNOW what calls they've made.
258  *
259  * By using the NFS protocol directly :
260  *      "lookup" operations sidestep the client kernel name cache,
261  *      "getattr" operations avoid the client kernel attribute cache,
262  *      "read" operations avoid the client kernel buffer cache,
263  *      and so on.
264  *
265  * A consequence of not going thru the client kernel is that the sfs
266  * program must maintain a table of file handles rather than open
267  * file descriptors.
268  *
269  * The parent process starts children to do the real work of generating load.
270  * The parent coordinates them so that they all start at the same time, and
271  * collects statistics from them when they are done. To coordinate the
272  * start up, the parent waits for each child to write one byte into
273  * a common log file (opened in append mode to avoid overwriting).
274  * After they write a byte the children pause, and the parent send SIGUSR1
275  * when it has heard from all of the kids. The children write their statistics
276  * into the same common log file and the parent reads and accumulates the
277  * statistics and prints them out.
278  *
279  *
280  *.Exported_Routines
281  *      int main(int, char*)
282  *
283  *.Local_Routines
284  *      void init_logfile(void)
285  *      void usage(void)
286  *      int setmix(char *)
287  *      int setiodist(FILE *)
288  *      int parseiodist(FILE *, int)
289  *      void init_iodist(sfs_io_dist_type *)
290  *      void init_fss(void)
291  *      void init_filedist(void)
292  *      int lad_substr(char *, char *)
293  *
294  *.Revision_History
295  *      21-Aug-92       0.1.11   Wittle File set access code.
296  *      14-Jul-92       0.1.9    Teelucksingh
297  *                                      Implemented Mark Wittle's proposal to
298  *                                      base File Set Size on peak load value,
299  *                                      added "-L peak_load" option.
300  *      10-Jan-92       0.0.0.19 Teelucksingh
301  *                                      Reworked setpgrp() usage to
302  *                                      better handle BSD vs SYSV variations.
303  *      04-Jan-92       0.0.0.18 Pawlowski
304  *                                      Added raw data dump code.
305  *      04-Dec-91       0.0.0.15 Keith
306  *                                      Include string.h if SVR4.
307  *      28-Nov-91       0.0.0.13 Teelucksingh
308  *                                      Modified code to use unique sfs /tmp
309  *                                      logfiles; sfs can now be used on
310  *                                      clients that have a shared /tmp area.
311  *                                      Added ANSI C features. Fixed 'multiple
312  *                                      signals from the Prime-Client' problem.
313  *                                      Added code to allow clients to
314  *                                      check for and create client specific
315  *                                      directories under each mount point -
316  *                                      clients share partitions. (code from
317  *                                      M.Molloy).
318  *      22-Nov-91                Wittle Updated program description comment.
319  *                                      Added new op generation code.
320  *                                      Added block_dist_table and block_size
321  *                                      options, removed 8KB packet assumptions.
322  *      04-Oct-91       0.0.0.12 Teelucksingh
323  *                                      Changed SFS sources and executables
324  *                                      to use the "prelad" prefix.
325  *      23-Sep-91       0.0.0.11 Teelucksingh
326  *                                      Changed format of sfs output.
327  *      01-Aug-91       0.0.9 Wiryaman  Use the SPEC random number generator.
328  *                                      Since this RNG cannot take seed = 0,
329  *                                      use child_num+1 instead.
330  *      17-Jul-91       0.0.8 Teelucksingh
331  *                                      Enhance multi-client code and
332  *                                      documentation.
333  *                            Keith     Map "nhfsstone" to "laddis" in
334  *                                      README, nhfsstone_mgr.c. Create
335  *                                      initial DESCR.SFS for SPEC
336  *                                      submission.
337  *      15-Jul-91       0.0.8 Wiryaman  Add getmnt() for ULTRIX
338  *      25-Jun-91       0.0.7 Wiryaman  Added validation option to test all
339  *                                      of the NFS operations.
340  *      17-Jun-91       0.0.7 Teelucksingh
341  *                                      Added multi-client synchronization
342  *                                      support. By designating a client as
343  *                                      "Prime client" you can synchronize
344  *                                      multi-client SFS execution.
345  *      12-May-91       0.0.6 Wittle    Fix standard deviation.
346  *      02-May-91       0.0.5 Wittle    Fix SunOS signal bug; use default
347  *                                      warmuptime; add local time routine;
348  *                                      check for calls that underflow elapsed
349  *                                      time measurement; add std deviation
350  *                                      statistics; rework verbose output.
351  *                                      fix init invalid protocol rmdir calls;
352  *      15-Apr-91       0.0.4 Wittle    Test can be repeated without removing
353  *                                      test directories - initialization
354  *                                      restores base file set count and sizes.
355  *                                      Fix lack of call rate & mix accuracy -
356  *                                      set op_check before artificially
357  *                                      increasing call_targets.
358  *                                      Don't pre-create files/dirs that are
359  *                                      meant to be created during the test.
360  *      10-Mar-91       0.0.3 Wittle    Longer RPC timeout while populating
361  *                                      testdir; strstr() bug fix;
362  *                                      '-i' and '-e' options
363  *      06-Mar-91       0.0.2 Wittle    Loop forever pre-filling files.
364  *      22-Feb-91       0.0.1 Wittle    Use signal(2) instead of sigset(2).
365  *      18-Feb-91       0.0.0 Wittle    Change algorythm for determining i/o
366  *                                      sizes, preserve i/o file working set
367  *                                      by using separate files; bugs fixes.
368  *
369  *      nhfsstone renamed to laddis
370  *
371  *      31-Oct-90       2.0.4 Wittle    Many bug fixes.
372  *      24-Aug-90       2.0.3 Wittle    Output compatible w/graphing tools.
373  *      24-July-90      2.0.2 Wittle    Handle mounting below symlinks.
374  *      24-June-90      2.0.1 Wittle    Prefill files with data.
375  *      17-May-90       2.0.0 Bean      Rewrote the guts to use NFS
376  *                                      protocol directly.
377  *                                      Cleaned up self-pacing mechanism.
378  *      08-Nov-89       Guillermo Roa   Ported original version to DG/UX.
379  *      07-Jul-89       Legato Systems  Created.
380  */
381
382 /*
383  * -------------------------  Include Files  -------------------------
384  */
385
386 /*
387  * ANSI C headers
388  */
389 #include <stdio.h>
390 #include <stdlib.h>
391 #include <string.h>
392 #include <errno.h>
393 #include <ctype.h>
394 #include <signal.h>
395
396 #include <sys/types.h>
397 #include <sys/stat.h> 
398  
399 #include <sys/signal.h>
400
401 #include <sys/file.h>
402 #include <fcntl.h>
403
404 #include <unistd.h>
405
406 extern getmyhostname(char *, int);
407
408 #include "sfs_c_def.h"
409 #include "sfs_m_def.h"
410
411 /*
412  * -------------------------  External Definitions  -------------------------
413  */
414
415 /* external routines from RPC and system libraries */
416 #if defined(SETPGRP_BSD)
417 extern int setpgrp(int, int);
418 #endif /* SETPGRP_BSD */
419 #if defined(SETPGRP_SYSV)
420 extern pid_t setpgrp(void);
421 #endif /* SETPGRP_SYSV */
422
423 /* forward definitions for local routines */
424 static void init_logfile(void);
425 static void usage(void);
426 static int setmix(char *);
427 static int setiodist(FILE *);
428 static int parseiodist(FILE *, int);
429 static void init_iodist(sfs_io_dist_type *);
430 static void init_fss(void);
431 static void init_filedist(void);
432 static int lad_substr(char *, char *);
433 static double time_so_far1(void);
434 static double get_resolution(void);
435 static void check_clock(void);
436
437 int     Tot_client_num_io_files = 0;    /* # of files used for i/o per client */
438 int     Tot_client_num_non_io_files =   /* # of files used for i/o per client */
439                                 DEFAULT_NFILES;
440 int     Files_per_dir =                 /* # of pre-created dirs */
441                                 DEFAULT_FILES_PER_DIR;
442 int     Tot_client_num_symlinks =       /* # of pre-created symlinks/client */
443                                 DEFAULT_NSYMLINKS;
444 int     Child_num;
445 char *  Prime_client = NULL;            /* Prime client hostname */
446 int     Client_num = 1;                 /* My client number */
447 int     Tcp = 0;                        /* Flag set on command line */
448 char    *sfs_Myname;                 /* name program invoked under */
449 int     Log_fd;                         /* log fd */
450 char    Logname[NFS_MAXNAMLEN];         /* child processes sync logfile */
451 uid_t   Real_uid;                       /* real uid */
452 uid_t   Cur_uid;                        /* my uid */
453 gid_t   Cur_gid;                        /* my gid list */
454
455 static char Client_logname[SFS_MAXNAMLEN];
456
457 /*
458  * -----------------  SFS Main and Initialization Code  -----------------
459  */
460
461 /*
462  * Read the command line arguments, fork off child processes to
463  * generate NFS load, and perform the local (ie, on this client)
464  * book-keeping for the test and the results.
465  */
466 int
467 main(
468     int         argc,
469     char        *argv[])
470 {
471
472     char        *mix_file;              /* name of mix file */
473     char        *iodist_file;           /* name of block i/o dist table file */
474     int         children;               /* number of children */
475     int         child_num;              /* child index */
476     int         total_load;             /* total client load factor */
477     float       child_load;             /* per child load factor */
478     int         pid;                    /* process id */
479     FILE        *pid_fp;
480     FILE        *iodist_fp;             /* block io dist table file */
481     int         i;
482     int         c;
483     int         Saveerrno;
484     int         ret;
485     int         nsigs = 32;             /* reasonable default */
486     extern char *optarg;
487     extern int optind;
488
489     /*
490      * Place pid in pid log file
491      */
492     if ((pid_fp = fopen(SFS_PNT_PID, "a+")) == NULL) {
493         perror(SFS_PNT_PID);
494         exit(1);
495     }
496
497     (void) fprintf(pid_fp, "%d\n", getpid());
498
499     /* Get program name for stderr printing */
500     sfs_Myname = argv[0];
501
502     check_clock();
503     getmyhostname(lad_hostname, HOSTNAME_LEN);
504
505     init_ops();
506
507 /*
508  * Get the uid and gid information.
509  */
510     Real_uid = getuid();
511     Cur_gid = getgid();
512
513 /*
514  * Form a new process group so our syncrhonization signals don't
515  * cause our parent shell to exit.  Clear the umask.
516  * Default is to use the standard setsid
517  */
518 #ifdef SETPGRP3
519     ret = setpgrp3(); /* Work around HP-UX bug */
520 #else
521 #ifdef SETPGRP_SYSV
522     ret = setpgrp();
523 #else
524 #ifdef SETPGRP_BSD
525     ret = setpgrp(0, getpid());
526 #else
527     ret = setsid();
528 #endif /* SETPGRP_BSD */
529 #endif /* SETPGRP_SYSV */
530 #endif /* SETPGRP3 */
531
532     if (ret == -1) {
533         (void) fprintf(stderr, "%s: failed on setsid/setpgrp\n",
534                 sfs_Myname);
535         exit(95);
536     }
537
538     (void) umask(0);
539
540 /* Set up default parameters */
541     Validate = 0;
542
543     children = DEFAULT_NPROCS;
544     total_load = DEFAULT_LOAD;
545     mix_file = 0;
546     iodist_file = 0;
547     Nfs_timers = Nfs_udp_timers;
548
549     /* Parse command line arguments */
550     while ((c = getopt(argc, argv, "a:A:b:B:cd:D:f:F:il:m:M:N:p:PQR:S:T:t:V:W:w:z")) != EOF)
551         switch (c) {
552
553         case 'a': /* Percent of files to access */
554             if (!isdigit(optarg[0])) {
555                 (void) fprintf(stderr, "%s: illegal access value %s\n",
556                                         sfs_Myname, optarg);
557                 exit(96);
558             }
559             Access_percent = atoi(optarg);
560             if (Access_percent < 0 || Access_percent > 100) {
561                 (void) fprintf(stderr,
562                        "%s: %% access must be between 0 and 100\n",
563                         sfs_Myname);
564                 exit(97);
565             }
566             break;
567
568         case 'A': /* Percent of writes that append */
569             if (!isdigit(optarg[0])) {
570                 (void) fprintf(stderr, "%s: illegal append value %s\n",
571                                 sfs_Myname, optarg);
572                 exit(98);
573             }
574             Append_percent = atoi(optarg);
575             if (Append_percent < 0 || Append_percent > 100) {
576                 (void) fprintf(stderr,
577                        "%s: %% append must be between 0 and 100\n",
578                         sfs_Myname);
579                 exit(99);
580             }
581             break;
582
583         case 'b': /* Set block size distribution table from file */
584             if ((iodist_fp = fopen(optarg, "r")) == NULL) {
585                 Saveerrno = errno;
586                 (void) fprintf(stderr, "%s: bad block size file",
587                                 sfs_Myname);
588                 errno = Saveerrno;
589                 perror(optarg);
590                 exit(100);
591             }
592             if (setiodist(iodist_fp) < 0) {
593                 exit(101);
594             }
595             iodist_file = optarg;
596             (void) fclose(iodist_fp);
597             break;
598
599         case 'B': /* Set the per packet maximum block size */
600             if (!isdigit(optarg[0])) {
601                 (void) fprintf(stderr,
602                                 "%s: illegal block size value %s\n",
603                                 sfs_Myname, optarg);
604                 exit(102);
605             }
606             Kb_per_block = atoi(optarg);
607             if ((Kb_per_block < 1) ||
608                 (Kb_per_block > (DEFAULT_MAX_BUFSIZE/1024))) {
609                 (void) fprintf(stderr,
610                                 "%s: illegal block size value %s\n",
611                                 sfs_Myname, optarg);
612                 exit(103);
613             }
614             Bytes_per_block = Kb_per_block * 1024;
615             break;
616
617
618         case 'c': /* Set number of calls */
619             (void) fprintf(stderr, "%s: '-c option no longer supported\n",
620                                     sfs_Myname);
621             exit(104);
622             break;
623
624         case 'd': /* Set debugging level */
625             Debug_level = set_debug_level(optarg);
626             break;
627
628         case 'D': /* Set number of directories */
629             if (!isdigit(optarg[0])) {
630                 (void) fprintf(stderr, "%s: illegal dirs value %s\n",
631                                 sfs_Myname, optarg);
632                 exit(105);
633             }
634             Files_per_dir = atoi(optarg);
635             break;
636
637         case 'f': /* Percent change in file set size */
638             if (!isdigit(optarg[0])) {
639                 (void) fprintf(stderr, "%s: illegal file set delta value %s\n",
640                                 sfs_Myname, optarg);
641                 exit(106);
642             }
643             Fss_delta_percent = atoi(optarg);
644             if (Fss_delta_percent < 0 || Fss_delta_percent > 100) {
645                 (void) fprintf(stderr,
646                    "%s: %% file set delta must be between 0 and 100\n",
647                     sfs_Myname);
648                 exit(107);
649             }
650             break;
651
652         case 'F': /* Set number of io files */
653             if (!isdigit(optarg[0])) {
654                 (void) fprintf(stderr, "%s: illegal files value %s\n",
655                                 sfs_Myname, optarg);
656                 exit(108);
657             }
658             Tot_client_num_io_files = atoi(optarg);
659             break;
660
661         case 'i': /* Set interactive mode */
662             if (Prime_client != NULL) {
663                 (void) fprintf(stderr,
664                             "%s: -i and -M options are incompatible\n",
665                             sfs_Myname);
666                 exit(109);
667             }
668             Interactive++;
669             break;
670
671         case 'l': /* Set load */
672             if (!isdigit(optarg[0])) {
673                 (void) fprintf(stderr, "%s: illegal load value %s\n",
674                                 sfs_Myname, optarg);
675                 exit(110);
676             }
677             total_load = atoi(optarg);
678             if (total_load < 0) {
679                 (void) fprintf(stderr, "%s: load must be > 0\n",
680                                 sfs_Myname);
681                 exit(111);
682             }
683             break;
684
685         case 'm': /* Set mix from a file */
686             mix_file = optarg;
687             if (setmix(mix_file) < 0) {
688                 exit(112);
689             }
690             break;
691
692         case 'M': /* Set prime_client host name for multi-client sync */
693             if (Interactive) {
694                 (void) fprintf(stderr,
695                             "%s: -M and -i options are incompatible\n",
696                             sfs_Myname);
697                 exit(113);
698             }
699             Prime_client = optarg;
700             break;
701
702         case 'N': /* Set client number in multi-client run */
703             Client_num = atoi(optarg);
704             if (Client_num <= 0) {
705                 (void) fprintf(stderr,
706                                 "%s: client number must be > 0\n",
707                                 sfs_Myname);
708                 exit(114);
709             }
710             break;
711
712         case 'p': /* Set number of child processes */
713             if (!isdigit(optarg[0])) {
714                 (void) fprintf(stderr, "%s: illegal procs value %s\n",
715                                 sfs_Myname, optarg);
716                 exit(115);
717             }
718             children = atoi(optarg);
719             if (children < 0) {
720                 (void) fprintf(stderr, "%s: number of children must be > 0\n",
721                                 sfs_Myname);
722                 exit(116);
723             }
724             break;
725
726         case 'P': /* Populate only */
727             Populate_only++;
728             break;
729
730         case 'Q': /* Set NFS/TCP behaviour */
731             Tcp = 1;
732             Nfs_timers = Nfs_tcp_timers;
733             break;
734
735         case 'R': /* set maximum async read concurrency level */
736             if (!isdigit(optarg[0])) {
737                 (void) fprintf(stderr, "%s: illegal read count value %s\n",
738                                 sfs_Myname, optarg);
739                 exit(117);
740             }
741             Biod_max_outstanding_reads = atoi(optarg);
742             if (Biod_max_outstanding_reads < 0 ||
743                         Biod_max_outstanding_reads > MAX_BIODS) {
744                 (void) fprintf(stderr,
745                                 "%s: read count must be >= 0 and <= %d\n",
746                                 sfs_Myname, MAX_BIODS);
747                 exit(118);
748             }
749             break;
750
751         case 'S': /* Set number of symlinks */
752             if (!isdigit(optarg[0])) {
753                 (void) fprintf(stderr,
754                                 "%s: illegal symlinks value %s\n",
755                                 sfs_Myname, optarg);
756                 exit(119);
757             }
758             Tot_client_num_symlinks = atoi(optarg);
759             break;
760
761         case 'T': /* Set test mode, number following is opnum */
762             if (!isdigit(optarg[0])) {
763                 (void) fprintf(stderr, "%s: illegal test value %s\n",
764                                 sfs_Myname, optarg);
765                 exit(120);
766             }
767             Testop = atoi(optarg);
768             if (Testop >= NOPS) {
769                 (void) fprintf(stderr, "%s: illegal test value %d\n",
770                                 sfs_Myname, Testop);
771                 exit(121);
772             }
773             break;
774
775         case 't': /* Set run time */
776             if (Ops[TOTAL].target_calls > 0) {
777                 (void) fprintf(stderr,
778                             "%s: -t and -c options are incompatible\n",
779                             sfs_Myname);
780                 exit(122);
781             }
782             if (!isdigit(optarg[0])) {
783                 (void) fprintf(stderr, "%s: illegal time value %s\n",
784                                 sfs_Myname, optarg);
785                 exit(123);
786             }
787             Runtime = atoi(optarg);
788             if (Runtime < 0) {
789                 (void) fprintf(stderr, "%s: run time must be >= 0\n",
790                                 sfs_Myname);
791                 exit(124);
792             }
793             break;
794
795         case 'V': /* Set Validate Level */
796             if (!isdigit(optarg[0])) {
797                 (void) fprintf(stderr, "%s: illegal validate value %s\n",
798                                         sfs_Myname, optarg);
799                 exit(125);
800             }
801             Validate = atoi(optarg);
802             if (Validate < 1 || Validate > 3) {
803                 (void) fprintf(stderr, "%s: validate must be between 1 and 3\n",
804                                     sfs_Myname);
805                 exit(126);
806             }
807             break;
808
809         case 'W': /* set maximum async write concurrency level */
810             if (!isdigit(optarg[0])) {
811                 (void) fprintf(stderr, "%s: illegal write count value %s\n",
812                                         sfs_Myname, optarg);
813                 exit(127);
814             }
815             Biod_max_outstanding_writes = atoi(optarg);
816             if (Biod_max_outstanding_writes < 0 ||
817                         Biod_max_outstanding_writes > MAX_BIODS) {
818                 (void) fprintf(stderr,
819                                 "%s: write count must be >= 0 and <= %d\n",
820                                 sfs_Myname, MAX_BIODS);
821                 exit(128);
822             }
823             break;
824
825         case 'w': /* Set warmup time */
826             if (!isdigit(optarg[0])) {
827                 (void) fprintf(stderr, "%s: illegal warmup value %s\n",
828                                 sfs_Myname, optarg);
829                 exit(129);
830             }
831             Warmuptime = atoi(optarg);
832             if (Warmuptime < 0) {
833                 (void) fprintf(stderr, "%s: warmup time must be >= 0\n",
834                                 sfs_Myname);
835                 exit(130);
836             }
837             break;
838
839         case 'z': /* Do raw data dumps */
840             Dump_data++;
841             break;
842
843         case '?':
844         default:
845             usage();
846             exit(131);
847
848         } /* end switch on arg */
849
850
851    /* compute ops/request for i/o operations */
852    init_iodist(Io_dist_ptr);
853
854    /* compute bytes/file and number of files */
855    init_filedist();
856
857     /* validate all the NFS operations that sfs will use */
858     if (Validate > 0) {
859         /*
860          * -F <number of files > or else
861          * DEFAULT_NFILES
862          */
863         if (Tot_client_num_io_files == 0) {
864                 Tot_client_num_io_files = DEFAULT_NFILES;
865         }
866         Num_io_files = Tot_client_num_io_files/children + 1;
867         /* number of non-io files, dir and symlinks base on constants */
868         Num_non_io_files = Tot_client_num_non_io_files/children + 1;
869         Num_dirs = Num_io_files/Files_per_dir + 1;
870         Num_symlinks = Tot_client_num_symlinks/children + 1;
871
872         /* io operations access a subset of the files */
873         Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1;
874         /* non-io and other operations access all of the files */
875         Num_working_non_io_files = Num_io_files;
876         Num_working_dirs = Num_dirs;
877         Num_working_symlinks = Num_symlinks;
878
879         Validate_ops(argc - optind, &argv[optind]);
880         exit(133);
881     }
882
883     /*
884      * Initial check on the mount arguments, must be at least an
885      * even multiple of the number of procs.
886      */
887     if ((argc - optind) % children) {
888         (void) fprintf(stderr,
889 "%s: Invalid mount point list: Not a multiple of number of procs\n",
890                         sfs_Myname);
891         exit(182);
892     }
893
894     /*
895      * -F <number of io files > or else
896      * base files set on load ; this in NON-SPEC though
897      */
898     if (Tot_client_num_io_files == 0) {
899        Tot_client_num_io_files = ((DEFAULT_BYTES_PER_OP / 1024  * total_load)
900                              / (1024)) * files_per_megabyte;
901     }
902     Num_io_files = Tot_client_num_io_files/children + 1;
903
904     /*   
905      * Number of non-io files scales with load and is set at 2% of all files,
906      * but at least DEFAULT_NFILES worth.
907      */  
908     Tot_client_num_non_io_files = Tot_client_num_io_files * 0.02;
909     if (Tot_client_num_non_io_files < DEFAULT_NFILES)
910         Tot_client_num_non_io_files = DEFAULT_NFILES;
911     Num_non_io_files = Tot_client_num_non_io_files/children + 1;
912  
913     /* number of dir and symlinks base on constants */
914     Num_dirs = Num_io_files/Files_per_dir + 1;
915     Num_symlinks = Tot_client_num_symlinks/children + 1;
916
917     /* io operations access a subset of the files */
918     Num_working_io_files = ((Num_io_files * Access_percent) / 100) + 1;
919
920     /* non-io and other operations access all of the files */
921     Num_working_non_io_files = Num_io_files;
922     Num_working_dirs = Num_dirs;
923     Num_working_symlinks = Num_symlinks;
924
925     /*
926      * If we are doing a timed test, we still need an
927      * estimate of how many calls are needed in order to
928      * judge our progress.
929      * If we are doing a test for a number of calls, we still need an
930      * estimate of how long the test will take in order to
931      * establish the time interval between progress checks.
932      */
933     if (Timed_run) {
934         /*
935          * the total number of calls will be divided between the children
936          * when they are forked off.
937          */
938         Ops[TOTAL].target_calls = Runtime * total_load;
939     } else {
940         Runtime = (int) ((float) Ops[TOTAL].target_calls / (float) total_load);
941     }
942
943     /*
944      * multi-client sync support
945      * offset the Runtime value by MULTICLIENT_OFFSET seconds.
946      * This offset prevents the client from finishing before
947      * the Prime Client tells it to 'STOP'. The MULTICLIENT_OFFSET is larger
948      * than the time_out value on the Prime-Client; so in case the client
949      * does not stop when it's told to, the Prime-client should time_out.
950      */
951     if (Prime_client && Timed_run)
952         Runtime += MULTICLIENT_OFFSET;
953
954     /* compute file set sizes */
955     init_fss();
956
957     /* Set up synchronization and results log file */
958     init_logfile();
959
960     /*
961      * setup value of nsigs
962      */
963 #ifdef __NSIG
964     nsigs = __NSIG;
965 #endif
966 #ifdef _NSIG
967     nsigs = _NSIG;
968 #endif
969 #ifdef NSIG
970     nsigs = NSIG;
971 #endif
972 #if defined(SOLARIS2) && !defined(_sys_nsig)
973     nsigs = _sys_siglistn;
974 #endif
975
976     /* Set up the signal handlers for all signals */
977
978 #if (defined(_XOPEN_SOURCE) || defined(USE_POSIX_SIGNALS))
979     {
980         struct sigaction sig_act, old_sig_act;
981
982         /* use XOPEN signal handling */
983
984         sig_act.sa_handler = generic_catcher;
985         (void)sigemptyset(&sig_act.sa_mask);
986         sig_act.sa_flags = 0;
987
988         if (DEBUG_PARENT_GENERAL) {
989             if (nsigs == 0) {
990                 (void) fprintf (stderr,
991                     "WARNING: nsigs not defined, no extra signals caught\n");
992             }
993             for (i = 1; i < nsigs; i++) {
994 /* attempt to set up signal handler for these signals give an error !! K.T. */
995                 if (i!=SIGCHLD && i!=SIGKILL && i!=SIGSTOP && i!=SIGCONT) {
996                     if (sigaction(i,&sig_act,&old_sig_act) == -1) {
997                         if (errno == EINVAL) {
998                             (void) fprintf (stderr,
999                                         "Skipping invalid signal %d\n", i);
1000                         } else {
1001                             perror("sigaction failed");
1002                             exit(134);
1003                         }
1004                     }
1005                 }
1006             }
1007         }
1008
1009         /* signals handlers for signals used by sfs */
1010         sig_act.sa_handler = sfs_cleanup;
1011         if (sigaction(SIGINT,&sig_act,&old_sig_act) == -1) {
1012             perror("sigaction failed: SIGINT");
1013             exit(135);
1014         }
1015
1016         sig_act.sa_handler = sfs_alarm;
1017         if (sigaction(SIGALRM,&sig_act,&old_sig_act) != 0) {
1018             perror("sigaction failed: SIGALRM");
1019             exit(136);
1020         }
1021
1022         sig_act.sa_handler = sfs_cleanup;
1023         if (sigaction(SIGTERM,&sig_act,&old_sig_act) != 0)  {
1024             perror("sigaction failed: SIGTERM");
1025             exit(137);
1026         }
1027
1028         sig_act.sa_handler = sfs_startup;
1029         if (sigaction(SIGUSR1,&sig_act,&old_sig_act) != 0)  {
1030             perror("sigaction failed: SIGUSR1");
1031             exit(138);
1032         }
1033
1034         sig_act.sa_handler = sfs_stop;
1035         if (sigaction(SIGUSR2,&sig_act,&old_sig_act) != 0)  {
1036             perror("sigaction failed: SIGUSR2");
1037             exit(139);
1038         }
1039     }
1040 #else
1041     if (DEBUG_PARENT_GENERAL) {
1042         if (nsigs == 0) {
1043             (void) fprintf (stderr,
1044                     "WARNING: nsigs not defined, no extra signals caught\n");
1045         }
1046         for (i = 1; i < nsigs; i++) {
1047             if (i!=SIGCHLD)
1048                 (void) signal(i, generic_catcher);
1049         }
1050     }
1051     /* signals handlers for signals used by sfs */
1052     (void) signal(SIGINT, sfs_cleanup);
1053     (void) signal(SIGALRM, sfs_alarm);
1054     (void) signal(SIGTERM, sfs_cleanup);
1055     (void) signal(SIGUSR1, sfs_startup);
1056     (void) signal(SIGUSR2, sfs_stop);
1057 #endif
1058
1059     /* Fork children */
1060     for (child_num = 0; child_num < children; child_num++) {
1061         pid = fork();
1062         if (pid == -1) {
1063             Saveerrno = errno;
1064             (void) fprintf(stderr, "%s: can't fork children.", sfs_Myname);
1065             errno = Saveerrno;
1066             perror("fork");
1067             (void) generic_kill(0, SIGINT);
1068             exit(140);
1069         } else if (pid == 0) {
1070             break;      /* get out of child creation */
1071         }
1072         (void) fprintf(pid_fp, "%d\n", pid);
1073     } /* end for forking kids */
1074     (void) fclose(pid_fp);
1075
1076     /*
1077      * Parent: wait for kids to get ready, start them, wait for them to
1078      * finish, read and accumulate results.
1079      */
1080     if (pid != 0) {
1081         if (setuid(Real_uid) != 0) {
1082            (void) fprintf(stderr,"%s: %s%s\n",
1083                    sfs_Myname, "cannot perform setuid operation.\n",
1084                    "Do `make install` as root.\n");
1085         }
1086
1087         /* I'm the parent - let the common code signal handlers know it */
1088         Child_num = -1;
1089
1090         parent(children, total_load, mix_file, iodist_file);
1091
1092         /* Clean up and exit. */
1093         (void) close(Log_fd);
1094         (void) unlink(Logname);
1095         exit(0);
1096
1097     } /* parent */
1098
1099     /*
1100      * Children : initialize, then notify parent through log file,
1101      * wait to get signal, beat the snot out of the server, write
1102      * stats to the log file, and exit.
1103      */
1104     if (pid == 0) {
1105
1106         /* I'm a child - let the common code signal handlers know it */
1107         Child_num = child_num;
1108
1109         /*
1110          * Determine my share of the calls and load (including any left over)
1111          * The call target for each child differs by at most 1 call.
1112          * The load rate for each child differs by at most 1 call/sec.
1113          */
1114         Ops[TOTAL].target_calls = Ops[TOTAL].target_calls / children;
1115         if (child_num <= Ops[TOTAL].target_calls % children) {
1116             Ops[TOTAL].target_calls++;
1117         }
1118         child_load = (float) total_load / (float) children;
1119
1120         /*
1121          * Sleep a bit so the parent can catch up after procreating all us
1122          * children.
1123          */
1124         (void) sleep(10);
1125
1126         child(child_num, children, child_load, argc - optind, &argv[optind]);
1127         exit(0);
1128
1129     } /* child */
1130
1131     (void) unlink(SFS_PNT_PID);
1132
1133     return(0);
1134
1135 } /* main */
1136
1137
1138 /*
1139  * -----------------  Initalization of Parent/Child  ---------------------
1140  */
1141
1142 /*
1143  * Open the multi-client synchronization file with append mode.
1144  */
1145 static void
1146 init_logfile(void)
1147 {
1148     FILE        *cl_log_fd;
1149     int         Saveerrno;
1150
1151     (void) sprintf(Logname, "%s%d", CHILD_SYNC_LOG, Client_num);
1152     Log_fd = open(Logname, (O_RDWR | O_CREAT | O_TRUNC | O_APPEND), 0666);
1153     if (Log_fd == -1) {
1154         Saveerrno = errno;
1155         (void) fprintf(stderr, "%s: can't open log file %s ", sfs_Myname, Logname);
1156         errno = Saveerrno;
1157         perror(Logname);
1158         exit(141);
1159     }
1160     if (chown(Logname, Real_uid, Cur_gid) ==-1) {
1161         perror("chown");
1162         (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname);
1163     }
1164
1165     /* if multi-client execution then init client sync log */
1166     if (Prime_client != NULL) {
1167         /* init logfile and write process id */
1168         (void) sprintf(Client_logname, "%s%d",
1169                         SFS_CLIENT_SYNC_LOG, Client_num);
1170         cl_log_fd = fopen(Client_logname, "w+");
1171         if (chown(Client_logname, Real_uid, Cur_gid) ==-1) {
1172                 perror("chown");
1173                 (void) fprintf(stderr, "%s: chown failed\n", sfs_Myname);
1174         }
1175         if (cl_log_fd == NULL) {
1176             Saveerrno = errno;
1177             (void) fprintf(stderr,
1178                 "%s: can't open Client synchronization file %s ",
1179                             sfs_Myname, Client_logname);
1180             errno = Saveerrno;
1181             perror(Client_logname);
1182             exit(142);
1183         } else {
1184             /* store parent pid */
1185             (void) fprintf(cl_log_fd, "%d", (int)getpid());
1186             (void) fclose(cl_log_fd);
1187         }
1188     } /* init multi-client sync log */
1189
1190 } /* init_logfile */
1191
1192 /*
1193  * ------------------------  Utility Routines  --------------------------
1194  */
1195
1196
1197 /*
1198  * Print the program's usage message.
1199  * Usage: sfs [-l load] [-p procs] [-w warmup] [-t time]
1200  *               [-m mix_file] [-B block_size] [-b blocksz_file]
1201  *               [-f file_set_delta] [-a access_pnct]
1202  *               [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]
1203  *               [-d debug_level] [-i] [-P] [-T op_num]
1204  *               [-V validation_level] [-z] [-Q]
1205  *               [-R biod_reads] [-W biod_writes]
1206  *               [-M prime_client_hostname] [-N client_cnt]
1207  */
1208 static void
1209 usage(void)
1210 {
1211     (void) fprintf(stderr,
1212       "Usage: %s [-l load] [-p procs] [-w warmup] [-t time]\n", sfs_Myname);
1213     (void) fprintf(stderr,
1214       "              [-m mix_file] [-B block_size] [-b blocksz_file]\n");
1215     (void) fprintf(stderr,
1216       "              [-f file_set_delta] [-a access_pnct]\n");
1217     (void) fprintf(stderr,
1218       "              [-A append_pcnt] [-D dir_cnt] [-F file_cnt] [-S symlink_cnt]\n");
1219     (void) fprintf(stderr,
1220       "              [-d debug_level] [-i] [-P] [-T op_num]\n");
1221     (void) fprintf(stderr,
1222       "              [-V validation_level] [-z] [-Q]\n");
1223     (void) fprintf(stderr,
1224       "              [-R biod_reads] [-W biod_writes]\n");
1225     (void) fprintf(stderr,
1226       "              [-M prime_client_hostname] [-N client_cnt]\n");
1227 } /* usage */
1228
1229
1230
1231 /*
1232  * --------------  Command Line File Parsing  -------------------
1233  */
1234
1235 /*
1236  * Constants for mix file
1237  */
1238 #define LINELEN         128             /* max bytes/line in mix file */
1239 #define MIX_START       0
1240 #define MIX_DATALINE    1
1241 #define MIX_DONE        2
1242 #define MIX_FIRSTLINE   3
1243
1244 /*
1245  * Parse the operation mix file 'mix_file'.
1246  *
1247  * ORIGINAL PRE-SFS1.2 format:
1248  *      Assumes that the input file is in the same format as
1249  *      the output of the nfsstat(8) command.
1250  *
1251  *      Uses a simple state transition to keep track of what to expect.
1252  *      Parsing is done a line at a time.
1253  *
1254  *      State           Input                   action          New state
1255  *      MIX_START       ".*nfs:.*"              skip one line   MIX_FIRSTLINE
1256  *      MIX_FIRSTLINE   ".*[0-9]*.*"            get calls       MIX_DATALINE
1257  *      MIX_DATALINE    "[0-9]* [0-9]*%"X6      get op counts   MIX_DATALINE
1258  *      MIX_DATALINE    "[0-9]* [0-9]*%"X4      get op counts   MIX_DONE
1259  *      MIX_DONE        EOF                     return
1260  *
1261  *      We read operation counts from the mix file
1262  *      and compute our own mix percentages,
1263  *      rather than using those in the mix file.
1264  *
1265  * NEW SFS1.2 format version #2:
1266  *      SFS MIXFILE VERSION 2           Version header (must come first line)
1267  *      "^#.*"                          Comment (any line except first)
1268  *      "%s [0-9]*%"                    Op name Op percentage
1269  */
1270 static int
1271 setmix(
1272     char *      mix_file)
1273 {
1274     int         state;          /* current state of state machine */
1275     int         got;            /* number of items read from input line */
1276     int         opnum;          /* operation number index */
1277     int         calls;          /* total number of calls in mix */
1278     char        line[LINELEN];  /* input line buffer */
1279     char        op_name[LINELEN];       /* name buffer */
1280     int         mix_pcnt;
1281     unsigned int        len;    /* length of input line */
1282     FILE        *mix_fp;        /* mix file */
1283     int         vers;           /* mix file version number */
1284     sfs_op_type *op_ptr;
1285
1286     if ((mix_fp = fopen(mix_file, "r")) == NULL) {
1287         (void) fprintf(stderr, "%s: bad mix file", sfs_Myname);
1288         perror(mix_file);
1289         return(-1);
1290     }
1291
1292     if (fgets(line, LINELEN, mix_fp) == NULL) {
1293         (void) fprintf(stderr, "%s: bad mix format - unexpected empty file\n",
1294                                         sfs_Myname);
1295         (void) fclose(mix_fp);
1296         return (-1);
1297     }
1298
1299     opnum = 0;
1300
1301     /*
1302      * Look for initial version string
1303      */
1304     got = sscanf(line, "SFS MIXFILE VERSION %d", &vers);
1305     if (got != 1) {
1306         /*
1307          * Check to see if this is old mixfile
1308          */
1309         len = strlen(line);
1310         if (len < 4 || lad_substr(line, "nfs:") == 0) {
1311             (void) fprintf(stderr, "%s: bad mix format - initial line '%s'\n",
1312                                         sfs_Myname, line);
1313             (void) fclose(mix_fp);
1314             return (-1);
1315         }
1316         vers = 1;
1317     }
1318
1319     if (vers == 1) {
1320         /*
1321          * Old style mix file
1322          */
1323         state = MIX_START;
1324         while (state != MIX_DONE && fgets(line, LINELEN, mix_fp)) {
1325
1326             switch (state) {
1327                 case MIX_START:
1328                     /*
1329                      * Ate first line after nfs:
1330                      */
1331                     state = MIX_FIRSTLINE;
1332                     break;
1333
1334                 case MIX_FIRSTLINE:
1335                     got = sscanf(line, "%d", &calls);
1336                     if (got != 1) {
1337                         (void) fprintf(stderr,
1338                         "%s: bad mix format - can't find 'calls' value %d\n",
1339                                     sfs_Myname,got);
1340                                 (void) fclose(mix_fp);
1341                                 return (-1);
1342                     }
1343                     if (fgets(line, LINELEN, mix_fp) == NULL) {
1344                         (void) fprintf(stderr,
1345                         "%s: bad mix format - unexpected EOF after 'calls'\n",
1346                                     sfs_Myname);
1347                         (void) fclose(mix_fp);
1348                         return (-1);
1349                     }
1350                     state = MIX_DATALINE;
1351                     break;
1352
1353                 case MIX_DATALINE:
1354                     got = sscanf(line,
1355                "%d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%%",
1356                             &Ops[opnum].mix_pcnt,
1357                             &Ops[opnum + 1].mix_pcnt,
1358                             &Ops[opnum + 2].mix_pcnt,
1359                             &Ops[opnum + 3].mix_pcnt,
1360                             &Ops[opnum + 4].mix_pcnt,
1361                             &Ops[opnum + 5].mix_pcnt,
1362                             &Ops[opnum + 6].mix_pcnt);
1363
1364                     if (got == 4 && opnum == 14) {
1365                         /* looks like the last line */
1366                         state = MIX_DONE;
1367                     } else if (got == 7) {
1368                         opnum += 7;
1369                         if (fgets(line, LINELEN, mix_fp) == NULL) {
1370                             (void) fprintf(stderr,
1371                         "%s: bad mix format - unexpected EOF after 'calls'\n",
1372                                         sfs_Myname);
1373                             (void) fclose(mix_fp);
1374                             return (-1);
1375                         }
1376                     } else {
1377                         (void) fprintf(stderr,
1378                             "%s: bad mix format - can't find %d op values\n",
1379                                 sfs_Myname, got);
1380                         (void) fclose(mix_fp);
1381                         return (-1);
1382                     }
1383                     break;
1384
1385                 default:
1386                     (void) fprintf(stderr,
1387                                 "%s: error parsing mix file - bad state %d\n",
1388                                 sfs_Myname, state);
1389                     (void) fclose(mix_fp);
1390                     return (-1);
1391             } /* end switch on state */
1392         } /* end while there are lines to read */
1393
1394         if (state != MIX_DONE) {
1395             (void) fprintf(stderr, "%s: bad mix format - unexpected EOF\n",
1396                 sfs_Myname);
1397             (void) fclose(mix_fp);
1398             return (-1);
1399         }
1400         for (opnum = 0; opnum < NOPS; opnum++) {
1401             Ops[opnum].mix_pcnt = Ops[opnum].mix_pcnt * 100 / calls
1402                              + ((Ops[opnum].mix_pcnt * 1000 / calls % 10) >= 5);
1403         }
1404         (void) fclose(mix_fp);
1405         return (0);
1406     }
1407     if (vers == 2) {
1408         /*
1409          * New style mix file
1410          */
1411         while (fgets(line, LINELEN, mix_fp) != NULL) {
1412             if (line[0] == '#')                 /* Comment line */
1413                 continue;
1414             got = sscanf(line, "%s %d", op_name, &mix_pcnt);
1415             if (got != 2) {
1416                 (void) fprintf(stderr,
1417                             "%s: bad mix format - can't find op values: %s\n",
1418                                 sfs_Myname, line);
1419                 (void) fclose(mix_fp);
1420                 return (-1);
1421             }
1422             op_ptr = Ops;
1423             while (strcmp(op_ptr->name, "TOTAL") != 0) {
1424                 if (strcmp(op_ptr->name, op_name) == 0) {
1425                     op_ptr->mix_pcnt = mix_pcnt;
1426                     break;
1427                 }
1428                 op_ptr++;
1429             }
1430             if (strcmp(op_ptr->name, "TOTAL") == 0) {
1431                 (void) fprintf(stderr,
1432                             "%s: unknown op name: %s\n",
1433                                 sfs_Myname, op_name);
1434                 (void) fclose(mix_fp);
1435                 return (-1);
1436             }
1437         }
1438         /*
1439          * Make sure that the total mix percentages == 100
1440          */
1441         op_ptr = Ops;
1442         mix_pcnt = 0;
1443         while (strcmp(op_ptr->name, "TOTAL") != 0) {
1444             mix_pcnt += op_ptr->mix_pcnt;
1445             op_ptr++;
1446         }
1447         if (mix_pcnt != 100) {
1448             (void) fprintf(stderr,
1449                             "%s: WARNING total mix percentage %d != 100\n",
1450                                 sfs_Myname, mix_pcnt);
1451         }
1452         (void) fclose(mix_fp);
1453         return (0);
1454     }
1455
1456     (void) fprintf(stderr, "%s: Unknown mix file version number %d\n",
1457                                 sfs_Myname, vers);
1458     (void) fclose(mix_fp);
1459     return (-1);
1460 } /* setmix */
1461
1462
1463 /*
1464  * Parse the block I/O distribution file 'fp'.
1465  */
1466 static int
1467 setiodist(
1468     FILE *      fp)
1469 {
1470     int         i;
1471
1472     /* first, read and parse the i/o distribution file for syntax and size */
1473     if (parseiodist(fp, 1) == -1) {
1474         exit(143);
1475     }
1476     (void) fseek(fp, 0, SEEK_SET);
1477
1478     /* read the i/o distribution file into the i/o dist table */
1479     if (parseiodist(fp, 2) == -1) {
1480         exit(144);
1481     }
1482
1483     if (DEBUG_PARENT_GENERAL) {
1484         (void) fprintf(stdout, "I/o Distribution Table\n");
1485         (void) fprintf(stdout, "Read:\n");
1486         (void) fprintf(stdout, "\tpcnt bufs frags\n");
1487         for (i = 0 ; ; i++) {
1488             (void) fprintf(stdout, "\t%4d %4d %5d\n", Io_dist_ptr->read[i].pcnt,
1489                         Io_dist_ptr->read[i].bufs, Io_dist_ptr->read[i].frags);
1490             if (Io_dist_ptr->read[i].pcnt == 100)
1491                 break;
1492         }
1493
1494         (void) fprintf(stdout, "Write:\n");
1495         (void) fprintf(stdout, "\tpcnt bufs frags\n");
1496         for (i = 0; ; i++) {
1497             (void) fprintf(stdout, "\t%4d %4d %5d\n",
1498                             Io_dist_ptr->write[i].pcnt,
1499                             Io_dist_ptr->write[i].bufs,
1500                             Io_dist_ptr->write[i].frags);
1501             if (Io_dist_ptr->write[i].pcnt == 100)
1502                 break;
1503         }
1504         (void) fprintf(stdout, "Maximum file size: %d KB (%d * %d KB)\n",
1505                         Io_dist_ptr->max_bufs * Kb_per_block,
1506                         Io_dist_ptr->max_bufs, Kb_per_block);
1507     }
1508     return(0);
1509 } /* setiodist */
1510
1511
1512 /*
1513  * Block/File Distribution file parser.
1514  * Assumes that the input file is in the following format:
1515  *
1516  *
1517  *      READ_KEY_WORD
1518  *      percent         block_cnt               fragment_flag
1519  *         .                 .                        .
1520  *         .                 .                        .
1521  *         .                 .                        .
1522  *      WRITE_KEY_WORD
1523  *      percent         block_cnt               fragment_flag
1524  *         .                 .                        .
1525  *         .                 .                        .
1526  *         .                 .                        .
1527  *
1528  *
1529  * Notes:
1530  *      - The READ_KEY_WORD is "Read", the WRITE_KEY_WORD is "Write".
1531  *      - For each key word, the percent fields must sum to 100.
1532  *      - Fragment is either true (1) or false (0)
1533  *      - Maximum file size (and transfer size) is the largest
1534  *        eight_k_cnt * 8KB plus 7 Kb for fragments
1535  *
1536  *
1537  * Uses a simple state transition to keep track of what to expect.
1538  * Parsing is done a line at a time.
1539  *
1540  * State        Input                   action          New state
1541  * -----        --------------------    -------------   ---------
1542  * START        "Read"                  skip one line   READ
1543  * START        "Write"                 skip one line   WRITE
1544  * READ         "[0-9]* [0-9]* [01]"    get values      READ
1545  * READ         "Write"                 skip one line   WRITE
1546  * WRITE        "[0-9]* [0-9]* [01]"    get values      WRITE
1547  * WRITE        "Read"                  skip one line   READ
1548  * DONE         EOF                     return
1549  *
1550  * Pass 1 reads the file and allocates table space.
1551  * Pass 2 reads the file data into the tables.
1552  */
1553 static int
1554 parseiodist(
1555     FILE *      fp,
1556     int         pass)
1557 {
1558     int         state;          /* current state of state machine */
1559     int         got;            /* number of items read from input line */
1560     int         pcnt;           /* percent read from input line */
1561     int         bufs;           /* eight_kb_buffer_cnt read from input line */
1562     int         frags;          /* fragment flag read from input line */
1563     int         rbucket;        /* current read distribution table bucket */
1564     int         wbucket;        /* current write distribution table bucket */
1565     int         rpcnt;          /* cumulative percent for read buckets */
1566     int         wpcnt;          /* cumulative percent for write buckets */
1567     char        key[5];         /* keyword buffer */
1568     char        line[LINELEN];  /* input line buffer */
1569     int         nextline;
1570
1571     /*
1572      * Pass 1 reads, sizes, and error checks the input
1573      * and then allocates space for the distribution tables.
1574      * Pass 2 reads the input into the allocated tables.
1575      */
1576
1577     rbucket = 0;
1578     wbucket = 0;
1579     rpcnt = 0;
1580     wpcnt = 0;
1581     state = IO_DIST_START;
1582
1583     while (fgets(line, LINELEN, fp)) {
1584
1585         nextline = 0;
1586         while (nextline == 0) {
1587
1588             if (state == IO_DIST_READ) {
1589                 got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags);
1590                 if (got != 3) {
1591                     state = IO_DIST_START;
1592                     continue; /* same line, but goto new state */
1593                 }
1594                 if (pass == 1) {
1595                     rbucket++;
1596                     rpcnt += pcnt;
1597                     if (frags != 0 && frags != 1) {
1598                         (void) fprintf(stderr,
1599                             "%s: bad i/o dist format - bad fragment value\n",
1600                                         sfs_Myname);
1601                         return(-1);
1602                     }
1603                 } else {
1604                     rpcnt += pcnt;
1605                     Io_dist_ptr->read[rbucket].pcnt = rpcnt;
1606                     Io_dist_ptr->read[rbucket].bufs = bufs;
1607                     Io_dist_ptr->read[rbucket].frags = frags;
1608                     rbucket++;
1609                 }
1610                 if (DEBUG_CHILD_FILES) {
1611                     (void) fprintf(stdout, "p=%d b=%d f=%d rpcnt=%d\n",
1612                                     pcnt, bufs, frags, rpcnt);
1613                     (void) fflush(stdout);
1614                 }
1615
1616                 /* read next line in file */
1617                 nextline++;
1618                 break;
1619             }
1620
1621             if (state == IO_DIST_WRITE) {
1622                 got = sscanf(line, "%d %d %d", &pcnt, &bufs, &frags);
1623                 if (got != 3) {
1624                     state = IO_DIST_START;
1625                     continue; /* same line, but goto new state */
1626                 }
1627                 if (pass == 1) {
1628                     wbucket++;
1629                     wpcnt += pcnt;
1630                     if (frags != 0 && frags != 1) {
1631                         (void) fprintf(stderr,
1632                             "%s: bad i/o dist format - bad fragment value\n",
1633                                         sfs_Myname);
1634                         return(-1);
1635                     }
1636                 } else {
1637                     wpcnt += pcnt;
1638                     Io_dist_ptr->write[wbucket].pcnt = wpcnt;
1639                     Io_dist_ptr->write[wbucket].bufs = bufs;
1640                     Io_dist_ptr->write[wbucket].frags = frags;
1641                     wbucket++;
1642                 }
1643                 if (DEBUG_CHILD_FILES) {
1644                     (void) fprintf(stdout, "p=%d b=%d f=%d wpcnt=%d\n",
1645                                     pcnt, bufs, frags, wpcnt);
1646                     (void) fflush(stdout);
1647                 }
1648                 /* read next line in file */
1649                 nextline++;
1650                 break;
1651             }
1652
1653             if (state == IO_DIST_START) {
1654                 got = sscanf(line, "%s", key);
1655                 if (got != 1 || (strlen(key) != 5)){
1656                     (void) fprintf(stderr,
1657                             "%s: bad i/o dist format - invalid keyword %s\n",
1658                                     sfs_Myname, key);
1659                     return(-1);
1660                 }
1661                 if (!strcmp(key, "Read") || !strcmp(key, "read")) {
1662                     if (rbucket != 0) {
1663                         (void) fprintf(stderr,
1664                         "%s: bad i/o dist format - too many read keywords\n",
1665                                         sfs_Myname);
1666                         return(-1);
1667                     }
1668                     rpcnt = 0;
1669                     state = IO_DIST_READ;
1670
1671                     /* read next line in file */
1672                     nextline++;
1673                     break;
1674                 }
1675                 if (!strcmp(key, "Write") || !strcmp(key, "write")) {
1676                     if (wbucket != 0) {
1677                         (void) fprintf(stderr,
1678                        "%s: bad i/o dist format - too many write keywords\n",
1679                                         sfs_Myname);
1680                         return(-1);
1681                     }
1682                     wpcnt = 0;
1683                     state = IO_DIST_WRITE;
1684
1685                     /* read next line in file */
1686                     nextline++;
1687                     break;
1688                 }
1689                 (void) fprintf(stderr,
1690                             "%s: bad i/o dist format - unknown keyword %s\n",
1691                                 sfs_Myname, key);
1692                 return(-1);
1693             }
1694
1695         } /* end while processing this line */
1696     } /* end while more lines */
1697
1698     if (pass == 1) {
1699
1700         /* error check the input */
1701         if (rbucket == 0) {
1702             (void) fprintf(stderr,
1703                     "%s: bad i/o dist format - no read distribution data\n",
1704                             sfs_Myname);
1705             return(-1);
1706         }
1707         if (rpcnt != 100) {
1708             (void) fprintf(stderr,
1709                         "%s: bad i/o dist format - read total percent != 100\n",
1710                             sfs_Myname);
1711             return(-1);
1712         }
1713         if (wbucket == 0) {
1714             (void) fprintf(stderr,
1715                     "%s: bad i/o dist format - no write distribution data\n",
1716                             sfs_Myname);
1717             return(-1);
1718         }
1719         if (wpcnt != 100) {
1720             (void) fprintf(stderr,
1721                     "%s: bad i/o dist format - write percent total != 100\n",
1722                             sfs_Myname);
1723             return(-1);
1724         }
1725
1726         /* allocate space for the table */
1727         if ((Io_dist_ptr = (sfs_io_dist_type *)
1728                 malloc(sizeof(sfs_io_dist_type))) == NULL) {
1729             (void) fprintf(stderr,
1730                     "%s: block i/o distribution table allocation failed\n",
1731                             sfs_Myname);
1732                 return(-1);
1733         }
1734         if ((Io_dist_ptr->read = (sfs_io_op_dist_type *)
1735                 malloc(rbucket*sizeof(sfs_io_op_dist_type))) == NULL) {
1736             (void) fprintf(stderr,
1737                     "%s: read distribution table allocation failed\n", sfs_Myname);
1738             return(-1);
1739         }
1740         if ((Io_dist_ptr->write = (sfs_io_op_dist_type *)
1741                 malloc(wbucket*sizeof(sfs_io_op_dist_type)))==NULL) {
1742             (void) fprintf(stderr,
1743                     "%s: write distribution table allocation failed\n", sfs_Myname);
1744             return(-1);
1745         }
1746
1747     }
1748     return(0);
1749
1750 } /* parseiodist */
1751
1752
1753 /*
1754  * Compute the max i/o transfer size and average ops per request
1755  * for the block transfer distribution table.
1756  */
1757 static void
1758 init_iodist(
1759     sfs_io_dist_type    *       io_dist_ptr)
1760 {
1761     int                         max_bufs;
1762     double                      weighted_ops;
1763     double                      previous_pcnt;
1764     int                         i;
1765
1766     /*
1767      * compute expected number of ops for multi-op requests.
1768      * the calculation assumes that if a i/o distribution table
1769      * entry specifies that a fragment is to be generated, then
1770      * exactly one OTW operation will result.
1771      */
1772     max_bufs = 0;
1773
1774     weighted_ops = 0.0;
1775     previous_pcnt = 0.0;
1776     for (i = 0; ; i++) {
1777         weighted_ops += (io_dist_ptr->read[i].pcnt - previous_pcnt) *
1778                       (io_dist_ptr->read[i].bufs + io_dist_ptr->read[i].frags);
1779         previous_pcnt = io_dist_ptr->read[i].pcnt;
1780         if (io_dist_ptr->read[i].bufs > max_bufs)
1781             max_bufs = io_dist_ptr->read[i].bufs;
1782         if (io_dist_ptr->read[i].pcnt == 100)
1783             break;
1784     }
1785     io_dist_ptr->avg_ops_per_read_req = weighted_ops / 100.0;
1786
1787     weighted_ops = 0.0;
1788     previous_pcnt = 0.0;
1789     for (i = 0; ; i++) {
1790         weighted_ops += (io_dist_ptr->write[i].pcnt - previous_pcnt) *
1791                     (io_dist_ptr->write[i].bufs + io_dist_ptr->write[i].frags);
1792         previous_pcnt = io_dist_ptr->write[i].pcnt;
1793         if (io_dist_ptr->write[i].bufs > max_bufs)
1794             max_bufs = io_dist_ptr->write[i].bufs;
1795         if (io_dist_ptr->write[i].pcnt == 100)
1796             break;
1797     }
1798     io_dist_ptr->avg_ops_per_write_req = weighted_ops / 100.0;
1799
1800     io_dist_ptr->max_bufs = max_bufs + 1;
1801
1802 } /* init_iodist */
1803
1804 static void
1805 init_filedist()
1806 {
1807     int i;
1808     int cur_pcnt;
1809     int num_files = 0;
1810     int prev_pcnt = 0;
1811     int tot_size = 0;
1812
1813     /*
1814      * Calculate the average number of bytes per file
1815      */
1816     for (i = 0; Default_file_size_dist[i].size != 0; i++) {
1817         cur_pcnt = Default_file_size_dist[i].pcnt - prev_pcnt;
1818         num_files += cur_pcnt;
1819         tot_size += (Default_file_size_dist[i].size * 1024) * cur_pcnt;
1820         prev_pcnt = Default_file_size_dist[i].pcnt;
1821     }
1822
1823     avg_bytes_per_file = tot_size / num_files;
1824     files_per_megabyte = (((1024*1024) + avg_bytes_per_file) \
1825                                         / avg_bytes_per_file);
1826 }
1827
1828 static void
1829 init_fss()
1830 {
1831     int Delta_fss_bytes;
1832
1833     Base_fss_bytes = Num_working_io_files * (avg_bytes_per_file / 1024);
1834     Total_fss_bytes = Num_io_files * (avg_bytes_per_file / 1024);
1835     Cur_fss_bytes = Base_fss_bytes;
1836     Delta_fss_bytes = (Base_fss_bytes * Fss_delta_percent) / 100;
1837     Limit_fss_bytes = Base_fss_bytes + Delta_fss_bytes;
1838     Most_fss_bytes = Base_fss_bytes;
1839     Least_fss_bytes = Base_fss_bytes;
1840 }
1841
1842 /*
1843  * return true if 'sp' contains the substring 'subsp', false otherwise
1844  */
1845 static int
1846 lad_substr(
1847     char *              sp,
1848     char *              subsp)
1849 {
1850     unsigned int        found;
1851     int                 want;
1852     char *              s2;
1853
1854     if (sp == NULL || subsp == NULL) {
1855         return (0);
1856     }
1857
1858     want = strlen(subsp);
1859
1860     while (*sp != '\0') {
1861         while (*sp != *subsp && *sp != '\0') {
1862             sp++;
1863         }
1864         found = 0;
1865         s2 = subsp;
1866         while (*sp == *s2 && *sp != '\0') {
1867             sp++;
1868             s2++;
1869             found++;
1870         }
1871         if (found == want) {
1872             return (1);
1873         }
1874     }
1875     return (0);
1876
1877 } /* lad_substr */
1878
1879 /*
1880  * Check the gettimeofday() resolution. If the resolution
1881  * is in chunks bigger than SFS_MIN_RES then the client
1882  * does not have a usable resolution for running the 
1883  * benchmark.
1884  */
1885 static void
1886 check_clock(void)
1887 {
1888         double time_res;
1889         char tmp_hostname[HOSTNAME_LEN];
1890
1891         time_res = get_resolution();
1892         getmyhostname(tmp_hostname, HOSTNAME_LEN);
1893         if( time_res > (double)SFS_MIN_RES )
1894         {
1895                 (void) fprintf(stderr,
1896                 "\n%s: Clock resolution too poor to obtain valid results.\n",
1897                         tmp_hostname);
1898                 (void) fprintf(stderr,
1899                 "%s: Clock resolution %f Micro seconds.\n", tmp_hostname,
1900                         time_res);
1901                 exit(175);
1902         }
1903         else
1904         {
1905                 (void) fprintf(stderr,
1906                 "\n%s: Good clock resolution [ %f ] Micro seconds.\n", 
1907                         tmp_hostname, time_res);
1908         }
1909 }
1910
1911 /*
1912  * Lifted code from Iozone with permission from author. (Don Capps)
1913  * Returns the resolution of the gettimeofday() function 
1914  * in microseconds.
1915  */
1916 static double
1917 get_resolution(void)
1918 {
1919         double starttime, finishtime, besttime;
1920         long  j,delay;
1921         int k;
1922
1923         finishtime=time_so_far1(); /* Warm up the instruction cache */
1924         starttime=time_so_far1();  /* Warm up the instruction cache */
1925         delay=j=0;                 /* Warm up the data cache */
1926         for(k=0;k<10;k++)
1927         {
1928                 while(1)
1929                 {
1930                         starttime=time_so_far1();
1931                         for(j=0;j< delay;j++)
1932                         ;
1933                         finishtime=time_so_far1();
1934                         if(starttime==finishtime)
1935                                 delay++;
1936                         else
1937                         {
1938                                 if(k==0)
1939                                         besttime=(finishtime-starttime);
1940                                 if((finishtime-starttime) < besttime)
1941                                         besttime=(finishtime-starttime);
1942                                 break;
1943                         }
1944                 }
1945         }
1946          return(besttime);
1947 }
1948
1949 /*
1950  * Lifted code from Iozone with permission from author. (Don Capps)
1951  * Returns current result of gettimeofday() in microseconds.
1952  */
1953 /************************************************************************/
1954 /* Time measurement routines.                                           */
1955 /* Return time in microseconds                                          */
1956 /************************************************************************/
1957
1958 static double
1959 time_so_far1(void)
1960 {
1961         /* For Windows the time_of_day() is useless. It increments in 55 */
1962         /* milli second increments. By using the Win32api one can get */
1963         /* access to the high performance measurement interfaces. */
1964         /* With this one can get back into the 8 to 9 microsecond */
1965         /* resolution.  */
1966 #ifdef Windows
1967         LARGE_INTEGER freq,counter;
1968         double wintime;
1969         double bigcounter;
1970
1971         QueryPerformanceFrequency(&freq);
1972         QueryPerformanceCounter(&counter);
1973         bigcounter=(double)counter.HighPart *(double)0xffffffff +
1974                 (double)counter.LowPart;
1975         wintime = (double)(bigcounter/(double)freq.LowPart);
1976         return((double)wintime*1000000.0);
1977 #else
1978 #if defined (OSFV4) || defined(OSFV3) || defined(OSFV5)
1979   struct timespec gp;
1980
1981   if (getclock(TIMEOFDAY, (struct timespec *) &gp) == -1)
1982     perror("getclock");
1983   return (( (double) (gp.tv_sec)*1000000.0) +
1984     ( ((float)(gp.tv_nsec)) * 0.001 ));
1985 #else
1986   struct timeval tp;
1987
1988   if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
1989     perror("gettimeofday");
1990   return ((double) (tp.tv_sec)*1000000.0) +
1991     (((double) tp.tv_usec) );
1992 #endif
1993 #endif
1994 }
1995
1996 /* sfs_c_chd.c */
1997 /* sfs_c_man.c */
1998