Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / TBBT / trace_play / sfs_c_chd.c.org
1 #ifndef lint
2 static char sfs_c_chdSid[] = "@(#)sfs_c_chd.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  *                                                               *
26  *      Copyright 1991,1992  Legato Systems, Inc.                *
27  *      Copyright 1991,1992  Auspex Systems, Inc.                *
28  *      Copyright 1991,1992  Data General Corporation            *
29  *      Copyright 1991,1992  Digital Equipment Corporation       *
30  *      Copyright 1991,1992  Interphase Corporation              *
31  *      Copyright 1991,1992  Sun Microsystems, Inc.              *
32  *                                                               *
33  *****************************************************************/
34
35 /*
36  * -------------------------- sfs_c_chd.c -------------------------
37  *
38  *      The sfs child.  Routines to initialize child parameters,
39  *      initialize test directories, and generate load.
40  *
41  *.Exported_Routines
42  *      void child(int, float, int, char *);
43  *      void init_fileinfo(void);
44  *      void init_counters(void);
45  *      sfs_fh_type * randfh(int, int, uint_t, sfs_state_type,
46  *                              sfs_file_type);
47  *      int check_access(struct *stat)
48  *      int check_fh_access();
49  *
50  *.Local_Routines
51  *      void check_call_rate(void);
52  *      void init_targets(void);
53  *      void init_dirlayout(void);
54  *      void init_rpc(void);
55  *      void init_testdir(void);
56  *      int do_op(void);
57  *      int op(int);
58  *
59  *.Revision_History
60  *      21-Aug-92       Wittle          randfh() uses working set files array.
61  *                                      init_fileinfo() sets up working set.
62  *      02-Jul-92       Teelucksingh    Target file size now based on peak load
63  *                                      instead of BTDT.
64  *      04-Jan-92       Pawlowski       Added raw data dump hooks.
65  *      16-Dec-91       Wittle          Created.
66  */
67
68
69 /*
70  * -------------------------  Include Files  -------------------------
71  */
72
73 /*
74  * ANSI C headers
75  */
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <errno.h>
80 #include <math.h>
81 #include <signal.h>
82
83 #include <sys/types.h>
84 #include <sys/stat.h> 
85  
86 #include <unistd.h>
87
88 #include "sfs_c_def.h"
89 #include "sfs_m_def.h"
90
91 extern struct hostent   *Server_hostent;
92
93 #define PROB_SCALE 1000L
94 #define _M_MODULUS 2147483647L /* (2**31)-1 */
95
96 #define _GROUP_DIVISOR 500
97 #define _FILES_PER_GROUP 4
98 #define _MIN_GROUPS 12
99 #define _WORKING_SET_AT_25_OPS_PER_SEC 975
100
101 /*
102  * -----------------------  External Definitions  -----------------------
103  */
104
105 /* forward definitions for local functions */
106 static void check_call_rate(void);
107 static void init_targets(void);
108 static int init_rpc(void);
109 static void init_testdir(void);
110 static int do_op(void);
111 static int op(int);
112 static void init_dirlayout(void);
113
114
115 /*
116  * -------------------  File Set Size Control -------------------------
117  */
118 static uint_t Setattr_borrowed = 0;     /* setattr op used for file truncate */
119 static uint_t Create_borrowed = 0;              /* create op used for file truncate */
120
121 /*
122  * -------------  Per Child Load Generation Rate Variables  -----------
123  */
124 static float Child_call_load;   /* per child call/sec rate */
125 static float Child_req_load;    /* per child req/sec rate */
126 static uint_t Calls_this_period; /* calls made during the current run period */
127 static uint_t Calls_this_test;  /* calls made during the test so far */
128 static uint_t Reqs_this_period; /* reqs made during the current run period */
129 static uint_t Reqs_this_test;   /* reqs made during the test so far */
130 static uint_t Sleep_msec_this_test; /* msec slept during the test so far */
131 static uint_t Sleep_msec_this_period;
132 static uint_t Previous_chkpnt_msec; /* beginning time of current run period */
133 static int Target_sleep_mspc;   /* targeted sleep time per call */
134 static int Measurement_in_progress = 0;
135
136 static sfs_work_set_type Dir_working_set;
137 static sfs_work_set_type Io_working_set;
138 static sfs_work_set_type Non_io_working_set;
139 static sfs_work_set_type Symlink_working_set;
140
141 static uint_t Files_created = 0;         /* unique integer part of file names */
142 static char io_buf[BUFSIZ];
143 int generations = 
144         (_WORKING_SET_AT_25_OPS_PER_SEC/_GROUP_DIVISOR) * _MIN_GROUPS;
145 /*
146  * -------------------------  SFS Child  -------------------------
147  */
148
149
150 /*
151  * Child number 'child_num'.  Initialize internal data structure and
152  * the test directory,  then notify parent (through log file) that we
153  * are ready to start generating 'load' calls per second into the current
154  * working directory, or optionally, into the directories specified by
155  * 'argc' and 'argv'.  Wait for the start signal, and then generate load
156  * until we complete all our goal for calls or until the run time expires,
157  * depending on the 'Timed_run' flag.  The run time expires when the parent
158  * sends the stop signal.
159  */
160 void
161 child(
162     int         child_num,
163     int         children,
164     float       load,
165     int         argc,
166     char        *argv[])
167 {
168     char        namebuf[NFS_MAXNAMLEN]; /* unique name for this program */
169     char        *nameptr;
170     int         i;                      /* general use */
171     int         op_count;               /* ops completed during each request */
172     uint_t      rand_sleep_msec;        /* random sleep msec between calls */
173     uint_t      current_msec;           /* current test time in msecs */
174     double      previous_pcnt;
175     CLIENT *    mount_client_ptr;       /* Mount client handle */
176     char *      mount_point;            /* Mount point for remote FS */
177     int         Saveerrno;
178     int         mnt_argc;
179
180     struct ladtime      elapsed_time;   /* Current_time - Start_time */
181     sfs_results_report_type     report; /* final results log */
182
183     (void) setvbuf(stderr, io_buf, _IOLBF, BUFSIZ);
184
185     /* Change my name for error logging */
186     if ((nameptr = strrchr(sfs_Myname, '/')) != NULL)
187         sfs_Myname = ++nameptr;
188     (void) sprintf(namebuf, "%s%d", sfs_Myname, child_num);
189     sfs_Myname = namebuf;
190     Child_call_load = load;
191     Current_test_phase = Mount_phase;
192
193     /* Seed the random number generator based on my child number */
194
195     /*
196      * Note: If random seeds are allocated by the prime client
197      * then this code must change.
198      */
199     sfs_srandom((int)(load + Child_num + 1));
200
201     /* Setup user and group information */
202     Cur_uid = Real_uid;
203
204     /*
205      * Initialize call and request targets.
206      * Calls are the Over-The-Wire (OTW) operations that occur due to
207      * each request.  A request may cause one or more calls.
208      * Initialize the child file info and mount the remote test directory.
209      * Set up the rpc and biod structures.
210      */
211     init_targets();
212     init_fileinfo();
213     init_dirlayout();
214
215     /*
216      * Mount points list:
217      *   If the mount point list is equal to the number of procs (P), the
218      *   mount point for child M is the M'th entry in the list.
219      *   If the mount point list is greater than the number of procs (P), the
220      *   mount point for client N child M is ((N - 1) * P) + M
221      */
222     if (argc == children)
223         mnt_argc = Child_num;
224     else
225         mnt_argc = (Client_num - 1) * children + Child_num;
226
227     if (mnt_argc >= argc) {
228         (void) fprintf(stderr,
229 "%s: Invalid mount point list: required %d only specified %d mount points\n",
230                         sfs_Myname, mnt_argc + 1, argc);
231         (void) generic_kill(0, SIGINT);
232         exit(181);
233     }
234
235     mount_point = argv[mnt_argc];
236
237     /*
238      * May require root priv to perform bindresvport operation
239      */
240     mount_client_ptr = lad_getmnt_hand(mount_point);
241     if (mount_client_ptr == NULL) {
242         exit(145);
243     }
244
245     /*
246      * should be all done doing priv port stuff
247      */
248
249     if (init_rpc() == -1) {
250         (void) fprintf(stderr, "%s: rpc initialization failed\n", sfs_Myname);
251         (void) generic_kill(0, SIGINT);
252         exit(146);
253     }
254
255     /*
256      * finish all priv bindresvport calls
257      * reset uid
258      */
259     if (setuid(Real_uid) != (uid_t)0) {
260         (void) fprintf(stderr,"%s: %s%s", sfs_Myname,
261             "cannot perform setuid operation.\n",
262             "Do `make install` as root.\n");
263     }
264
265     init_mount_point(Child_num, mount_point, mount_client_ptr);
266
267     /*
268      * Cleanup client handle for mount point
269      */
270     clnt_destroy(mount_client_ptr);
271
272     /*
273      * Tell parent I'm ready to initialize my test directory,
274      * wait for the go ahead signal.
275      */
276     if (write(Log_fd, "x", 1) != 1) {
277         (void) fprintf(stderr, "%s: can't write to synchronization file %s",
278                         sfs_Myname, Logname);
279         (void) generic_kill(0, SIGINT);
280         exit(147);
281     }
282     (void) pause();
283
284     if (DEBUG_CHILD_GENERAL) {
285         if (Timed_run) {
286             if (Prime_client) {
287                 (void) fprintf(stderr,
288         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
289                     Child_num, Child_call_load, Child_req_load,
290                     Runtime - MULTICLIENT_OFFSET);
291             }
292             else {
293             (void) fprintf(stderr,
294         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
295                     Child_num, Child_call_load, Child_req_load, Runtime);
296             }
297         } else {
298             (void) fprintf(stderr,
299         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n",
300                     Child_num, Child_call_load, Child_req_load,
301                     Ops[TOTAL].target_calls);
302         }
303         (void) fflush(stderr);
304     }
305
306     /* Initialize the test directory */
307     Current_test_phase = Populate_phase;
308     init_testdir();
309
310     /*
311      * activate the biod behaviour if desired
312      */
313     if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) {
314         biod_turn_on();
315     }
316
317     /*
318      * Tell parent I'm ready to start test, wait for the go ahead signal.
319      */
320     if (write(Log_fd, "x", 1) != 1) {
321         (void) fprintf(stderr, "%s: can't write to synchronization file %s\n",
322                         sfs_Myname, Logname);
323         (void) generic_kill(0, SIGINT);
324         exit(148);
325     }
326     (void) pause();
327
328     if (DEBUG_CHILD_GENERAL) {
329         if (Timed_run) {
330             if (Prime_client) {
331                 (void) fprintf(stderr,
332         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
333                     Child_num, Child_call_load, Child_req_load,
334                     Runtime - MULTICLIENT_OFFSET);
335             }
336             else {
337             (void) fprintf(stderr,
338         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d seconds\n",
339                     Child_num, Child_call_load, Child_req_load, Runtime);
340             }
341         } else {
342             (void) fprintf(stderr,
343         "Child %d loading at %3.2f calls/sec (%3.2f reqs/sec) for %d calls\n",
344                     Child_num, Child_call_load, Child_req_load,
345                     Ops[TOTAL].target_calls);
346         }
347         (void) fflush(stderr);
348     }
349
350
351     /* Start the warmup phase; initialize operation counters */
352     Current_test_phase = Warmup_phase;
353     init_counters();
354     Measurement_in_progress = 0;
355
356     /*
357      * Compute the average sleep time per call.
358      * Start off with the assumption that we can sleep half the time.
359      * Note: using msec-per-call to adjust sleeping time
360      * limits benchmark load rates to less than 1000 calls-per-sec-per-child.
361      */
362     Target_sleep_mspc = (int) (((1000.0 / Child_call_load) / 2.0) + .5);
363
364     /*
365      * Occasionally, check to see if ops are being generating at the
366      * correct rate.  During the warmup phase, checks are made every 2 seconds.
367      * Hopefully, this will allow the test to reach steady state before the
368      * warmup phase ends.  During the timed test run, checks are made every
369      * 10 seconds.  The switch is made when we receive the start signal.
370      */
371     Msec_per_period = DEFAULT_WARM_RATE_CHECK * 1000;
372
373     /* Loop generating load */
374     while ((Timed_run && Runtime) ||
375            (!Timed_run &&
376             (Ops[TOTAL].results.good_calls < Ops[TOTAL].target_calls))) {
377
378         if (start_run_phase) {
379             init_counters();
380             Measurement_in_progress = 1;
381             /*
382              * Progress is checked every 10 seconds during the test run.
383              */  
384             Msec_per_period = DEFAULT_RUN_RATE_CHECK * 1000;
385
386             start_run_phase = 0;
387         }
388
389         /* Do an NFS operation, unless we need to sleep for the whole period. */
390         if (Target_sleep_mspc < Msec_per_period)
391             op_count = do_op();
392         else
393             op_count = 0;
394
395         /* if the call was successful, add op_count to the period total. */
396         if (op_count > 0) {
397             Calls_this_period += op_count;
398             Reqs_this_period++;
399         }
400
401         /*
402          * If the call was successful,
403          * or we need to sleep for the whole period,
404          * sleep for a while before doing the next op.
405          */
406         if ((op_count > 0) || (Target_sleep_mspc >= Msec_per_period)) {
407             /*
408              * Sleep for the whole period or
409              * for a random (positive) time period in the range
410              * (Target_sleep_mspc +- 1/2(Target_sleep_mspc)).
411              */
412             if (Target_sleep_mspc >= Msec_per_period)
413                 rand_sleep_msec =  Msec_per_period;
414             else if (Target_sleep_mspc >= 1)
415                 rand_sleep_msec = (Target_sleep_mspc >> 1)
416                                   + (sfs_random() % Target_sleep_mspc);
417             else
418                 rand_sleep_msec = 0;
419
420             if (rand_sleep_msec != 0) {
421                 if (DEBUG_CHILD_TIMING) {
422                     (void) fprintf(stderr, "Child %d sleep for %d msec\n",
423                                         Child_num, rand_sleep_msec);
424                     (void) fflush(stderr);
425                 }
426                 Sleep_msec_this_period += msec_sleep(rand_sleep_msec);
427             }
428         }
429
430         /*
431          * See if it's time to check our progress.
432          * If an operation was just performed, then Cur_time was updated
433          * in the op routine; otherwise we need to get Cur_time.
434          */
435         if (op_count <= 0) {
436             sfs_gettime(&Cur_time);
437         }
438
439         current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000);
440         if (DEBUG_CHILD_XPOINT) {
441                 (void) fprintf(stderr, "cur=%d prev=%d per=%d\n",
442                         current_msec, Previous_chkpnt_msec, Msec_per_period);
443         }
444
445         if ((current_msec - Previous_chkpnt_msec) > Msec_per_period) {
446             check_call_rate();
447         }
448
449     } /* end while more calls to make */
450
451     /*
452      * We are done generating our part of the load.
453      * Store total time in last slot of counts array.
454      *
455      * The last slot has the wall clock time of all the load generation.
456      * Individual slots have the wall clock time spent just for the op
457      * gen routine.
458      */
459     sfs_gettime(&Cur_time);
460     Measurement_in_progress = 0;
461     elapsed_time.sec = Cur_time.sec;
462     elapsed_time.usec = Cur_time.usec;
463     SUBTIME(elapsed_time, Starttime);
464
465     Ops[TOTAL].results.time.sec = elapsed_time.sec;
466     Ops[TOTAL].results.time.usec = elapsed_time.usec;
467
468     if (DEBUG_CHILD_FILES) {
469         (void) fprintf(stderr,
470                     "%s: max fss %d KB  min fss %d KB\n",
471                         sfs_Myname, Most_fss_bytes, Least_fss_bytes);
472         (void) fflush(stderr);
473     }
474
475     if (DEBUG_CHILD_FILES) {
476         (void) fprintf(stderr, "Child %d Files:\n", Child_num);
477         for (i = 0; i < Num_io_files; i++)
478             (void) fprintf(stderr, "Io[%d] use %d xfer %d\n",
479                                 i, Io_files[i].use_cnt, Io_files[i].xfer_cnt);
480         for (i = 0; i < Num_non_io_files; i++)
481             (void) fprintf(stderr, "Non_io[%d] use %d xfer %d\n",
482                                 i, Non_io_files[i].use_cnt,
483                                 Non_io_files[i].xfer_cnt);
484         for (i = 0; i < Num_dir_files; i++)
485             (void) fprintf(stderr, "Dir[%d] use %d xfer %d\n",
486                                 i, Dirs[i].use_cnt, Dirs[i].xfer_cnt);
487         for (i = 0; i < Num_symlink_files; i++)
488             (void) fprintf(stderr, "Sym[%d] use %d xfer %d\n",
489                                 i, Symlinks[i].use_cnt, Symlinks[i].xfer_cnt);
490         (void) fflush(stderr);
491     }
492
493     if (DEBUG_CHILD_SETUP) {
494         int j, group_size, offset, index, tot;
495         for (i = 0; i < Io_working_set.access_group_cnt; i++) {
496             group_size = Io_working_set.access_group_size;
497             if (i < (Num_working_io_files -
498                      ((Num_working_io_files / Io_working_set.access_group_cnt)
499                       * Io_working_set.access_group_cnt)))
500                 group_size += 1;
501             tot = 0;
502             for (j = 0; j < group_size; j++) {
503                 offset = i + (j * Io_working_set.access_group_cnt);
504                 index = Io_working_set.entries[offset].index;
505                 tot += Io_files[index].use_cnt;
506                 (void) fprintf(stderr, "Working[%d] use %d xfer %d\n",
507                                 offset, Io_files[index].use_cnt,
508                                 Io_files[index].xfer_cnt);
509             }
510             (void) fprintf(stderr, "Group %d total use %d\n", i, tot);
511         }
512         (void) fflush(stderr);
513     }
514
515     if (DEBUG_CHILD_GENERAL) {
516         (void) fprintf(stderr, "Child %d Ops:\n", Child_num);
517
518         previous_pcnt = 0.0;
519         (void) fprintf(stderr,
520     "             calls                             reqs\n");
521         (void) fprintf(stderr,
522     "             trgt actl  trgt actl  bad  no     trgt actl   trgt   actl\n");
523         (void) fprintf(stderr,
524     "     name    mix  mix   cnt   cnt  cnt  cnt     mix  mix    cnt    cnt\n");
525
526         for (i = 0; i < NOPS + 1; i++) {
527             (void) fprintf(stderr,
528                     "%11s %4d %4.1f %5d %5d %4d %3d    %4.1f %4.1f %6d %6d\n",
529                     Ops[i].name, Ops[i].mix_pcnt,
530                     (float) (100 * Ops[i].results.good_calls)
531                         / (float) Ops[TOTAL].results.good_calls,
532                     Ops[i].target_calls, Ops[i].results.good_calls,
533                     Ops[i].results.bad_calls, Ops[i].no_calls,
534                     Ops[i].req_pcnt - previous_pcnt,
535                     (float) (100 * Ops[i].req_cnt) / (float) Ops[TOTAL].req_cnt,
536                     Ops[i].target_reqs, Ops[i].req_cnt);
537             previous_pcnt = Ops[i].req_pcnt;
538         }
539         (void) fflush(stderr);
540     }
541
542     if (DEBUG_CHILD_GENERAL) {
543         (void) fprintf(stderr, "Child %d made %d of %d calls in %ld sec\n",
544                         Child_num, Ops[TOTAL].results.good_calls,
545                         Ops[TOTAL].target_calls,
546                         Ops[TOTAL].results.time.sec);
547         (void) fflush(stderr);
548     }
549
550     clnt_destroy(NFS_client);
551     biod_term();
552
553     /* write stats to log file (append mode) */
554     report.version = nfs_version;
555     for (i = 0; i < NOPS + 1; i++) {
556         report.results_buf[i] = Ops[i].results;
557     }
558     report.total_fss_bytes = Total_fss_bytes;
559     report.least_fss_bytes = Least_fss_bytes;
560     report.most_fss_bytes = Most_fss_bytes;
561     report.base_fss_bytes = Base_fss_bytes;
562
563     if (write(Log_fd, (char *) &report, sizeof(report)) == -1) {
564         Saveerrno = errno;
565         (void) fprintf(stderr, "%s: can't write to synchronization file %s ",
566                         sfs_Myname, Logname);
567         errno = Saveerrno;
568         perror(Logname);
569         (void) generic_kill(0, SIGINT);
570         exit(149);
571     }
572     (void) close(Log_fd);
573
574     print_dump(Client_num, Child_num);
575
576 } /* child */
577
578
579 /*
580  * --------------------  Call Target Initialization  --------------------
581  */
582
583 /*
584  * Initialize call and request targets.
585  */
586 static void
587 init_targets(void)
588 {
589     int         call_target;            /* total number of calls to make */
590     int         req_target;             /* total number of reqs to make */
591     int32_t     equal_mix;              /* equal mix of operations */
592     int32_t     slack;                  /* calls leftover after % mix */
593     int         randnum;                /* a random number */
594     int32_t     i;                      /* general use */
595     double      total_req_pcnt;
596     double      previous_pcnt;
597     int         nops_used = 0;
598
599
600     /*
601      * Compute number of target calls for each operation.
602      * These are used to estimate the number of filehandles
603      * that will be used for each type of operation.
604      */
605     call_target = Ops[TOTAL].target_calls;
606     Ops[TOTAL].target_calls = 0;
607
608     for (i = 0; i < NOPS; i++) {
609         Ops[i].target_calls = (Ops[i].mix_pcnt * call_target) / 100;
610         Ops[TOTAL].target_calls += Ops[i].target_calls;
611         if (Ops[i].mix_pcnt != 0)
612             nops_used++;
613     }
614
615     /* Put left over calls into the heavier mix operations. */
616     slack = call_target - Ops[TOTAL].target_calls;
617     equal_mix =  (100 / nops_used) / 2;
618     while (slack > 0) {
619         randnum = sfs_random() % NOPS;
620         if (Ops[randnum].mix_pcnt != 0 && Ops[randnum].mix_pcnt >= equal_mix) {
621             Ops[randnum].target_calls++;
622             Ops[TOTAL].target_calls++;
623             slack--;
624         }
625     }
626
627     /*
628      * compute request targets (based on suggestions from M. Molloy, HP)
629      */
630
631     /* compute total of target requests, based on weighted ops */
632     total_req_pcnt = 0.0;
633     for (i = 0; i < NOPS ; i++) {
634         switch (i) {
635         case READ:
636             total_req_pcnt += ((double) Ops[i].mix_pcnt)
637                             / Io_dist_ptr->avg_ops_per_read_req;
638             break;
639         case WRITE:
640             total_req_pcnt += ((double) Ops[i].mix_pcnt)
641                             / Io_dist_ptr->avg_ops_per_write_req;
642             break;
643         case COMMIT:    /* Commits never generate requests */
644             break;
645         default:
646             total_req_pcnt += (double) Ops[i].mix_pcnt;
647             break;
648         }
649     }
650
651     /*
652      * compute cumulative frequency distribution percentile for each op.
653      * This code assumes that the NULLCALL does not generate multiple
654      * OTW operations per request.
655      */
656     previous_pcnt = 0.0;
657     for (i = 0; i < NOPS; i++) {
658         switch (i) {
659         case READ:
660             Ops[i].req_pcnt = previous_pcnt +
661                               (((100.0 * (double) Ops[i].mix_pcnt)
662                               / Io_dist_ptr->avg_ops_per_read_req)
663                               / total_req_pcnt);
664             break;
665         case WRITE:
666             Ops[i].req_pcnt = previous_pcnt +
667                               (((100.0 * (double) Ops[i].mix_pcnt)
668                               / Io_dist_ptr->avg_ops_per_write_req)
669                               / total_req_pcnt);
670             break;
671         case COMMIT:    /* Commits never generate requests */
672             Ops[i].req_pcnt = previous_pcnt;
673             break;
674         default:
675             Ops[i].req_pcnt = previous_pcnt +
676                               ((100.0 * (double) Ops[i].mix_pcnt)
677                               / total_req_pcnt);
678             break;
679         }
680         previous_pcnt = Ops[i].req_pcnt;
681     }
682     /* force last bucket to 100 */
683     Ops[NOPS-1].req_pcnt = 100;
684
685     /* compute the req load rate */
686     Child_req_load = (total_req_pcnt * Child_call_load) / 100.0;
687
688     /*
689      * Compute number of target reqs for each operation.
690      * These are used for debugging purposes.
691      */
692     req_target = (total_req_pcnt * Ops[TOTAL].target_calls) / 100;
693     Ops[TOTAL].target_reqs = 0;
694
695     previous_pcnt = 0.0;
696     for (i = 0; i < NOPS; i++) {
697         Ops[i].target_reqs = 0;
698         if (Ops[i].mix_pcnt != 0) {
699             Ops[i].target_reqs = ((Ops[i].req_pcnt - previous_pcnt) *
700                                                         req_target) / 100;
701         }
702         Ops[TOTAL].target_reqs += Ops[i].target_reqs;
703         previous_pcnt = Ops[i].req_pcnt;
704     }
705
706     /* Put left over reqs into the heavier mix operations. */
707     slack = req_target - Ops[TOTAL].target_reqs;
708     equal_mix =  (100 / nops_used) / 2;
709     while (slack > 0) {
710         randnum = sfs_random() % NOPS;
711         if (Ops[randnum].target_reqs != 0 &&
712                                         Ops[randnum].req_pcnt >= equal_mix) {
713             Ops[randnum].target_reqs++;
714             Ops[TOTAL].target_reqs++;
715             slack--;
716         }
717     }
718     if (DEBUG_CHILD_GENERAL) {
719         (void) fprintf(stderr,
720                         "      Op\t  Op mix\tCalls\t\t Req mix\t Reqs\t\n");
721         previous_pcnt = 0.0;
722         for (i = 0; i < NOPS; i++) {
723             (void) fprintf(stderr, "%8s\t%8d\t%5d\t\t%8.2f\t%5d\n",
724                                 Ops[i].name,
725                                 Ops[i].mix_pcnt, Ops[i].target_calls,
726                                 Ops[i].req_pcnt - previous_pcnt,
727                                 Ops[i].target_reqs);
728             previous_pcnt = Ops[i].req_pcnt;
729         }
730     }
731 } /* init_targets */
732
733
734 /*
735  * -----------------------  File Set Initialization  -----------------------
736  */
737
738 static file_array_initialized = 0;
739 static int file_size_array[100];
740
741 /*
742  * For a value between 0-99, return a size based on distribution
743  */
744 static int
745 get_file_size(int i)
746 {
747     if (i < 0 || i > 99)
748         return (0);
749
750     if (file_array_initialized == 0) {
751         int j, k;
752
753         for (j = 0, k = 0; j < 100; j++) {
754             if (j >= Default_file_size_dist[k].pcnt &&
755                         Default_file_size_dist[k + 1].size != 0)
756                 k++;
757             file_size_array[j] = Default_file_size_dist[k].size * 1024;
758         }
759         file_array_initialized++;
760     }
761     return (file_size_array[i]);
762 }
763
764 /*
765  * allocate and initialize the various file information structures.
766  */
767 void
768 init_fileinfo(void)
769 {
770     int      i, index;
771     int      j;
772     int      group_size, group_cnt;
773     int      range, previous_range;
774     int      next_value;
775     double   lambda;
776     double   e_to_the_lambda;
777     double   cumulative_ratio;
778     int      num_non_io_to_init;
779     int      io_file_num = 0;
780     int      files_per_generation;
781     sfs_fh_data         *fh_datap;
782
783
784     /*
785      * Zero number of files created used to create unique names
786      */
787     Files_created = 0;
788
789     /*
790      * Dirs - Initialize the files info structure.
791      * Directories must come first, in initializing test dirs we
792      * need to make sure that any files deleted are no full directories
793      */
794     Num_dir_files =
795              Num_dirs +                         /* exist: readdir, rmdir */
796              Ops[MKDIR].target_calls +          /* non-exist: mkdir */
797              Ops[RMDIR].target_calls;           /* empty dir to be removed */
798     if (DEBUG_CHILD_SETUP) {
799         (void) fprintf(stderr, "%s: allocate %d directories\n",
800                            sfs_Myname, Num_dir_files);
801         (void) fflush(stderr);
802     }
803     Dirs = (sfs_fh_type *) calloc(Num_dir_files, sizeof(sfs_fh_type));
804
805     if (Dirs == (sfs_fh_type *) 0) {
806         (void) fprintf(stderr,"%s: init_fileinfo dir calloc %d bytes failed",
807                         sfs_Myname, Num_dir_files * sizeof(sfs_fh_type));
808         (void) generic_kill(0, SIGINT);
809         exit(150);
810     }
811     for (i = 0; i < Num_dir_files; i++) {
812         Dirs[i].working_set = 0;
813         Dirs[i].state = Nonexistent;
814         if (i <= (Num_dirs + Ops[RMDIR].target_calls)) {
815             Dirs[i].initialize = 1;
816             Dirs[i].fh_data = (sfs_fh_data *)0;
817         }
818         Dirs[i].unique_num = i;
819     }
820
821     /* Working Set Directory Files - Initialize the working files array. */
822     Num_working_dirs = Num_dir_files;
823     Dir_working_set.entries = (sfs_work_fh_type *)
824                               calloc(Num_working_dirs,
825                                      sizeof(sfs_work_fh_type));
826     if (Dir_working_set.entries == (sfs_work_fh_type *) 0) {
827         (void) fprintf(stderr,"%s: init_fileinfo wdir calloc %d bytes failed",
828                 sfs_Myname, Num_working_dirs * sizeof(sfs_work_fh_type));
829         (void) generic_kill(0, SIGINT);
830         exit(151);
831     }
832
833     /*
834      * Dirs are accessed uniformly.  See Non_io_files for a description.
835      */
836     if (init_rand_range(Num_dir_files)) {
837         (void) fprintf(stderr, "%s: init_fileinfo dir init_rand_range failed",
838                 sfs_Myname);
839         (void) generic_kill(0, SIGINT);
840         exit(183);
841     }
842
843     for (i = 0; i < Num_working_dirs; i++) {
844         if (Num_working_dirs != Num_dir_files) {
845             /* generate a random subset */
846             index = rand_range(i);
847         } else {
848             /* match the working set one-to-one with the files */
849             index = i;
850         }
851
852         Dirs[index].working_set = 1;
853         Dir_working_set.entries[i].index = index;
854         Dir_working_set.entries[i].range = i + 1;
855     }
856     Dir_working_set.access_group_size = Num_working_dirs;
857     Dir_working_set.access_group_cnt = 1;
858
859     Dir_working_set.max_range = Num_working_dirs;
860
861     if (DEBUG_CHILD_SETUP) {
862         (void) fprintf(stderr, "\nDir size=%d cnt=%d max=%d\n",
863                             Dir_working_set.access_group_size,
864                             Dir_working_set.access_group_cnt,
865                             Dir_working_set.max_range);
866         (void) fflush(stderr);
867     }
868
869
870     /*
871      * I/o Files - Initialize the files info structure to Num_io_files.
872      */
873     if (DEBUG_CHILD_SETUP) {
874         (void) fprintf(stderr, "%s: allocate %d i/o files, %d working\n",
875                            sfs_Myname, Num_io_files, Num_working_io_files);
876         (void) fflush(stderr);
877     }
878
879     Io_files = (sfs_fh_type *) calloc(Num_io_files, sizeof(sfs_fh_type));
880     if (Io_files == (sfs_fh_type *) 0) {
881         (void) fprintf(stderr,"%s: init_fileinfo %d io files calloc %d bytes failed",
882                         sfs_Myname, Num_io_files,
883                         Num_io_files * sizeof(sfs_fh_type));
884         (void) generic_kill(0, SIGINT);
885         exit(152);
886     }
887     io_file_num = 0;
888     for (i = 0; i < Num_io_files; i++) {
889         Io_files[i].working_set = 0;
890         Io_files[i].state = Nonexistent;
891         Io_files[i].initialize = 1;
892         Io_files[i].size = get_file_size(io_file_num % 100);
893         Io_files[i].unique_num = Files_created++;
894         /* Memory allocation for the fh_data will be done later.  */
895         Io_files[i].fh_data = (sfs_fh_data *)0;
896         io_file_num++;
897     }
898
899     /*
900      * Working Set I/o Files - Initialize the working files array.
901      * Only Access_percent of the Io_files are put into the working set.
902      */
903     Io_working_set.entries = (sfs_work_fh_type *)
904                            calloc(Num_working_io_files,
905                                   sizeof(sfs_work_fh_type));
906     if (Io_working_set.entries == (sfs_work_fh_type *) 0) {
907         (void) fprintf(stderr,"%s: init_fileinfo wio calloc %d bytes failed",
908                 sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type));
909         (void) generic_kill(0, SIGINT);
910         exit(153);
911     }
912
913
914     if (DEBUG_CHILD_FILES) {
915         (void) fprintf(stderr, "working_set: ");
916         (void) fflush(stderr);
917     }
918
919     /*
920      * For now, the access distribution is poisson.  See below.
921      */
922 /* #define UNIFORM_ACCESS */
923 #define POISSON_ACCESS
924
925 #ifdef UNIFORM_ACCESS
926     /*
927      * With a uniform access distribution, there is no need for access
928      * groups.
929      * Hopefully SPEC-SFS will agree on a non-uniform access function.
930      * (see below for an example using a poisson distribution).
931      */
932     if (init_rand_range(Num_io_files)) {
933         (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed",
934                 sfs_Myname);
935         (void) generic_kill(0, SIGINT);
936         exit(184);
937     }
938
939     for (i = 0; i < Num_working_io_files; i++) {
940         if (Num_working_io_files != Num_io_files) {
941             /* generate a random subset */
942             index = rand_range(i);
943         } else {
944             /* match the working set one-to-one with the files */
945             index = i;
946         }
947         Io_files[index].working_set = 1;
948         Io_working_set.entries[i].index = index;
949         Io_working_set.entries[i].range = i + 1;
950
951         if (DEBUG_CHILD_FILES) {
952             (void) fprintf(stderr, "%d,", index);
953             (void) fflush(stderr);
954         }
955     }
956     Io_working_set.access_group_size = Num_working_io_files;
957     Io_working_set.access_group_cnt = 1;
958     Io_working_set.max_range = Num_working_io_files;
959
960     if (DEBUG_CHILD_FILES) {
961         (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n",
962                             Io_working_set.access_group_size,
963                             Io_working_set.access_group_cnt,
964                             Io_working_set.max_range);
965         (void) fflush(stderr);
966     }
967
968 #endif /* ! UNIFORM_ACCESS */
969 #ifdef POISSON_ACCESS
970
971     /*
972      * The working set is partitioned into access groups of Access_group_size
973      * files.  Each group is assigned a probability of being accessed.
974      * This is implemented as a cumulative distribution table, with
975      * variable probabilities for each group.  The distribution function
976      * is used to generate a sequence of values, one for each group.
977      * Each group is assigned a 'range' value that is the sum of all
978      * previous range values, plus the next value in the distribution
979      * sequence.  Thus, the probability of choosing any particular group
980      * is equal to the relative height of the distribution curve at the
981      * point represented by that group.
982      * The choice is made by generating a random number in the range
983      * 0 up to (the sum of all values in the distribution sequence - 1),
984      * and finding the group with the greatest range value less than
985      * the random number.
986      * Once a group is chosen, a random number in the range
987      * 1 - Access_group_size is used to pick an entry from within the group.
988      * The entry chosen points to a file in the Io_files array.
989      * If the file at Io_files[index] is eligible for the operation,
990      * then it is accessed, otherwise, the access group is searched
991      * sequentially (mod Access_group_size with wrap-around) until an
992      * eligible file is found.
993      * Access_group_size is derived so that there are enough files
994      * in each group to give a good chance of finding an eligible file
995      * for each operation, but so that there are enough groups (each
996      * representing a point on the distribution curve) to generate a
997      * fairly smooth access distribution curve.
998      */
999
1000     /*
1001      * group_cnt = 8 + ((Num_working_io_files/500) * 4);
1002      *
1003      * The function is chosen to guarentee that each group contains
1004      * at least 1 file, and, beginning with a base of 8 groups, the
1005      * number of groups increases by 4 for each 500 files in the working
1006      * set.  It was arrived at heuristically.  The goal is to put enough
1007      * files into each group to ensure that a file with the right
1008      * attributes can be found once the group is selected (which can be
1009      * difficult for small working sets), while at the same time creating
1010      * enough groups to provide enough points on the distribution curve
1011      * to yield an interesting access distribution.
1012      *
1013      * Since this function is being computed per child, the interesting range
1014      * of working set sizes is computed based on a range of per child load
1015      * values from 1 op/sec to 100 op/sec.  Note that this assumes an
1016      * average server response time of at least 10 msec, which seems to be
1017      * a good minimum value for a wide range of servers given the default
1018      * mix of NFS operations.
1019      * Based on these load values, the total file set, based on the default
1020      * values of 10 MB/op and 38 files/MB, works out to 380 - 38000 files.
1021      * The default working set of 10% of these files yields a working
1022      * set size of 38 - 3800 files.
1023      */
1024
1025      files_per_generation = (_GROUP_DIVISOR * generations) / _FILES_PER_GROUP;
1026      Io_working_set.access_group_cnt = generations +
1027         ((Num_working_io_files/files_per_generation) * generations);
1028     /*
1029      * if the number of files in the working set is not a multiple of
1030      * the group size, then some groups will contain (group_size+1) files.
1031      * Thus, this is the base group size.
1032      */
1033     Io_working_set.access_group_size = Num_working_io_files /
1034                                       Io_working_set.access_group_cnt;
1035
1036     if (init_rand_range(Num_io_files)) {
1037         (void) fprintf(stderr, "%s: init_fileinfo io init_rand_range failed",
1038                 sfs_Myname);
1039         (void) generic_kill(0, SIGINT);
1040         exit(185);
1041     }
1042
1043     /* randomly set up working set of indices into Io_files */
1044     for (i = 0; i < Num_working_io_files; i++) {
1045         if (Num_working_io_files != Num_io_files) {
1046             /* generate a random subset */
1047             index = rand_range(i);
1048         } else {
1049             /* match the working set one-to-one with the files */
1050             index = i;
1051         }
1052         Io_files[index].working_set = 1;
1053         Io_working_set.entries[i].index = index;
1054
1055         if (DEBUG_CHILD_FILES) {
1056             (void) fprintf(stderr, "%d,", index);
1057             (void) fflush(stderr);
1058         }
1059     }
1060
1061     /* initialization for distribution function */
1062     range = 0;
1063     lambda = (double) (generations / 2);
1064     if (lambda <= 0) lambda = 1;
1065     e_to_the_lambda = exp(lambda);
1066     cumulative_ratio = 1.0;
1067
1068     if (DEBUG_CHILD_FILES) {
1069         (void) fprintf(stderr,
1070                         "\ngrp_cnt %d lambda %6.0f e_to_the_lambda %6.2f\n",
1071                         Io_working_set.access_group_cnt, lambda,
1072                         e_to_the_lambda);
1073         (void) fflush(stderr);
1074     }
1075
1076     /* assign a range to each group */
1077     for (i = 0; i < Io_working_set.access_group_cnt; i++) {
1078         /*
1079          * get next value in poisson distribution sequence, using
1080          *    lambda^x / (e^(lambda) * x!) , for x=1,2,3,...,group_cnt
1081          */
1082         double probability;
1083
1084         if( i % generations == 0)
1085         {
1086                 lambda = (double) (generations / 2);
1087                 if (lambda <= 0) lambda = 1;
1088                         e_to_the_lambda = exp(lambda);
1089                 cumulative_ratio = 1.0;
1090         }
1091         probability = cumulative_ratio/e_to_the_lambda;
1092         if (probability <= 0.0 || probability > 1.0) {
1093                 (void) fprintf(stderr, "%s: access probability = %g while setting up Io_working_set, i=%d of %d\n",
1094                         sfs_Myname, probability,
1095                         i, Io_working_set.access_group_cnt);
1096                 (void) generic_kill(0, SIGINT);
1097                 exit(154);
1098         }
1099
1100         /* convert probability to scaled integer */
1101         next_value = (int) (PROB_SCALE * probability);
1102
1103         /* check for negative numbers */
1104         if (next_value <= 0) {
1105                 (void) fprintf(stderr, "%s: next_value = %d while setting up Io_working_set, i=%d of %d\n",
1106                         sfs_Myname, next_value,
1107                         i, Io_working_set.access_group_cnt);
1108                 (void) generic_kill(0, SIGINT);
1109                 exit(154);
1110         }
1111
1112         previous_range = range;
1113         range = previous_range + next_value;
1114         if (range <= previous_range || range < 0) {
1115                 (void) fprintf(stderr, "%s: range = %d previous_range = %d while setting up Io_working_set, i=%d of %d\n",
1116                         sfs_Myname, range, previous_range,
1117                         i, Io_working_set.access_group_cnt);
1118                 (void) generic_kill(0, SIGINT);
1119                 exit(154);
1120         }
1121
1122         /* assign range value to each file in this group */
1123         group_size = Io_working_set.access_group_size;
1124         group_cnt = Io_working_set.access_group_cnt;
1125         if (i < (Num_working_io_files -
1126                  ((Num_working_io_files / group_cnt) * group_cnt)))
1127             group_size += 1;
1128         for (j = 0; j < group_size; j++) {
1129             index = i + (j * Io_working_set.access_group_cnt);
1130             Io_working_set.entries[index].range = range;
1131         }
1132
1133         cumulative_ratio *= lambda / (double) ((i%generations)+1);
1134
1135         if (DEBUG_CHILD_SETUP) {
1136             (void) fprintf(stderr, "group %d next %d range %d\n",
1137                                 i, next_value, range);
1138             (void) fflush(stderr);
1139         }
1140     }
1141     Io_working_set.max_range = range;
1142
1143     if (DEBUG_CHILD_SETUP) {
1144         (void) fprintf(stderr, "\nIo size=%d cnt=%d max=%d\n",
1145                            Io_working_set.access_group_size,
1146                            Io_working_set.access_group_cnt,
1147                            Io_working_set.max_range);
1148         (void) fflush(stderr);
1149     }
1150 #endif /* POISSON_ACCESS */
1151
1152
1153      /* figure out how many files to allocate and initialize */
1154
1155     /* initialize half the non-I/O files */
1156     /* NOTE: initializing half the non-i/o files works ok with the
1157              default op mix.  If the mix is changed affecting the
1158              ratio of creations to removes, there may not be enough
1159              empty slots for file creation (or there may not be
1160              enough created during initialization to handle a lot of
1161              removes that occur early in the test run), and this would
1162              cause do_op() to fail to find a file appropriate for the
1163              chosen op.  This will result in the global variable
1164              Ops[op].no_calls being incremented (turn on child level
1165              debugging to check this count), and the do_op() local
1166              variable aborted_ops to be incremented and checked during
1167              runtime for too many failures.
1168      */
1169     num_non_io_to_init = Num_non_io_files * RATIO_NON_IO_INIT;
1170
1171     if (DEBUG_CHILD_SETUP) {
1172         (void) fprintf(stderr, "%s: allocate %d non-i/o files\n",
1173                             sfs_Myname, Num_non_io_files);
1174         (void) fflush(stderr);
1175     }
1176     Non_io_files = (sfs_fh_type *)
1177                     calloc(Num_non_io_files, sizeof(sfs_fh_type));
1178     if (Non_io_files == (sfs_fh_type *) 0) {
1179         (void) fprintf(stderr,"%s: init_fileinfo nio calloc %d bytes failed",
1180                 sfs_Myname, Num_non_io_files * sizeof(sfs_fh_type));
1181         (void) generic_kill(0, SIGINT);
1182         exit(154);
1183     }
1184     for (i = 0; i < Num_non_io_files; i++) {
1185         Non_io_files[i].working_set = 0;
1186         Non_io_files[i].state = Nonexistent;
1187         if (i <= num_non_io_to_init)
1188             Non_io_files[i].initialize = 1;
1189         Non_io_files[i].size = get_file_size(io_file_num % 100);
1190         Non_io_files[i].unique_num = Files_created++;
1191         /* Allocation of fh_data will happen in init_testdir */
1192         Non_io_files[i].fh_data = (sfs_fh_data *)0;
1193         io_file_num++;
1194     }
1195
1196     /* Working Set Non i/o Files - Initialize the working files array. */
1197     Num_working_non_io_files = Num_non_io_files;
1198     Non_io_working_set.entries = (sfs_work_fh_type *)
1199                                  calloc(Num_working_non_io_files,
1200                                         sizeof(sfs_work_fh_type));
1201     if (Non_io_working_set.entries == (sfs_work_fh_type *) 0) {
1202         (void) fprintf(stderr,"%s: init_fileinfo nwio calloc %d bytes failed",
1203                 sfs_Myname, Num_working_io_files * sizeof(sfs_work_fh_type));
1204         (void) generic_kill(0, SIGINT);
1205         exit(155);
1206     }
1207
1208     /*
1209      * Non_io_files are accessed uniformly.  Each entry has a
1210      * 1/Num_working_non_io_files change of being accessed.
1211      * The choice is made by generating a random number in the range
1212      * 0 through (Num_working_non_io_files - 1) and finding the entry
1213      * with the greatest range value less than the random number.
1214      * If the file at Non_io_files[index] is eligible for the operation,
1215      * it is accessed, otherwise, the access group that the entry belongs
1216      * to is searched sequentially until an eligible file is found.
1217      * For non i/o files, all of the working set files are in the same
1218      * access group (since they access is uniform, this is ok, and
1219      * maximizes the chances of finding an eligible file).
1220      */
1221     if (init_rand_range(Num_non_io_files)) {
1222         (void) fprintf(stderr, "%s: init_fileinfo non_io init_rand_range failed",
1223                 sfs_Myname);
1224         (void) generic_kill(0, SIGINT);
1225         exit(186);
1226     }
1227
1228     for (i = 0; i < Num_working_non_io_files; i++) {
1229         if (Num_working_non_io_files != Num_non_io_files) {
1230             /* generate a random subset */
1231             index = rand_range(i);
1232         } else {
1233             /* match the working set one-to-one with the files */
1234             index = i;
1235         }
1236         Non_io_files[index].working_set = 1;
1237         Non_io_working_set.entries[i].index = index;
1238         Non_io_working_set.entries[i].range = i + 1;
1239     }
1240     Non_io_working_set.access_group_size = Num_working_non_io_files;
1241     Non_io_working_set.access_group_cnt = 1;
1242     Non_io_working_set.max_range = Num_working_non_io_files;
1243
1244     if (DEBUG_CHILD_SETUP) {
1245         (void) fprintf(stderr, "\nNon_io        size=%d cnt=%d max=%d\n",
1246                             Non_io_working_set.access_group_size,
1247                             Non_io_working_set.access_group_cnt,
1248                             Non_io_working_set.max_range);
1249         (void) fflush(stderr);
1250     }
1251
1252
1253     /* Symlinks - Initialize the files info structure. */
1254     Num_symlink_files =
1255              Num_symlinks +                     /* exist: readlink */
1256              Ops[SYMLINK].target_calls;         /* non-exist: symlink */
1257     if (DEBUG_CHILD_SETUP) {
1258         (void) fprintf(stderr, "%s: allocate %d symlinks\n",
1259                            sfs_Myname, Num_symlink_files);
1260         (void) fflush(stderr);
1261     }
1262     Symlinks = (sfs_fh_type *)
1263                 calloc(Num_symlink_files, sizeof(sfs_fh_type));
1264     if (Symlinks == (sfs_fh_type *) 0) {
1265         (void) fprintf(stderr,"%s: init_fileinfo sym calloc %d bytes failed",
1266                         sfs_Myname, (Num_symlink_files * sizeof(sfs_fh_type)));
1267         (void) generic_kill(0, SIGINT);
1268         exit(156);
1269     }
1270     for (i = 0; i < Num_symlink_files; i++) {
1271         Symlinks[i].working_set = 0;
1272         Symlinks[i].state = Nonexistent;
1273         if (i <= Num_symlinks)
1274             Symlinks[i].initialize = 1;
1275         Symlinks[i].fh_data = (sfs_fh_data *)0;
1276         Symlinks[i].unique_num = i;
1277     }
1278
1279     /* Working Set Symlinks - Initialize the working files array. */
1280     /* This appears to cause the following loop to be mostly dead */
1281     /* code.  It is unclear why this line is here. One */
1282     /* possibility is that Num_symlink_files should be */
1283     /* Num_symlinks.  XXX */
1284     Num_working_symlinks = Num_symlink_files;
1285     Symlink_working_set.entries = (sfs_work_fh_type *)
1286                                   calloc(Num_working_symlinks,
1287                                          sizeof(sfs_work_fh_type));
1288     if (Symlink_working_set.entries == (sfs_work_fh_type *) 0) {
1289         (void) fprintf(stderr,"%s: init_fileinfo wsym calloc %d bytes failed",
1290                 sfs_Myname, Num_working_symlinks * sizeof(sfs_work_fh_type));
1291         (void) generic_kill(0, SIGINT);
1292         exit(157);
1293     }
1294
1295     /*
1296      * Symlinks are accessed uniformly.  See Non_io_files for a description.
1297      */
1298     if (init_rand_range(Num_symlink_files)) {
1299         (void) fprintf(stderr, "%s: init_fileinfo sym init_rand_range failed",
1300                 sfs_Myname);
1301         (void) generic_kill(0, SIGINT);
1302         exit(187);
1303     }
1304
1305     for (i = 0; i < Num_working_symlinks; i++) {
1306         if (Num_working_symlinks != Num_symlink_files) {
1307             /* generate a random subset */
1308             index = rand_range(i);
1309         } else {
1310             /* match the working set one-to-one with the files */
1311             index = i;
1312         }
1313
1314         Symlinks[index].working_set = 1;
1315         Symlink_working_set.entries[i].index = index;
1316         Symlink_working_set.entries[i].range = i + 1;
1317     }
1318     Symlink_working_set.access_group_size = Num_working_symlinks;
1319     Symlink_working_set.access_group_cnt = 1;
1320     Symlink_working_set.max_range = Num_working_symlinks;
1321
1322     if (DEBUG_CHILD_SETUP) {
1323         (void) fprintf(stderr, "\nSymlink size=%d cnt=%d max=%d\n",
1324                             Symlink_working_set.access_group_size,
1325                             Symlink_working_set.access_group_cnt,
1326                             Symlink_working_set.max_range);
1327         (void) fflush(stderr);
1328     }
1329
1330     /*
1331      * Free up random number range
1332      */
1333     (void)init_rand_range(0);
1334
1335
1336 } /* init_fileinfo */
1337
1338 /*
1339  * allocate and initialize the directory layout of the files
1340  *
1341  * We can only place files in directories that can't be removed
1342  */
1343 static void
1344 init_dirlayout(void)
1345 {
1346     int i,j;
1347
1348     /*
1349      * Initially create directories only one level deep so all directories
1350      * must be in the parent directory.
1351      */
1352     for (i = 0; i < Num_dir_files; i++) {
1353         Dirs[i].dir = &Export_dir;
1354     }
1355
1356     /*
1357      * Files must only be placed in the first Num_dirs entries leaving
1358      * a set for directory create and remove.
1359      */
1360     j = 0;
1361     for (i = 0; i < Num_io_files; i++) {
1362         if (i != 0 && (i % Files_per_dir) == 0)
1363                 j++;
1364         Io_files[i].dir = &Dirs[j];
1365     }
1366
1367     /*
1368      * All non-io and symlink files are placed in the parent directory
1369      */
1370     for (i = 0; i < Num_non_io_files; i++) {
1371         Non_io_files[i].dir = &Export_dir;
1372     }
1373
1374     for (i = 0; i < Num_symlink_files; i++) {
1375         Symlinks[i].dir = &Export_dir;
1376     }
1377 }
1378
1379 /*
1380  * allocate and initialize client handles
1381  */
1382 static int
1383 init_rpc(void)
1384 {
1385     /*
1386      * Set up the client handles.  We get them all before trying one
1387      * out to insure that the client handle for LOOKUP class is allocated
1388      * before calling op_getattr().
1389      */
1390     if (DEBUG_CHILD_GENERAL) {
1391         (void) fprintf(stderr, "%s: set up client handle\n", sfs_Myname);
1392     }
1393
1394     NFS_client = lad_clnt_create(Tcp? 1: 0, Server_hostent,
1395                                         (uint32_t) NFS_PROGRAM,
1396                                         (uint32_t) nfs_version,
1397                                         RPC_ANYSOCK, &Nfs_timers[0]);
1398                 
1399     if (NFS_client  == ((CLIENT *) NULL)) {
1400         return(-1);
1401     }
1402
1403     /*
1404      * create credentials using the REAL uid
1405      */
1406     NFS_client->cl_auth = authunix_create(lad_hostname, (int)Real_uid,
1407                                       (int)Cur_gid, 0, NULL);
1408
1409     /* Initialize biod simulation mechanism if desired.  */
1410     if (Biod_max_outstanding_reads > 0 || Biod_max_outstanding_writes > 0) {
1411         if (biod_init(Biod_max_outstanding_writes,
1412                 Biod_max_outstanding_reads) == -1) {
1413             return(-1);
1414         }
1415     }
1416
1417     return(0);
1418 } /* init_rpc */
1419
1420 /*
1421  * Initialize the test directory 'parentdir'/testdir'dirnum'.
1422  *
1423  * If the directory already exists, check to see that all of the
1424  * files exist and can be written.  If the directory doesn't exist
1425  * create it and fill it with the proper files.  The caller is
1426  * left with his cwd being the test directory.
1427  *
1428  * Each child pseudo-mount's his own test directory to get its filehandle.
1429  *
1430  * Files, directories, and symlinks all have the same name structure
1431  * but they are strictly ordered, files first, directories next, then symlinks.
1432  * While initializing after a previous run we may have to delete existing
1433  * files of the wrong type and then create them later.
1434  *
1435  * XXX In the future it is probably wiser to have seperate namespaces for
1436  * each type of file.
1437  */
1438 static void
1439 init_testdir(void)
1440 {
1441     int                 filenum;
1442     int                 max_filenum;
1443     int                 init_size;
1444     int                 append_size;
1445     int                 ret;
1446     int                 non = 0;
1447     int                 dealloc;
1448     int                 alloc_count, dealloc_count;
1449     /*
1450      * Create directories first so operations that
1451      * require them will have a file to work with.
1452      */
1453     alloc_count=dealloc_count=0;
1454     for (filenum = 0; filenum < Num_dir_files; filenum++) {
1455         sfs_gettime(&Cur_time);
1456
1457         Cur_file_ptr = &Dirs[filenum];
1458         dealloc=0;
1459         if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
1460         {
1461                 alloc_count++;
1462                 Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
1463                 Cur_file_ptr->attributes2.type = NFNON;
1464                 Cur_file_ptr->attributes3.type = NF3NON;
1465                 if(Cur_file_ptr->working_set == 1)
1466                         dealloc=0;
1467                 else
1468                         dealloc=1;
1469         }
1470
1471         (void) sprintf(Cur_filename, Dirspec, Cur_file_ptr->unique_num);
1472
1473         if (DEBUG_CHILD_SETUP) {
1474             (void) fprintf(stderr, "%s: initialize %s (DIR)\n",
1475                                    sfs_Myname, Cur_filename);
1476             (void) fflush(stderr);
1477         }
1478
1479         if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
1480             /* some error that I don't know what to do with, quit. */
1481             (void) generic_kill(0, SIGINT);
1482             exit(159);
1483         }
1484
1485         if (ret == 0) {
1486             /* file exists */
1487             if (fh_isdir(Cur_file_ptr) && Cur_file_ptr->initialize)
1488             {
1489                 if(dealloc == 1)
1490                 {
1491                         dealloc_count++;
1492                         free(Cur_file_ptr->fh_data);
1493                         Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1494                 }
1495                 continue;
1496             }
1497
1498             if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
1499                 /* some error that I don't know what to do with, quit. */
1500                 (void) generic_kill(0, SIGINT);
1501                 exit(160);
1502             }
1503         }
1504
1505         if (!Cur_file_ptr->initialize) {
1506                 /* dir shouldn't exist */
1507             if(dealloc == 1)
1508             {   
1509                 dealloc_count++;
1510                 free(Cur_file_ptr->fh_data);
1511                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1512             }
1513             continue;
1514         }
1515
1516         /* make the directory */
1517         if (lad_mkdir(Cur_file_ptr, Cur_filename) == -1) {
1518             /* some error that I don't know what to do with, quit. */
1519             (void) generic_kill(0, SIGINT);
1520             exit(161);
1521         }
1522         if(dealloc == 1)
1523         {
1524                 dealloc_count++;
1525                 free(Cur_file_ptr->fh_data);
1526                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1527         }
1528     } /* end for each directory */
1529
1530     /*
1531      * Setup for file i/o operations.
1532      * Verify that we can read and write all the files.
1533      * Make sure we have the attributes && fh for all regular files.
1534      * Create any missing files.
1535      */
1536     max_filenum = Num_io_files + Num_non_io_files;
1537     alloc_count=dealloc_count=0;
1538     for (filenum = 0; filenum < max_filenum; filenum++) {
1539         sfs_gettime(&Cur_time);
1540
1541         if (filenum < Num_io_files) {
1542             Cur_file_ptr = &Io_files[filenum];
1543         } else {
1544             Cur_file_ptr = &Non_io_files[filenum - Num_io_files];
1545             non = 1;
1546         }
1547         (void) sprintf(Cur_filename, Filespec, Cur_file_ptr->unique_num);
1548         dealloc=0;
1549         if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
1550         {
1551                 alloc_count++;
1552                 Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
1553                 Cur_file_ptr->attributes2.type = NFNON;
1554                 Cur_file_ptr->attributes3.type = NF3NON;
1555                 if(Cur_file_ptr->working_set == 1)
1556                         dealloc=0;
1557                 else
1558                         dealloc=1;
1559         }
1560
1561         /*
1562          * Get the size this file should be initialized to, then reset
1563          * so we don't get confused.
1564          */
1565         init_size = Cur_file_ptr->size;
1566         Cur_file_ptr->size = 0;
1567
1568         if (DEBUG_CHILD_SETUP) {
1569             (void) fprintf(stderr, "%s: initialize %s (REG for %sIO)\n",
1570                                    sfs_Myname, Cur_filename,
1571                                    (non ? "non-": ""));
1572             (void) fflush(stderr);
1573         }
1574
1575         if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
1576             /* some error that I don't know what to do with, quit. */
1577             (void) generic_kill(0, SIGINT);
1578             exit(162);
1579         }
1580
1581         if (ret == 0) {
1582             /*
1583              * If file exists and it shouldn't, remove it
1584              */
1585             if (!Cur_file_ptr->initialize) {
1586                 if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
1587                     /* some error that I don't know what to do with, quit. */
1588                     (void) generic_kill(0, SIGINT);
1589                     exit(163);
1590                 }
1591                 if(dealloc == 1)
1592                 {
1593                         dealloc_count++;
1594                         free(Cur_file_ptr->fh_data);
1595                         Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1596                 }
1597                 continue;
1598             }
1599
1600             /*
1601              * file exists: make sure it is
1602              *  - a regular file
1603              *  - accessible (permissions ok)
1604              * if not, remove it (if necessary) and recreate it
1605              * or extend or truncate it to the standard length.
1606              */
1607             if (fh_isfile(Cur_file_ptr) &&
1608                                 check_fh_access(Cur_file_ptr) == 0) {
1609                 goto adjust_size;
1610             }
1611             if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
1612                 /* some error that I don't know what to do with, quit. */
1613                 (void) generic_kill(0, SIGINT);
1614                 exit(164);
1615             }
1616
1617         } /* end if the file exists */
1618
1619         /* the file doesn't exist */
1620         if (!Cur_file_ptr->initialize) {
1621             /* file doesn't exist and it shouldn't */
1622             if(dealloc == 1)
1623             {
1624                 dealloc_count++;
1625                 free(Cur_file_ptr->fh_data);
1626                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1627             }
1628             continue;
1629         }
1630
1631         /* if the file doesn't exist (or was removed), create it */
1632         if (lad_create(Cur_file_ptr, Cur_filename) == -1) {
1633             /* some error that I don't know what to do with, quit. */
1634             (void) generic_kill(0, SIGINT);
1635             exit(165);
1636         }
1637
1638 adjust_size:
1639         /* the non-i/o regular files can be left empty */
1640         if (filenum >= Num_io_files) {
1641             /* Truncate if it has grown */
1642             if (fh_size(Cur_file_ptr) != 0) {
1643                 if (lad_truncate(Cur_file_ptr, 0)) {
1644                     /* some error that I don't know what to do with, quit. */
1645                     (void) generic_kill(0, SIGINT);
1646                     exit(166);    
1647                 }
1648             }
1649             if(dealloc == 1)
1650             {
1651                 dealloc_count++;
1652                 free(Cur_file_ptr->fh_data);
1653                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1654             }
1655             continue;
1656         }     
1657
1658         /* the i/o file must be prefilled, check if file too big */
1659         if (fh_size(Cur_file_ptr) > init_size) {
1660             /* Truncate if it has grown */
1661             if (fh_size(Cur_file_ptr) != 0) {
1662                 if (lad_truncate(Cur_file_ptr, init_size)) {
1663                     /* some error that I don't know what to do with, quit. */
1664                     (void) generic_kill(0, SIGINT);
1665                     exit(167);    
1666                 }
1667             }
1668             if(dealloc == 1)
1669             {
1670                 dealloc_count++;
1671                 free(Cur_file_ptr->fh_data);
1672                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1673             }
1674             continue;
1675         }
1676
1677         /* the i/o file must be prefilled, set up the write arguments. */
1678         if (fh_size(Cur_file_ptr) < init_size) {
1679             append_size = init_size - fh_size(Cur_file_ptr);
1680
1681             if (lad_write(Cur_file_ptr, fh_size(Cur_file_ptr), append_size)) {
1682                 /* some error that I don't know what to do with, quit. */
1683                 (void) generic_kill(0, SIGINT);
1684                 exit(168);
1685             }
1686         }
1687         if(dealloc == 1)
1688         {
1689                 dealloc_count++;
1690                 free(Cur_file_ptr->fh_data);
1691                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1692         }
1693     } /* end for each regular file */
1694
1695     /*
1696      * Create symlinks so operations that
1697      * require them will have a file to work with.
1698      */
1699     alloc_count=dealloc_count=0;
1700     for (filenum = 0; filenum < Num_symlink_files; filenum++) {
1701         char    symlink_target[SFS_MAXNAMLEN];
1702
1703         sfs_gettime(&Cur_time);
1704
1705         Cur_file_ptr = &Symlinks[filenum];
1706         (void) sprintf(Cur_filename, Symspec, Cur_file_ptr->unique_num);
1707
1708         dealloc=0;
1709         if(Cur_file_ptr->fh_data == (sfs_fh_data *)0)
1710         {
1711                 alloc_count++;
1712                 Cur_file_ptr->fh_data = calloc(1,sizeof(sfs_fh_data));
1713                 Cur_file_ptr->attributes2.type = NFNON;
1714                 Cur_file_ptr->attributes3.type = NF3NON;
1715                 if(Cur_file_ptr->working_set == 1)
1716                         dealloc=0;
1717                 else
1718                         dealloc=1;
1719         }
1720         if (DEBUG_CHILD_SETUP) {
1721             (void) fprintf(stderr, "%s: initialize %s (SYMLINK)\n",
1722                                    sfs_Myname, Cur_filename);
1723             (void) fflush(stderr);
1724         }
1725
1726         if ((ret = lad_lookup(Cur_file_ptr, Cur_filename)) == -1) {
1727             /* some error that I don't know what to do with, quit. */
1728             (void) generic_kill(0, SIGINT);
1729             exit(169);
1730         }
1731
1732         if (ret == 0) {
1733             /* file exists */
1734             if (lad_remove(Cur_file_ptr, Cur_filename) != 0) {
1735                 /* some error that I don't know what to do with, quit. */
1736                 (void) generic_kill(0, SIGINT);
1737                 exit(170);
1738             }
1739         }
1740         
1741         /* File doesn't exist */
1742         if (Cur_file_ptr->initialize) {
1743                 /* make the symlink */
1744             (void) sprintf(symlink_target, Filespec, filenum);
1745             if (lad_symlink(Cur_file_ptr, symlink_target, Cur_filename) != 0) {
1746                 /* some error that I don't know what to do with, quit. */
1747                 (void) generic_kill(0, SIGINT);
1748                 exit(171);
1749             }
1750         }
1751         if(dealloc == 1)
1752         {
1753                 dealloc_count++;
1754                 free(Cur_file_ptr->fh_data);
1755                 Cur_file_ptr->fh_data=(sfs_fh_data *)0;
1756         }
1757     } /* end for each symlink */
1758 } /* init_testdir */
1759
1760 /*
1761  * Initialize the test results counters.
1762  */
1763 void
1764 init_counters(void)
1765 {
1766     uint_t i;
1767     uint_t start_msec;
1768
1769     /* Ready to go - initialize operation counters */
1770     for (i = 0; i < NOPS + 1; i++) {
1771         Ops[i].req_cnt = 0;
1772         Ops[i].results.good_calls = 0;
1773         Ops[i].results.bad_calls = 0;
1774         Ops[i].results.fast_calls = 0;
1775         Ops[i].results.time.sec = 0;
1776         Ops[i].results.time.usec = 0;
1777         Ops[i].results.msec2 = 0;
1778     }
1779
1780     /* initialize use count for each file */
1781     for (i = 0; i < Num_io_files; i++) {
1782         Io_files[i].use_cnt = 0;
1783         Io_files[i].xfer_cnt = 0;
1784     }
1785     for (i = 0; i < Num_non_io_files; i++)
1786         Non_io_files[i].use_cnt = 0;
1787     for (i = 0; i < Num_dir_files; i++)
1788         Dirs[i].use_cnt = 0;
1789     for (i = 0; i < Num_symlink_files; i++)
1790         Symlinks[i].use_cnt = 0;
1791
1792     /* initialize timers and period variables */
1793     sfs_gettime(&Starttime);
1794     Cur_time = Starttime;
1795     start_msec = (Starttime.sec * 1000) + (Starttime.usec / 1000);
1796     Previous_chkpnt_msec = start_msec;
1797     Calls_this_period = 0;
1798     Reqs_this_period = 0;
1799     Sleep_msec_this_period = 0;
1800     Calls_this_test = 0;
1801     Reqs_this_test = 0;
1802     Sleep_msec_this_test = 0;
1803 }
1804
1805
1806
1807 /*
1808  * -------------------------  Load Generation  -------------------------
1809  */
1810
1811 /*
1812  * The routines below attempt to do over-the-wire operations.
1813  * Each op tries to cause one or more of a particular
1814  * NFS operation to go over the wire.  Each individual op routine
1815  * returns how many OTW calls were made.
1816  *
1817  * An array of file information is kept for files existing in
1818  * the test directory.  File handles, attributes, names, etc
1819  * are stored in this array.
1820  *
1821  */
1822
1823
1824 #define OP_ABORTED      (-1)
1825 #define OP_BORROWED     (-2)
1826 #define OP_SKIPPED      (-3)
1827 /*
1828  * Randomly perform an operation according to the req mix weightings.
1829  */
1830 static int
1831 do_op(void)
1832 {
1833     double      ratio;
1834     int         op_count;
1835     int         opnum;
1836     int         start_opnum;
1837     static int  failed_ops = 0;
1838     static int  aborted_ops = 0;
1839     static int  borrowed_ops = 0;
1840
1841     if (Testop != -1) {
1842         if (DEBUG_CHILD_OPS) {
1843             (void) fprintf(stderr, "testop start op=%s\n", Ops[Testop].name);
1844         }
1845         op_count = op(Testop);
1846         if (DEBUG_CHILD_OPS) {
1847             (void) fprintf(stderr, "end op=%s\n", Ops[Testop].name);
1848         }
1849         return(op_count);
1850     }
1851
1852     /* get a random number and search the Ops tables for the proper entry */
1853     ratio = sfs_random() % 10000;
1854     for (opnum = 0; Ops[opnum].req_pcnt <= ratio / 100.0 ; opnum++) {
1855             ;
1856     }
1857
1858     /*
1859      * If test targeted a a specific number of ops,
1860      * and the call would put us over the call target for this op,
1861      * search Ops table sequentially for an op that hasn't
1862      * reached its target yet
1863      */
1864     if (!Timed_run) {
1865         start_opnum = opnum;
1866         for (; Ops[opnum].results.good_calls >= Ops[opnum].target_calls;) {
1867             opnum = (opnum + 1) % NOPS;
1868             if (opnum == start_opnum)
1869                 break;
1870         }
1871     }
1872
1873     if (DEBUG_CHILD_RPC) {
1874         (void) fprintf(stderr, "(%d,%d,%d) ",
1875                            Child_num, Ops[TOTAL].results.good_calls, opnum);
1876         (void) fflush(stderr);
1877     }
1878
1879     /* attempt the op */
1880     op_count = op(opnum);
1881
1882     /* count the operations as completed or check for too many errors */
1883     if (op_count > 0) {
1884         Ops[opnum].req_cnt++;
1885         Ops[TOTAL].req_cnt++;
1886     } else if (op_count == 0) {
1887         failed_ops++;
1888         if (DEBUG_CHILD_OPS) {
1889             (void) fprintf(stderr, "Child %d - %d failed %d op\n",
1890                            Child_num, failed_ops, opnum);
1891             (void) fflush(stderr);
1892         }
1893         if ((failed_ops % 50) == 0) {
1894             (void) fprintf(stderr, "Child %d - %d failed ops\n",
1895                            Child_num, failed_ops);
1896             (void) fflush(stderr);
1897         }
1898     } else if (op_count == OP_ABORTED) {
1899         aborted_ops++;
1900         if (DEBUG_CHILD_OPS) {
1901             (void) fprintf(stderr, "Child %d - %d aborted %d op\n",
1902                            Child_num, aborted_ops, opnum);
1903             (void) fflush(stderr);
1904         }
1905         if ((aborted_ops % 50) == 0) {
1906             (void) fprintf(stderr, "Child %d - %d aborted ops\n",
1907                            Child_num, aborted_ops);
1908             (void) fflush(stderr);
1909         }
1910     } else if (op_count == OP_BORROWED) {
1911         borrowed_ops++;
1912         if (DEBUG_CHILD_OPS) {
1913             (void) fprintf(stderr, "Child %d - %d borrowed %d op\n",
1914                            Child_num, borrowed_ops, opnum);
1915             (void) fflush(stderr);
1916         }
1917     } else if (op_count == OP_SKIPPED) {
1918         if (DEBUG_CHILD_OPS) {
1919             (void) fprintf(stderr, "Child %d - skipped %d op\n",
1920                            Child_num, opnum);
1921             (void) fflush(stderr);
1922         }
1923     }
1924
1925     return(op_count);
1926
1927 } /* do_op */
1928
1929
1930 /*
1931  * Because file sizes are variable in length, it is possible that
1932  * a group chosen for a large transfer size may not contain a file
1933  * that large.  Loop calling randfh to try and find another group
1934  * with a large enough file, but only up to IO_LOOP_MAX times.
1935  */
1936 #define IO_LOOP_MAX 5
1937
1938 /*
1939  * Call the RPC operation generator for op 'opnum'.
1940  * The return values of the op generator routines is the count
1941  * of operations performed.  This routine also returns that count.
1942  * A return of 0 means no operation was attempted,
1943  * OP_ABORTED (-1) means that the operation failed.
1944  * OP_BORROWED (-2) means that the operation was borrowed.
1945  * OP_SKIPPED (-3) means that the operation was not done on purpose.
1946  */
1947 static int
1948 op(
1949     int                         opnum)
1950 {
1951     int                         op_count;
1952     int                         trunc_count;
1953     sfs_io_op_dist_type         *dist;          /* io size distribution */
1954     int                         i;
1955     int                         ratio;
1956     int                         buf_size;
1957     int                         frag_size;
1958     int                         xfer_size;
1959     int                         file_size;
1960     int                         trunc_op;
1961     uint_t                      append_flag = 0;
1962     uint_t                      randfh_flags = 0;
1963     char                        *spec;
1964     int                         io_loop = 0;
1965
1966     spec = Filespec;
1967
1968     /* pick a file that make sense for the operation */
1969     switch (opnum) {
1970
1971         case NULLCALL:
1972             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
1973             break;
1974
1975         case GETATTR:
1976             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
1977             break;
1978
1979         case SETATTR:
1980             if (Setattr_borrowed != 0) {
1981                 Setattr_borrowed--;
1982                 return(OP_BORROWED);
1983             }
1984             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
1985             break;
1986
1987         case ROOT:
1988             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
1989             break;
1990
1991         case LOOKUP:
1992             ratio = (int) (sfs_random() % 100);
1993             if (ratio < Num_failed_lookup)
1994                 Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
1995             else
1996                 Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
1997             break;
1998
1999         case READLINK:
2000             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_symlink);
2001             spec = Symspec;
2002             break;
2003
2004         case READ:
2005             /* special handling for i/o operations */
2006             dist = Io_dist_ptr->read;
2007
2008             /* determine number of full buffers and their total size */
2009             ratio = (sfs_random() % 100);
2010             for (i = 0; dist[i].pcnt <= ratio; i++)
2011                 ;
2012             buf_size = dist[i].bufs * Bytes_per_block;
2013
2014             /* determine size of fragment */
2015             /* 1KB - (Kb_per_block - 1) KB fragment */
2016             ratio = sfs_random();
2017             if (Kb_per_block > 1)
2018                 ratio = ratio % (Kb_per_block-1);
2019             else
2020                 ratio = 0;
2021             ratio = (ratio + 1) * 1024;
2022             frag_size = dist[i].frags * ratio;
2023
2024             xfer_size = buf_size + frag_size;
2025
2026             do {
2027                 Cur_file_ptr = randfh(opnum, xfer_size, 0, Exists,
2028                                   Sfs_io_file);
2029             } while (Cur_file_ptr == (sfs_fh_type *) -1 &&
2030                                                 io_loop++ < IO_LOOP_MAX);
2031             break;
2032
2033         case WRCACHE:
2034             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
2035             break;
2036
2037         case WRITE:
2038             /* special handling for i/o operations */
2039             dist = Io_dist_ptr->write;
2040
2041             /* determine number of full buffers and their total size */
2042             ratio = (sfs_random() % 100);
2043             for (i = 0; dist[i].pcnt <= ratio; i++)
2044                 ;
2045             buf_size = dist[i].bufs * Bytes_per_block;
2046
2047             /* determine size of fragment */
2048             /* 1KB - (Kb_per_block - 1) KB fragment */
2049             ratio = sfs_random();
2050             if (Kb_per_block > 1)
2051                 ratio = ratio % (Kb_per_block-1);
2052             else
2053                 ratio = 0;
2054             ratio = (ratio + 1) * 1024;
2055             frag_size = dist[i].frags * ratio;
2056
2057             xfer_size = buf_size + frag_size;
2058
2059             /* decide if it should append or overwrite. */
2060             ratio = (sfs_random() % 100);
2061             if (ratio < Append_percent) {
2062                 append_flag = 1;
2063                 randfh_flags &= RANDFH_APPEND;
2064             }
2065
2066             /* decide if a truncation will be needed */
2067             if (append_flag &&
2068                     ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) {
2069                 randfh_flags &= RANDFH_TRUNC;
2070             }
2071
2072             do {
2073                 Cur_file_ptr = randfh(opnum, xfer_size,
2074                                   randfh_flags,
2075                                   Exists, Sfs_io_file);
2076             } while (Cur_file_ptr == (sfs_fh_type *) -1 &&
2077                                                 io_loop++ < IO_LOOP_MAX);
2078             break;
2079
2080         case CREATE:
2081             if (Create_borrowed != 0) {
2082                 Create_borrowed--;
2083                 return(OP_BORROWED);
2084             }
2085             if ((Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent,
2086                               Sfs_non_io_file)) != (sfs_fh_type *) NULL)
2087                 break;
2088
2089             /* if there are no Nonexistent files, use one that exists */
2090             Cur_file_ptr = randfh(opnum, 0, 0, Exists,
2091                                           Sfs_non_io_file);
2092             /* flag create of existing file for data dump interface */
2093             dump_create_existing_file = TRUE;
2094             break;
2095
2096         case REMOVE:
2097             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
2098             break;
2099
2100         case RENAME:
2101             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
2102             break;
2103
2104         case LINK:
2105             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent,
2106                                   Sfs_non_io_file);
2107             break;
2108
2109         case SYMLINK:
2110             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_symlink);
2111             spec = Symspec;
2112             break;
2113
2114         case MKDIR:
2115             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_dir);
2116             spec = Dirspec;
2117             break;
2118
2119         case RMDIR:
2120             Cur_file_ptr = randfh(opnum, 0, 0, Empty_dir, Sfs_dir);
2121             spec = Dirspec;
2122             break;
2123
2124         case READDIR:
2125             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir);
2126             spec = Dirspec;
2127             break;
2128
2129         case FSSTAT:
2130             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
2131             break;
2132
2133         case ACCESS:
2134             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_io_file);
2135             break;
2136
2137         case COMMIT:
2138             return(OP_SKIPPED);
2139
2140         case FSINFO:
2141             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
2142             break;
2143
2144         case MKNOD:
2145             Cur_file_ptr = randfh(opnum, 0, 0, Nonexistent, Sfs_non_io_file);
2146             break;
2147
2148         case PATHCONF:
2149             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_non_io_file);
2150             break;
2151
2152         case READDIRPLUS:
2153             Cur_file_ptr = randfh(opnum, 0, 0, Exists, Sfs_dir);
2154             spec = Dirspec;
2155             break;
2156
2157         default:
2158             (void) fprintf(stderr, "%s: invalid operation %d\n", sfs_Myname, opnum);
2159             (void) generic_kill(0, SIGINT);
2160             exit(172);
2161     } /* switch on opnum */
2162
2163     if (Cur_file_ptr == (sfs_fh_type *) NULL ||
2164                                 Cur_file_ptr == (sfs_fh_type *) -1) {
2165         Ops[opnum].no_calls++;
2166         return(OP_ABORTED);
2167     }
2168
2169     (void) sprintf(Cur_filename, spec, Cur_file_ptr->unique_num);
2170
2171     /* Call the op routine.  For io operations, maintain file set size info. */
2172     switch (opnum) {
2173
2174         case SETATTR:
2175             op_count = (*Ops[opnum].funct)(-1);
2176             break;
2177
2178         case READ:
2179             op_count = (*Ops[opnum].funct)(xfer_size);
2180             if (op_count > 0)
2181                 Cur_file_ptr->xfer_cnt += (xfer_size + 1023)  / 1024;
2182             else if (DEBUG_CHILD_ERROR) {
2183                 (void) fprintf(stderr, "%s: READ failed\n", sfs_Myname);
2184                 (void) fflush(stderr);
2185             }
2186             break;
2187
2188         case WRITE:
2189             trunc_count = 0;
2190
2191             /* if appending, we may need to truncate the file first */
2192             if (append_flag &&
2193                     ((Cur_fss_bytes + (xfer_size / 1024)) > Limit_fss_bytes)) {
2194
2195                 /* use either SETATTR or CREATE for truncation */
2196                 file_size = fh_size(Cur_file_ptr);
2197                 trunc_op = -1; /* assume there are no ops to borrow */
2198
2199                 if (Ops[SETATTR].mix_pcnt == 0 && Ops[CREATE].mix_pcnt == 0)
2200                     trunc_op = -1;              /* no ops to borrow */
2201
2202                 else if (Ops[SETATTR].mix_pcnt > 0 && Ops[CREATE].mix_pcnt > 0){
2203                     /* only borrow if the target hasn't been met yet */
2204                     if (Ops[SETATTR].results.good_calls
2205                         >= Ops[SETATTR].target_calls) {
2206                         if (Ops[CREATE].results.good_calls
2207                             < Ops[CREATE].target_calls) {
2208                             trunc_op = CREATE;          /* borrow a CREATE */
2209                         }
2210                     } else if (Ops[CREATE].results.good_calls
2211                                >= Ops[CREATE].target_calls) {
2212                         trunc_op = SETATTR;             /* borrow a SETATTR */
2213                     } else {
2214                         /* borrow weighted by mix percentage */
2215                         if ((Ops[SETATTR].mix_pcnt * Create_borrowed) >
2216                             (Ops[CREATE].mix_pcnt * Setattr_borrowed))
2217                             trunc_op =  SETATTR;
2218                         else
2219                             trunc_op =  CREATE;
2220                     }
2221
2222                 } else if (Ops[SETATTR].results.good_calls <
2223                            Ops[SETATTR].target_calls) {
2224                     /* only borrow if the target hasn't been met yet */
2225                     trunc_op = SETATTR;         /* borrow a SETATTR */
2226
2227                 } else if (Ops[CREATE].results.good_calls <
2228                            Ops[CREATE].target_calls) {
2229                     /* only borrow if the target hasn't been met yet */
2230                     trunc_op = CREATE;          /* borrow a CREATE */
2231                 }
2232
2233                 /* perform the truncation and update the file set size */
2234                 if (trunc_op != -1) {
2235                     dump_truncate_op = TRUE;
2236                     if (trunc_op == SETATTR) {
2237                         trunc_count = (*Ops[SETATTR].funct)(0);
2238                         if (trunc_count > 0) {
2239                             Setattr_borrowed++;
2240                             if (DEBUG_CHILD_FILES) {
2241                                 (void) fprintf(stderr, "%s: SETATTR TRUNCATE\n",
2242                                                 sfs_Myname);
2243                                 (void) fflush(stderr);
2244                             }
2245                         }
2246                     } else if (trunc_op == CREATE) {
2247                         trunc_count = (*Ops[CREATE].funct)();
2248                         if (trunc_count > 0) {
2249                             Create_borrowed++;
2250                             if (DEBUG_CHILD_FILES) {
2251                                 (void) fprintf(stderr, "%s: CREATE TRUNCATE\n",
2252                                             sfs_Myname);
2253                                 (void) fflush(stderr);
2254                             }
2255                         }
2256                     }
2257
2258                     Cur_fss_bytes -= (file_size / 1024);
2259                     if (Cur_fss_bytes < Least_fss_bytes)
2260                         Least_fss_bytes = Cur_fss_bytes;
2261                 }
2262             } /* end of if an append is needed */
2263
2264             /*
2265              * do the write request
2266              * specify the stable flag to always be off, it is not used
2267              * with V2 servers.
2268              */
2269             op_count = (*Ops[opnum].funct)(xfer_size, append_flag, 0);
2270             if (op_count > 0)
2271                 Cur_file_ptr->xfer_cnt += (xfer_size + 1023)  / 1024;
2272             else if (DEBUG_CHILD_ERROR) {
2273                 (void) fprintf(stderr, "%s: WRITE failed\n", sfs_Myname);
2274                 (void) fflush(stderr);
2275             }
2276             if (append_flag) {
2277                 Cur_fss_bytes += (xfer_size / 1024);
2278                 if (Cur_fss_bytes > Most_fss_bytes)
2279                     Most_fss_bytes = Cur_fss_bytes;
2280             }
2281             op_count += trunc_count;
2282             break;
2283
2284         default:
2285             op_count = (*Ops[opnum].funct)();
2286             break;
2287
2288     } /* send switch on opnum */
2289
2290     if ((DEBUG_CHILD_ERROR) && (op_count <= 0)) {
2291         (void) fprintf(stderr, "%s: OP %d failed\n", sfs_Myname, opnum);
2292         (void) fflush(stderr);
2293     }
2294
2295     return(op_count);
2296
2297 } /* op */
2298
2299
2300 /*
2301  * Return an entry into the fh array for a file of type 'file_type'
2302  * with existence state 'file_state'.  When 'opnum' specifies an I/O
2303  * operation, the file must be atleast 'xfer_size' bytes long
2304  * (except when 'append_flag' is true).  If 'trunc_flag', spare the
2305  * first file found that is longer than the base file size (to support
2306  * the READ operation).  If only one file is longer than the base file
2307  * size, return the the next longest file.
2308  */
2309 sfs_fh_type *
2310 randfh(
2311     int                         opnum,
2312     int                         xfer_size,
2313     uint_t                      flags,
2314     sfs_state_type              file_state,
2315     sfs_file_type               file_type)
2316 {
2317     sfs_fh_type *       files;          /* file array */
2318     int                 fh;             /* index into file array */
2319     int                 found_fh = -1;  /* index into file array */
2320     uint_t              append_flag = flags & RANDFH_APPEND;
2321     uint_t              trunc_flag = flags & RANDFH_TRUNC;
2322
2323     sfs_work_set_type * work_set;       /* work_set array */
2324     int                 start_file;     /* index into work_set array */
2325     int                 file;           /* index into work_set array */
2326
2327     int                 nworkfiles;     /* # files in work_set */
2328     int                 group_cnt;      /* # file groups in work_set */
2329     int                 group_size;     /* size of each group in work_set */
2330     int                 group;          /* group index with work_set */
2331     int                 offset;         /* file index within group */
2332
2333     int                 value;          /* distribution function value */
2334     int                 previous;       /* binary search for value */
2335     int                 low;            /* binary search for value */
2336     int                 high;           /* binary search for value */
2337
2338     int                 found_file = 0; /* count */
2339     int                 best_delta = 0;
2340     static int          op_num = 0;
2341     long                rand_int;
2342     int                 max_range;
2343
2344     op_num++;
2345
2346     /*
2347      * if the more than one type of file will do, choose one.
2348      * Note: this code assumes specific values and order for
2349      * the entries in sfs_file_enum_type.
2350      */
2351     switch (file_type) {
2352
2353         case Sfs_regular:
2354             file_type = (int) (sfs_random() % 2);
2355             break;
2356
2357         case Sfs_non_dir:
2358             file_type = (int) (sfs_random() % 3);
2359             break;
2360
2361         case Sfs_any_file:
2362             file_type = (int) (sfs_random() % 4);
2363             break;
2364
2365         default:
2366             break;
2367
2368     } /* end switch on file type */
2369
2370     /* get the file type arrays */
2371     switch (file_type) {
2372
2373         case Sfs_io_file:
2374             files = Io_files;
2375             work_set = &Io_working_set;
2376             nworkfiles = Num_working_io_files;
2377             break;
2378
2379         case Sfs_non_io_file:
2380             files = Non_io_files;
2381             work_set = &Non_io_working_set;
2382             nworkfiles = Num_working_non_io_files;
2383             break;
2384
2385         case Sfs_symlink:
2386             files = Symlinks;
2387             work_set = &Symlink_working_set;
2388             nworkfiles = Num_working_symlinks;
2389             break;
2390
2391         case Sfs_dir:
2392             files = Dirs;
2393             work_set = &Dir_working_set;
2394             nworkfiles = Num_working_dirs;
2395             break;
2396
2397         default:
2398             (void) fprintf(stderr, "%s: invalid file type\n", sfs_Myname);
2399             (void) kill(0, SIGINT);
2400             exit(174);
2401     } /* end switch on file type */
2402
2403     /*
2404      * Pick the access group.
2405      *
2406      * Each access group consists of those files in the working set
2407      * (numbered according to the file's index in the array) that
2408      * have the same value modulo the number of groups.  For example,
2409      * a working set of 13 files with 3 groups is organized as
2410      *          group   files
2411      *          -----   -----------------
2412      *            0     0,  3,  6,  9, 12       ie, == 0 mod 3
2413      *            1     1,  4,  7, 10           ie, == 1 mod 3
2414      *            2     2,  5,  8, 11           ie, == 2 mod 3
2415      *
2416      * Generate a random number mod the maximum range value of the working set.
2417      * and then binary search the first group_cnt entries in the working set
2418      * to find the group whose range contains the random number.
2419      * (this implements the file access distribution function)
2420      */
2421     max_range = work_set->max_range;
2422     rand_int = (long) sfs_random();
2423
2424     while ((rand_int / max_range) >= (_M_MODULUS / max_range)) {
2425       /*
2426        * for large values of max_range, modulo doesn't provide a uniform
2427        * distribution unless we exclude these values ...
2428        */
2429       rand_int = (long) sfs_random();
2430     }      
2431     value = rand_int % max_range;
2432
2433     if (DEBUG_CHILD_OPS) {
2434         (void) fprintf(stderr, "randfh: size=%d cnt=%d max=%d val=%3d\n",
2435                             work_set->access_group_size,
2436                             work_set->access_group_cnt,
2437                             work_set->max_range, value);
2438         (void) fflush(stderr);
2439     }
2440
2441     previous = -1;
2442     for (low = 0, high = work_set->access_group_cnt-1, group = (low + high)/2;;
2443          previous = group, group = (low + high)/2) {
2444
2445         if (DEBUG_CHILD_OPS) {
2446             (void) fprintf(stderr,
2447                             "PICK GROUP low=%d hi=%d grp=%d range=%d val=%d\n",
2448                             low, high, group, work_set->entries[group].range,
2449                             value);
2450             (void) fflush(stderr);
2451         }
2452
2453         if (previous == group)
2454             break;
2455         if (work_set->entries[group].range == value)
2456             break;
2457         if (work_set->entries[group].range > value) {
2458             if (group == 0)
2459                 break;
2460             if (work_set->entries[group-1].range < value)
2461                 break;
2462             else
2463                 high = group - 1;
2464         } else if (work_set->entries[group].range < value) {
2465             if (work_set->entries[group+1].range > value) {
2466                 group++;
2467                 break;
2468             } else
2469                 low = group + 1;
2470         }
2471     }
2472
2473     /*
2474      * Pick a file within the group to operate on.
2475      * Since (working_set_size / group_size) may have a remainder,
2476      * groups may have either group_size or (group_size+1) files.
2477      */
2478     group_cnt = work_set->access_group_cnt;
2479     group_size = work_set->access_group_size;
2480     if (group < (nworkfiles - ((nworkfiles / group_cnt) * group_cnt)))
2481         group_size += 1;
2482
2483     if (DEBUG_CHILD_OPS) {
2484         (void) fprintf(stderr, "Selected group = %d\n", group);
2485         (void) fflush(stderr);
2486     }
2487     /*
2488      * Beginning with a random starting point in the group,
2489      * search for a file that is eligible for this operation.
2490      *    index is an index into the files in the group.
2491      *    file and start_file are indices into the working set array.
2492      */
2493     if (DEBUG_CHILD_OPS) {
2494         (void) fprintf(stderr, "group_size = %d\n", group_size);
2495         (void) fflush(stderr);
2496     }
2497
2498     offset = (int) (sfs_random() % group_size);
2499     start_file = group + (offset * group_cnt);
2500     file = start_file;
2501     do {
2502         int f_size;
2503         int delta;
2504
2505         fh = work_set->entries[file].index;
2506
2507         if (DEBUG_CHILD_OPS) {
2508             (void) fprintf(stderr, "PICK FILE op= %d file=%d fh=%d\n",
2509                                 opnum, file, fh);
2510             (void) fprintf(stderr, "fh_state = %d file_state= %d\n",
2511                                 files[fh].state, file_state);
2512             (void) fflush(stderr);
2513         }
2514
2515         /* look for a file that has the right state attribute */
2516         if (files[fh].state == file_state) {
2517             f_size = fh_size(&files[fh]);
2518
2519             /*
2520              * for read and write ops and setattr truncates,
2521              * the file must be large enough to do the xfer or truncate.
2522              */
2523             if ((opnum == READ) || (opnum == WRITE && !append_flag) ||
2524                                                                 trunc_flag) {
2525
2526                 /*
2527                  * If the request is a read and the transfer size is
2528                  * less than or equal to be block size, grab the first
2529                  * file that is less than or equal in size.  Should never
2530                  * see a transfer size less than block size as it will
2531                  * be rounded up for the request.  This allows small files
2532                  * to be read.
2533                  */
2534                 if (opnum == READ && xfer_size <= Bytes_per_block) {
2535                     if (f_size <= Bytes_per_block) {
2536                         found_fh = fh;
2537                         break;
2538                     }
2539                 }
2540 /* #define FIRST_FIT */
2541 #define BEST_FIT
2542 #ifdef FIRST_FIT
2543                 if (f_size >= xfer_size) {
2544                     found_fh = fh;
2545                     break;
2546                 }
2547 #endif
2548 #ifdef BEST_FIT
2549                 if (DEBUG_CHILD_FIT) {
2550                     (void) fprintf(stderr,
2551 "%s: %8d: xfer_size %d f_size %d best_delta %d found %d\n",
2552 sfs_Myname, op_num, xfer_size, f_size, best_delta, found_file);
2553                     (void) fflush(stderr);
2554                 }
2555
2556                 /*
2557                  * If we find an good enough match we should use it.
2558                  * Define good enough to be xfer_size <= X < xfer_size + 8K
2559                  * If not we continue to search for the best fit within
2560                  * a fixed distance 8.
2561                  */
2562                 if (f_size >= xfer_size) {
2563                     if (f_size < (xfer_size + 8 * 1024)) {
2564                         found_fh = fh;
2565                         break;
2566                     }
2567
2568                     found_file++;
2569                     delta = f_size - xfer_size;
2570
2571                     if (found_fh == -1) {
2572                         best_delta = delta;
2573                         found_fh = fh;
2574                         /* break; Removed as per Robinson */
2575                     }
2576
2577                     if (delta < best_delta) {
2578                         found_fh = fh;
2579                         best_delta = delta;
2580                     }
2581
2582                     if (found_file >= 8) {
2583                         break;
2584                     }
2585                 }
2586 #endif
2587             } else {
2588                 /* for non-i/o ops, only requirement is proper file state */
2589                 found_fh = fh;
2590                 break;
2591             }
2592         }
2593         offset = (offset + 1) % group_size;
2594         file = group + (offset * group_cnt);
2595     } while (file != start_file);
2596
2597     if (found_fh == -1) {
2598         /* didn't find a file for this operation */
2599         if (DEBUG_CHILD_FIT) {
2600             if (opnum == READ || (opnum == WRITE && !append_flag) ||
2601                 opnum == SETATTR) {
2602                 (void) fprintf(stderr, "%s: no file for %d byte %d op\n",
2603                                sfs_Myname, xfer_size, opnum);
2604             } else {
2605                 (void) fprintf(stderr, "%s: no file for %d op\n",
2606                                sfs_Myname, opnum);
2607             }
2608             (void) fflush(stderr);
2609             return((sfs_fh_type *) -1);
2610         }
2611         return((sfs_fh_type *) NULL);
2612     }
2613     /* found it */
2614     files[found_fh].use_cnt++;
2615     return(&files[found_fh]);
2616 } /* randfh */
2617
2618 /*
2619  * ------------------------ Miscellaneous Subroutines  -----------------------
2620  */
2621
2622 /*
2623  * check to make sure that we have both read and write permissions
2624  * for this file or directory given in 'statb'.
2625  * return: 0 == ok, -1 == bad
2626  */
2627 int
2628 check_access(
2629     struct stat         *statb)
2630 {
2631     /* check user */
2632     if (statb->st_uid == Real_uid) {
2633         if ((statb->st_mode & 0400) && (statb->st_mode & 0200)) {
2634             return(0);
2635         } else {
2636             return(-1);
2637         }
2638     }
2639
2640     /* check group */
2641     if (statb->st_gid == Cur_gid) {
2642         if ((statb->st_mode & 040) && (statb->st_mode & 020)) {
2643                 return(0);
2644             } else {
2645                 return(-1);
2646             }
2647     }
2648
2649     /* check other */
2650     if ((statb->st_mode & 04) && (statb->st_mode & 02)) {
2651         return(0);
2652     } else {
2653         return(-1);
2654     }
2655
2656 } /* check_access */
2657
2658 /*
2659  * check to make sure that we have both read and write permissions
2660  * for this file or directory given in the file attributes.
2661  * return: 0 == ok, -1 == bad
2662  */
2663 int
2664 check_fh_access(sfs_fh_type *file_ptr)
2665 {
2666     /* check user */
2667     if (fh_uid(file_ptr) == Real_uid) {
2668         if ((fh_mode(file_ptr) & 0400) && (fh_mode(file_ptr) & 0200)) {
2669             return(0);
2670         } else {
2671             return(-1);
2672         }
2673     }
2674
2675     /* check group */
2676     if (fh_gid(file_ptr) == Cur_gid) {
2677         if ((fh_mode(file_ptr) & 040) && (fh_mode(file_ptr) & 020)) {
2678                 return(0);
2679             } else {
2680                 return(-1);
2681             }
2682     }
2683
2684     /* check other */
2685     if ((fh_mode(file_ptr) & 04) && (fh_mode(file_ptr) & 02)) {
2686         return(0);
2687     } else {
2688         return(-1);
2689     }
2690 }
2691
2692 static int last_bad_calls = 0;
2693
2694 /*
2695  * Adjust the sleep time per call based on a number of global variables,
2696  */
2697 static void
2698 check_call_rate()
2699 {
2700     int         call_target_per_period; /* target calls for each period */
2701     int         req_target_per_period;  /* target reqs for each period */
2702     int         call_target_this_test;  /* target calls for test so far */
2703     int         req_target_this_test;   /* target reqs for test so far */
2704     int         msec_this_period;       /* actual length of this period */
2705     int         msec_this_test;         /* actual length of test so far */
2706     uint_t      current_msec;           /* current time in msecs */
2707     int         old_target_sleep_mspc;
2708     struct ladtime elapsed_time;        /* Current_time - Start_time */
2709
2710     int         reqs_needed_next_period;/* req target for the next period */
2711     float       mspc;                   /* target msec per call, with/sleep */
2712     float       work_mspc;              /* actual msec worked / call */
2713
2714
2715     if (Child_num == -1)
2716         /* I'm the parent, ignore the signal */
2717         return;
2718
2719     /* update the test so far totals */
2720     Calls_this_test += Calls_this_period;
2721     Reqs_this_test += Reqs_this_period;
2722     Sleep_msec_this_test += Sleep_msec_this_period;
2723
2724     /* compute per period targets */
2725     call_target_per_period = (int) (Child_call_load *
2726                              ((float) Msec_per_period / (float) 1000));
2727     req_target_per_period = (int) (Child_req_load *
2728                             ((float) Msec_per_period / (float) 1000));
2729
2730     /*
2731      * The child() routine retrieved the Cur_time when deciding to call us.
2732      * Use Cur_time to compute the elapsed time since the last checkpoint
2733      * and the current checkpoint time (ie, elapsed time since test began)
2734      */
2735     /* sfs_gettime(&Cur_time); */
2736     elapsed_time.sec = Cur_time.sec;
2737     elapsed_time.usec = Cur_time.usec;
2738     SUBTIME(elapsed_time, Starttime);
2739
2740     msec_this_test = (elapsed_time.sec * 1000) + (elapsed_time.usec / 1000);
2741     current_msec = (Cur_time.sec * 1000) + (Cur_time.usec / 1000);
2742     msec_this_period = current_msec - Previous_chkpnt_msec;
2743
2744     if (msec_this_test < Sleep_msec_this_test) {
2745         if (DEBUG_CHILD_XPOINT) {
2746             (void) fprintf(stderr,
2747                         "Accum. sleep time %d is msecs ahead of wall clock\n",
2748                                 Sleep_msec_this_test - msec_this_test);
2749             (void) fflush(stderr);
2750         }
2751         Sleep_msec_this_test = msec_this_test;
2752     }
2753
2754     /* compute targets for test so far */
2755     call_target_this_test = (int) ((Child_call_load * (float) msec_this_test)
2756                                    / (float) 1000);
2757     req_target_this_test = (int) ((Child_req_load * (float) msec_this_test)
2758                                    / (float) 1000);
2759
2760     /* save the old sleep rate */
2761     old_target_sleep_mspc = Target_sleep_mspc;
2762
2763     /* Compute how long each request has taken on average. */
2764     if (Reqs_this_test != 0)
2765         work_mspc = ((float) (msec_this_test - Sleep_msec_this_test))
2766                      / (float) Reqs_this_test;
2767     else
2768         work_mspc = (1000.0 / (float) Child_req_load) / 2.0;
2769
2770     /*
2771      * Compute the number of reqs needed in the next period
2772      * in order to just meet the reqstarget for the test when that period ends.
2773      * (Try to make up the entire shortage or overage in the next period.)
2774      * Beware that we might not need to make any reqs next period.
2775      */
2776     reqs_needed_next_period = (req_target_this_test - Reqs_this_test)
2777                               + req_target_per_period;
2778
2779     if (reqs_needed_next_period <= 0) {
2780         /* if no reqs are needed, set the sleep time to the whole period */
2781         mspc = 0.0;
2782         Target_sleep_mspc = Msec_per_period;
2783     } else {
2784         /* decide how much time is available for each request */
2785         mspc = (float) (Msec_per_period) / (float) (reqs_needed_next_period);
2786         Target_sleep_mspc = (int) (mspc - work_mspc);
2787     }
2788
2789     /* Don't increase the target_sleep_mspc by much more than a factor of two,
2790         because doing so can lead to violent oscillations. */
2791     if (Target_sleep_mspc > 2*(old_target_sleep_mspc + 5)) {
2792        Target_sleep_mspc = 2*(old_target_sleep_mspc + 5);
2793     }    
2794
2795     if (Target_sleep_mspc >= Msec_per_period) {
2796         Target_sleep_mspc = Msec_per_period;
2797         if (DEBUG_CHILD_XPOINT) {
2798             (void) fprintf(stderr,
2799     "Child %d: 0 call, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n",
2800                     Child_num, reqs_needed_next_period, mspc, work_mspc,
2801                     msec_this_test, Sleep_msec_this_test, Reqs_this_test);
2802             (void) fflush(stderr);
2803         }
2804         if (Measurement_in_progress) {
2805             (void) fprintf(stderr,
2806                 "Child %d:  0 calls during measurement interval\n",Child_num);
2807             (void) fprintf(stderr,
2808                     "Child %d:  probably unstable, try more processes.\n",Child_num);
2809             (void) generic_kill(0, SIGINT);
2810             (void) fflush(stderr);
2811             exit(188);
2812          }
2813     }
2814     if (Target_sleep_mspc <= 0) {
2815          Target_sleep_mspc = 0;
2816          if (DEBUG_CHILD_XPOINT) {
2817                 (void) fprintf(stderr,
2818     "Child %d: 0 slp, rqnd %d mspc %3.2f wmspc %3.2f time %d slp %d reqs %d\n",
2819                         Child_num, reqs_needed_next_period, mspc, work_mspc,
2820                         msec_this_test, Sleep_msec_this_test, Reqs_this_test);
2821                 (void) fflush(stderr);
2822          }
2823     }
2824
2825     if (DEBUG_CHILD_XPOINT) {
2826         (void) fprintf(stderr, "Child %d\n%s", Child_num,
2827         "        msec_prd  calls_prd reqs_prd calls_tot req_tot    mspc_req\n");
2828         (void) fprintf(stderr, "target: %8d %9d %8d %9d %8d    %6.2f\n",
2829                         Msec_per_period,
2830                         call_target_per_period, req_target_per_period,
2831                         call_target_this_test, req_target_this_test,
2832                         1000.0 / (float) req_target_per_period);
2833         (void) fprintf(stderr, "actual: %8d %9d %8d %9d %8d  ->%6.2f\n",
2834                         msec_this_period,
2835                         Calls_this_period, Reqs_this_period,
2836                         Calls_this_test, Reqs_this_test,
2837                         mspc);
2838         (void) fprintf(stderr,
2839                         "        old_sleep_mspc  %5d   new_sleep_mspc  %5d\n\n",
2840                         old_target_sleep_mspc, Target_sleep_mspc);
2841     }
2842
2843     /*
2844      * check for too many failed RPC calls
2845      * and print a warning if there are too many.
2846      */
2847     if (((Ops[TOTAL].results.bad_calls - last_bad_calls) > 100) ||
2848         ((Ops[TOTAL].results.good_calls > 300) &&
2849          ((Ops[TOTAL].results.bad_calls - last_bad_calls) >
2850                                         Ops[TOTAL].results.good_calls/50))) {
2851         (void) fprintf(stderr,
2852                         "%s: too many failed RPC calls - %d good %d bad\n",
2853                         sfs_Myname, Ops[TOTAL].results.good_calls,
2854                         Ops[TOTAL].results.bad_calls);
2855         last_bad_calls = Ops[TOTAL].results.bad_calls;
2856     }
2857
2858     /* reset the period counters */
2859     Calls_this_period = 0;
2860     Reqs_this_period = 0;
2861     Sleep_msec_this_period = 0;
2862     Previous_chkpnt_msec = current_msec;
2863
2864 } /* check_call_rate */
2865
2866 /* sfs_c_chd.c */