Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / TBBT / trace_play / sfs_c_bio.c
1 #ifndef lint
2 static char sfs_c_bioSid[] = "@(#)sfs_c_bio.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  * ---------------------- sfs_c_bio.c ---------------------
26  *
27  *      Routines that attempt to simulate biod behavior
28  *
29  *      The routines contained here model biod behavior.  Simply call
30  *      biod_init() to replace regular calls to op_read() and op_write()
31  *      with calls to op_biod_read() and op_biod_write().  The variables
32  *      max_out_writes and max_out_reads control the maximum number of
33  *      outstanding writes and reads respectively.
34  *
35  *.Exported Routines
36  *      int     biod_init(int, int);
37  *      void    biod_turn_on(void);
38  *      void    op_biod_write(int, int, int);
39  *      void    op_biod_read(int);
40  *
41  *.Local Routines
42  *      uint32_t        biod_clnt_call(CLIENT *, uint32_t,
43  *                                              xdrproc_t, void *);
44  *      struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
45  *                                              void *, struct timeval *);
46  *      int             biod_poll_wait(CLIENT *, uint32_t);
47  *
48  *.Revision_History
49  *      03-May-94       Robinson
50  *                              History now kept in SCCS
51  *      03-Mar-92       0.1.0 Corbin
52  *                                      Added biod behavior
53  */
54
55 /*
56  * -------------------------  Include Files  -------------------------
57  */
58
59 /*
60  * ANSI C headers
61  */
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <errno.h>
66
67 #include <sys/types.h>
68 #include <sys/stat.h> 
69  
70 #include <unistd.h>
71
72 #include "sfs_c_def.h"
73 #include "rfs_c_def.h"
74
75 /*
76  * Information associated with outstanding read/write requests
77  */
78 #ifndef RFS
79 struct biod_req {
80     uint32_t            xid;            /* RPC transmission ID  */
81     bool_t              in_use;                 /* Indicates if the entry is in use */
82         int     dep_tab_index;                  /* corresponding index in dep_tab */
83     unsigned int        count;          /* Count saved for Dump routines */
84     unsigned int        offset;         /* Offset saved for Dump routines */
85     struct ladtime      start;          /* Time RPC call was made */
86     struct ladtime      stop;           /* Time RPC reply was received */
87     struct ladtime      timeout;        /* Time RPC call will time out */
88 };
89 #endif
90
91 /*
92  * ----------------------  Static Declarations  ----------------------
93  */
94
95 static int      max_out_writes;
96 static int      max_out_reads;
97 int     max_biod_reqs = 0;
98 struct biod_req *biod_reqp;
99
100 /* forward definitions for local functions */
101 extern uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *);
102 static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t,
103                                                 void *, struct timeval *);
104 extern int      biod_poll_wait(CLIENT *, uint32_t);
105
106 static int op_biod_write(int, int, int);
107 static int op_biod_read(int);
108
109 /*
110  * ----------------------  BIOD Support Routines  ----------------------
111  */
112
113 /*
114  * biod_init()
115  *
116  * This function is called during the initialization phase. It performs
117  * the following tasks:
118  *      - Allocate memory to hold outstanding biod request information
119  *
120  * Returns 0 for OK, -1 for failure
121  */
122 int
123 biod_init(
124     int                 out_writes,
125     int                 out_reads)
126 {
127     // RFS max_out_writes = MAXIMUM(1, out_writes);
128     // RFS max_out_reads = MAXIMUM(1, out_reads);
129     // RFS max_biod_reqs = MAXIMUM(out_writes, out_reads);
130         max_biod_reqs = MAX_OUTSTANDING_REQ;    // RFS
131
132     biod_reqp = (struct biod_req *) calloc(max_biod_reqs,
133                                            sizeof (struct biod_req));
134     if (biod_reqp == (struct biod_req *)0) {
135         (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname);
136         (void) fflush(stderr);
137         return (-1);
138     }
139
140     return (0);
141 } /* biod_init */
142
143 #ifndef RFS
144
145 /*
146  *      - Change the operation functions for reads and writes to use the
147  *        biod routines. This step should be done last to allow callers
148  *        to still run with the old op functions if the biod initialization
149  *        fails.
150  */
151 void
152 biod_turn_on(void)
153 {
154     Ops[WRITE].funct = op_biod_write;
155     Ops[READ].funct = op_biod_read;
156 }
157
158 #endif
159
160 /*
161  * biod_term()
162  *
163  * This function is called during the termination phase to free any resources
164  * allocated by the biod_init() routine. It performs the following tasks:
165  *      - Frees memory associated with outstanding biod request information
166  *      - Frees the biod client handle
167  */
168 void
169 biod_term(void)
170 {
171     if (max_biod_reqs) {
172         free(biod_reqp);
173     }
174 } /* biod_term */
175
176 #ifndef RFS
177 /*
178  * Perform and RPC biod style write operation of length 'xfer_size'.
179  * If 'append_flag' is true, then write the data to the end of the file.
180  */
181 static int
182 op_biod_write(
183     int                 xfer_size,
184     int                 append_flag,
185     int                 stab_flag)
186 {
187     sfs_op_type         *op_ptr;        /* per operation info */
188     static char         *buf = NULL;    /* the data buffer */
189     unsigned int        size;           /* size of data write */
190     int                 max_cnt;
191     attrstat            reply2;         /* the reply */
192     writeargs           args2;
193     WRITE3res           reply3;         /* the reply */
194     WRITE3args          args3;
195     struct ladtime      curr_time;
196     struct ladtime      tmp_time;
197     struct ladtime      call_timeout;
198     struct biod_req     *reqp;
199     int                 ret;            /* ret val == call success */
200     int                 num_out_reqs;   /* # of outstanding writes */
201     int                 i;
202     int                 error;
203     int32_t             offset;
204     static int          calls = 0;
205
206     calls++;
207
208     if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
209         return (0);
210
211     /*
212      * Initialize write buffer to known value
213      */  
214     if (buf == NULL) {
215         buf = init_write_buffer();
216     }
217
218
219     /*
220      * For now we treat DATA_SYNC to be the same as FILE_SYNC.
221      * If it is not a V3 op then it must always be stable
222      */  
223     if (stab_flag == DATA_SYNC || nfs_version != NFS_V3)
224         stab_flag = FILE_SYNC;
225
226     op_ptr = &Ops[WRITE];
227     ret = 0;
228
229     /* set up the arguments */
230     (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
231                         NFS_FHSIZE);
232     (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
233                         sizeof (nfs_fh3));
234
235     args2.beginoffset = 0;      /* unused */
236
237     if (append_flag == 1) {
238         args2.offset = Cur_file_ptr->attributes2.size;
239         args3.offset = Cur_file_ptr->attributes3.size;
240     } else {
241         if (fh_size(Cur_file_ptr) > xfer_size) {
242             offset = Bytes_per_block * (sfs_random() %
243                             (((fh_size(Cur_file_ptr) - xfer_size)
244                             / Bytes_per_block) + 1));
245             args2.offset = offset;
246             args3.offset._p._u = 0;
247             args3.offset._p._l = offset;
248         } else {
249             args2.offset = 0;
250             args3.offset._p._u = args3.offset._p._l = 0;
251         }
252     }
253
254     size = Bytes_per_block;
255     args2.totalcount = size;    /* unused */
256     args2.data.data_len = size;
257     args2.data.data_val = buf;
258     args3.data.data_len = size;
259     args3.data.data_val = buf;
260     args3.count = size;
261     args3.stable = stab_flag;
262
263     /* Calculate the number of NFS writes required */
264     max_cnt = xfer_size / Bytes_per_block;
265     if ((xfer_size % Bytes_per_block) != 0) {
266         max_cnt++;
267     }
268
269     /* check our stats to see if this would overflow */
270     if (!Timed_run) {
271         if (op_ptr->target_calls > 0 &&
272            (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
273             max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
274         }
275     }
276
277     if (DEBUG_CHILD_OPS) {
278         (void) fprintf(stderr, "write: %d buffers xfer_size %d\n",
279                           max_cnt, xfer_size);
280         (void) fflush(stderr);
281     }
282
283     /* Mark all request slots as not in use */
284     for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
285         reqp->in_use = FALSE;
286     }
287
288     if (Current_test_phase < Warmup_phase) {
289         call_timeout.sec = Nfs_timers[Init].tv_sec;
290         call_timeout.usec = Nfs_timers[Init].tv_usec;
291     } else {
292         call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
293         call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
294     }
295
296     /* capture length for possible dump */
297     Dump_length = fh_size(Cur_file_ptr);
298  
299     /* make the call(s) now */
300     num_out_reqs = 0;
301     while (xfer_size > 0 || num_out_reqs > 0) {
302         /*
303          * Send out calls async until either the maximum number of outstanding
304          * requests has been reached or there are no more requests to make.
305          */
306         while (num_out_reqs < max_out_writes && xfer_size > 0) {
307
308             /* find an empty write request slot */
309             for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
310                 if (reqp->in_use == FALSE) {
311                     break;
312                 }
313             }
314
315             if (xfer_size < size) {
316                 size = xfer_size;
317                 args2.data.data_len = xfer_size;
318                 args2.totalcount = xfer_size;           /* unused */
319                 args3.data.data_len = xfer_size;
320                 args3.count = xfer_size;
321             }
322             xfer_size -= size;
323
324             sfs_gettime(&reqp->start);
325             if (nfs_version == NFS_V3) {
326                     reqp->xid = biod_clnt_call(NFS_client,
327                                         (uint32_t)NFSPROC3_WRITE,
328                                         xdr_WRITE3args, (char *) &args3);
329                     if (reqp->xid != 0) {
330                         /* capture count and offset for possible dump */
331                         reqp->count = args3.data.data_len;
332                         reqp->offset = args3.offset._p._l;
333                         reqp->timeout = reqp->start;
334                         ADDTIME(reqp->timeout, call_timeout);
335                         reqp->in_use = TRUE;
336                         num_out_reqs++;
337                     }
338             }
339             if (nfs_version == NFS_VERSION) {
340                     reqp->xid = biod_clnt_call(NFS_client,
341                                         (uint32_t)NFSPROC_WRITE,
342                                         xdr_write, (char *) &args2);
343                     if (reqp->xid != 0) {
344                         /* capture count and offset for possible dump */
345                         reqp->count = args2.data.data_len;
346                         reqp->offset = args2.offset;
347                         reqp->timeout = reqp->start;
348                         ADDTIME(reqp->timeout, call_timeout);
349                         reqp->in_use = TRUE;
350                         num_out_reqs++;
351                     }
352             }
353             if (DEBUG_CHILD_BIOD) {
354                 (void) fprintf (stderr,
355 "[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n",
356                                 calls, reqp->xid,
357                                 reqp->start.sec, reqp->start.usec,
358                                 reqp->timeout.sec, reqp->timeout.usec);
359             }
360
361             args2.offset += size;
362             args3.offset._p._l += size;
363             if (biod_poll_wait(NFS_client, 0) > 0) {
364                 break;
365             }
366         } /* while can make an async call */
367
368         /*
369          * Process replies while there is data on the socket buffer.
370          * Just do polls on the select, no sleeping occurs in this loop.
371          */
372         do {
373             error = biod_poll_wait(NFS_client, 0);
374             switch (error) {
375                 case -1:
376                     if (errno == EINTR) {
377                         error = 1;
378                         continue;
379                     }
380                     if (DEBUG_CHILD_BIOD) {
381                         (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n",
382                                            sfs_Myname, calls);
383                         (void) fflush(stderr);
384                     }
385                     break;
386
387                 case 0:
388                     break;
389
390
391                 default:
392                     if (nfs_version == NFS_VERSION)
393                             reqp = biod_get_reply(NFS_client, xdr_write,
394                                           (char *) &reply2,
395                                           &Nfs_timers[op_ptr->call_class]);
396                     if (nfs_version == NFS_V3)
397                             reqp = biod_get_reply(NFS_client, xdr_WRITE3res,
398                                           (char *) &reply3,
399                                           &Nfs_timers[op_ptr->call_class]);
400
401                     /*
402                      * If biod_get_reply returns NULL then we got an RPC
403                      * level error, probably a dropped fragment or the
404                      * remains of a previous partial request.
405                      */
406                     if (reqp == (struct biod_req *)NULL) {
407                         error = 0;
408                         break;
409                     }
410
411                     /*
412                      * We have a valid response, check if procedure completed
413                      * correctly.
414                      */
415                     if ((nfs_version == NFS_VERSION &&
416                          reply2.status == NFS_OK) ||
417                          (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) {
418                         Cur_file_ptr->state = Exists;
419                         /*
420                          * In updating attributes we may get replies out
421                          * of order.  We blindly update the attributes
422                          * which may cause old attributes to be stored.
423                          * XXX We should check for old attributes.
424                          */
425                         if (nfs_version == NFS_VERSION)
426                             Cur_file_ptr->attributes2 =
427                                         reply2.attrstat_u.attributes;
428                         if (nfs_version == NFS_V3)
429                             Cur_file_ptr->attributes3 =
430                                         reply3.res_u.ok.file_wcc.after.attr;
431                         if (DEBUG_CHILD_RPC) {
432                             (void) fprintf(stderr,
433                                         "%s: WRITE %s %d bytes offset %d \n",
434                                         sfs_Myname, Cur_filename,
435                                         reqp->count, reqp->offset);
436                             (void) fflush(stderr);
437                         }
438
439                         /* capture count and offset for possible dump */
440                         Dump_count = reqp->count;
441                         Dump_offset = reqp->offset;
442                         sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
443                         op_ptr->results.good_calls++;
444                         Ops[TOTAL].results.good_calls++;
445                         ret++;
446                         reqp->in_use = FALSE;
447                         num_out_reqs--;
448                         if (DEBUG_CHILD_BIOD) {
449                             (void) fprintf (stderr,
450 "[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n",
451                                 calls, reqp->xid,
452                                 reqp->start.sec, reqp->start.usec,
453                                 reqp->timeout.sec, reqp->timeout.usec,
454                                 reqp->stop.sec, reqp->stop.usec);
455                         }
456                     } else {
457                         op_ptr->results.bad_calls++;
458                         Ops[TOTAL].results.bad_calls++;
459                         reqp->in_use = FALSE;
460                         num_out_reqs--;
461                         if (DEBUG_CHILD_BIOD) {
462                             (void) fprintf (stderr,
463 "[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n",
464                                 calls, reqp->xid,
465                                 reqp->start.sec, reqp->start.usec,
466                                 reqp->timeout.sec, reqp->timeout.usec);
467
468                             (void) fprintf(stderr,
469                                     "[%d]:BIOD WRITE FAILED: xid %x",
470                                     calls, reqp->xid);
471
472                             if (nfs_version == NFS_VERSION)
473                                 (void) fprintf(stderr, "  status %d",
474                                                                 reply2.status);
475                             if (nfs_version == NFS_V3)
476                                 (void) fprintf(stderr, "  status %d",
477                                                                 reply3.status);
478                             (void) fprintf(stderr, "\n");
479                         }
480                     }
481                     break;
482             }
483         } while (error > 0 && num_out_reqs > 0);
484
485         /* Scan for replies that have timed out */
486         if (num_out_reqs > 0) {
487             sfs_gettime(&curr_time);
488             for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
489                 if (reqp->in_use == FALSE) {
490                     continue;
491                 }
492                 if (reqp->timeout.sec < curr_time.sec ||
493                     (reqp->timeout.sec == curr_time.sec &&
494                     reqp->timeout.usec < curr_time.usec)) {
495
496                     op_ptr->results.bad_calls++;
497                     Ops[TOTAL].results.bad_calls++;
498                     reqp->in_use = FALSE;
499                     num_out_reqs--;
500                     if (DEBUG_CHILD_BIOD) {
501                         (void) fprintf (stderr,
502 "[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n",
503                                 calls, reqp->xid,
504                                 reqp->start.sec, reqp->start.usec,
505                                 reqp->timeout.sec, reqp->timeout.usec,
506                                 curr_time.sec, curr_time.usec);
507                         if (biod_poll_wait(NFS_client, 0) > 0) {
508                            (void) fprintf(stderr,
509                                 "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls);
510                         }
511                     }
512                 }
513             }
514         }
515
516         /*
517          * We go to sleep waiting for a reply if all the requests have
518          * been sent and there are outstanding requests, or we cannot
519          * send any more requests.
520          */
521         if ((xfer_size <= 0 && num_out_reqs > 0) ||
522             num_out_reqs == max_out_writes) {
523             /*
524              * Find the next outstanding request that will timeout
525              * and take a time differential to use for the poll timeout.
526              * If the differential is less than zero, then we go to the
527              * top of the loop. Note that we are not picky on errors
528              * returned by select, after the sleep we return to the top
529              * of the loop so extensive error/status checking is not
530              * needed.
531              */
532             tmp_time.sec = 0;
533             tmp_time.usec = 0;
534             for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) {
535                 if (reqp->in_use == FALSE) {
536                     continue;
537                 }
538                 if (tmp_time.sec == 0 ||
539                     (reqp->timeout.sec < tmp_time.sec ||
540                      (reqp->timeout.sec == tmp_time.sec &&
541                       reqp->timeout.usec < tmp_time.usec))) {
542                     
543                      tmp_time = reqp->timeout;
544                 }
545             }
546             if (tmp_time.sec == 0 && tmp_time.usec == 0)
547                 continue;
548             sfs_gettime(&curr_time);
549             SUBTIME(tmp_time, curr_time);
550             (void) biod_poll_wait(NFS_client,
551                                         tmp_time.sec * 1000000 + tmp_time.usec);
552         }
553     } /* while not done */
554
555  
556     /*
557      * If we have not gotten an error and we were asked for an async write
558      * send a commit operation.
559      */  
560     if (ret && stab_flag != FILE_SYNC)
561         ret += (*Ops[COMMIT].funct)();
562
563     return (ret);
564
565 } /* op_biod_write */
566
567
568 /*
569  * perform an RPC read operation of length 'xfer_size'
570  */
571 static int
572 op_biod_read(
573     int                         xfer_size)
574 {
575     sfs_op_type                 *op_ptr;        /* per operation info */
576     int                         max_cnt;        /* packet ctrs */
577     char                        buf[DEFAULT_MAX_BUFSIZE];/* data buffer */
578     readargs                    args2;
579     readres                     reply2;         /* the reply */
580     READ3args                   args3;
581     READ3res                    reply3;         /* the reply */
582     int                         size;
583     struct ladtime              curr_time;
584     struct ladtime              call_timeout;
585     struct ladtime              tmp_time;
586     struct biod_req             *reqp;
587     int                         ret;            /* ret val == call success */
588     int                         num_out_reqs;   /* # of outstanding writes */
589     int                         i;
590     int                         error;
591     int32_t                     offset;
592     static int          calls = 0;
593
594     calls++;
595
596     if (nfs_version != NFS_VERSION && nfs_version != NFS_V3)
597         return (0);
598
599     op_ptr = &Ops[READ];
600     ret = 0;
601
602     /* set up the arguments */
603     (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2,
604                         NFS_FHSIZE);
605     (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3,
606                         sizeof (nfs_fh3));
607
608     /*
609      * Don't allow a read of less than one block size
610      */
611     if (xfer_size < Bytes_per_block)
612         xfer_size = Bytes_per_block;
613  
614
615     /* Calculate the number of NFS reads required */
616     max_cnt = xfer_size / Bytes_per_block;
617     if ((xfer_size % Bytes_per_block) != 0) {
618             max_cnt++;
619     }
620
621     /* check our stats to see if this would overflow */
622     if (!Timed_run) {
623         if (op_ptr->target_calls > 0 &&
624             (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) {
625             max_cnt = op_ptr->target_calls - op_ptr->results.good_calls;
626         }
627     }
628
629     args2.offset = 0;
630     args3.offset._p._l = args3.offset._p._u = 0;
631
632     /*
633      * randomly choose an offset that is a multiple of the block size
634      * and constrained by making the transfer fit within the file
635      */
636     if (fh_size(Cur_file_ptr) > xfer_size) {
637         offset = Bytes_per_block * (sfs_random() %
638                             (((fh_size(Cur_file_ptr) - xfer_size)
639                             / Bytes_per_block) + 1));
640         args2.offset = offset;
641         args3.offset._p._u = 0;
642         args3.offset._p._l = offset;
643     }
644
645     size = Bytes_per_block;
646     args2.count = size;
647     args3.count = size;
648     args2.totalcount = size;    /* unused */
649
650     /* Have lower layers fill in the data directly.  */
651     reply2.readres_u.reply.data.data_val = buf;
652     reply3.res_u.ok.data.data_val = buf;
653
654     if (DEBUG_CHILD_OPS) {
655         (void) fprintf(stderr, "read: %d buffers xfer_size %d\n",
656                           max_cnt, xfer_size);
657         (void) fflush(stderr);
658     }
659
660     /* Mark all request slots as not in use */
661     for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) {
662         reqp->in_use = FALSE;
663     }
664
665     if (Current_test_phase < Warmup_phase) {
666         call_timeout.sec = Nfs_timers[Init].tv_sec;
667         call_timeout.usec = Nfs_timers[Init].tv_usec;
668     } else {
669         call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec;
670         call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec;
671     }
672
673     /* capture length for possible dump */
674     Dump_length = fh_size(Cur_file_ptr);
675
676     /* make the call(s) now */
677     num_out_reqs = 0;
678     while (xfer_size > 0 || num_out_reqs > 0) {
679         /*
680          * Send out calls async until either the maximum number of outstanding
681          * requests has been reached or there are no more requests to make.
682          */
683         while (num_out_reqs < max_out_reads && xfer_size > 0) {
684
685             /* find an empty read request slot */
686             for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
687                 if (reqp->in_use == FALSE) {
688                     break;
689                 }
690             }
691
692             if (xfer_size < size) {
693                 size = xfer_size;
694                 args2.count = xfer_size;
695                 args3.count = xfer_size;
696                 args2.totalcount = xfer_size;           /* unused */
697             }
698             xfer_size -= size;
699
700             sfs_gettime(&reqp->start);
701             if (nfs_version == NFS_VERSION) {
702                 reqp->xid = biod_clnt_call(NFS_client,
703                                         (uint32_t)NFSPROC_READ,
704                                         xdr_read, (char *) &args2);
705                 if (reqp->xid != 0) {
706                     /* capture count and offset for possible dump */
707                     reqp->count = args2.count;
708                     reqp->offset = args2.offset;
709                     reqp->timeout = reqp->start;
710                     ADDTIME(reqp->timeout, call_timeout);
711                     reqp->in_use = TRUE;
712                     num_out_reqs++;
713                 }
714             } else if (nfs_version == NFS_V3) {
715                 reqp->xid = biod_clnt_call(NFS_client,
716                                         (uint32_t)NFSPROC3_READ,
717                                         xdr_READ3args, (char *) &args3);
718                 if (reqp->xid != 0) {
719                     /* capture count and offset for possible dump */
720                     reqp->count = args3.count;
721                     reqp->offset = args3.offset._p._l;
722                     reqp->timeout = reqp->start;
723                     ADDTIME(reqp->timeout, call_timeout);
724                     reqp->in_use = TRUE;
725                     num_out_reqs++;
726                 }
727             }
728
729             args2.offset += size;
730             args3.offset._p._l += size;
731             if (biod_poll_wait(NFS_client, 0) > 0) {
732                 break;
733             }
734         } /* while can make an async call */
735
736         /*
737          * Process replies while there is data on the socket buffer.
738          * Just do polls on the select, no sleeping occurs in this loop.
739          */
740         do {
741             error = biod_poll_wait(NFS_client, 0);
742             switch (error) {
743                 case -1:
744                     if (errno == EINTR) {
745                         error = 1;
746                         continue;
747                     }
748                     if (DEBUG_CHILD_BIOD) {
749                         (void) fprintf(stderr,
750                                         "%s:[%d]: biod_poll_wait error\n",
751                                         sfs_Myname, calls);
752                         (void) fflush(stderr);
753                     }
754                     break;
755
756                 case 0:
757                     break;
758
759
760                 default:
761                     if (nfs_version == NFS_VERSION)
762                         reqp = biod_get_reply(NFS_client, xdr_read,
763                                           (char *) &reply2,
764                                           &Nfs_timers[op_ptr->call_class]);
765                     if (nfs_version == NFS_V3)
766                         reqp = biod_get_reply(NFS_client, xdr_READ3res,
767                                           (char *) &reply3,
768                                           &Nfs_timers[op_ptr->call_class]);
769
770                     /*
771                      * If biod_get_reply returns NULL then we got an RPC
772                      * level error, probably a dropped fragment or the
773                      * remains of a previous partial request.
774                      */
775                     if (reqp == (struct biod_req *)NULL) {
776                         error = 0;
777                         break;
778                     }
779
780                     /*
781                      * We have a valid response, check if procedure completed
782                      * correctly.
783                      */
784                     if ((nfs_version == NFS_VERSION &&
785                                                 reply2.status == NFS_OK) ||
786                          (nfs_version == NFS_V3 &&
787                                                 reply3.status == NFS3_OK)) {
788                         Cur_file_ptr->state = Exists;
789                         if (DEBUG_CHILD_RPC) {
790                             (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n",
791                                    sfs_Myname, Cur_filename, reqp->count, reqp->offset);
792                             (void) fflush(stderr);
793                         }
794                         /*
795                          * In updating attributes we may get replies out
796                          * of order.  We blindly update the attributes
797                          * which may cause old attributes to be stored.
798                          * XXX We should check for old attributes.
799                          */
800                         if (nfs_version == NFS_VERSION) {
801                             Cur_file_ptr->attributes2 =
802                                         reply2.readres_u.reply.attributes;
803                             /* capture count and offset for possible dump */
804                             Dump_count = reply2.readres_u.reply.data.data_len;
805                         }
806                         if (nfs_version == NFS_V3) {
807                             Cur_file_ptr->attributes3 =
808                                         reply3.res_u.ok.file_attributes.attr;
809                             /* capture count and offset for possible dump */
810                             Dump_count = reply3.res_u.ok.data.data_len;
811                         }
812
813                         Dump_offset = reqp->offset;
814                         sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop);
815                         op_ptr->results.good_calls++;
816                         Ops[TOTAL].results.good_calls++;
817                         ret++;
818                         reqp->in_use = FALSE;
819                         num_out_reqs--;
820                     } else {
821                         op_ptr->results.bad_calls++;
822                         Ops[TOTAL].results.bad_calls++;
823                         reqp->in_use = FALSE;
824                         num_out_reqs--;
825
826                         if (DEBUG_CHILD_BIOD) {
827                             (void) fprintf(stderr,
828                                     "[%d]:BIOD READ FAILED: xid %x",
829                                     calls, reqp->xid);
830
831                             if (nfs_version == NFS_VERSION)
832                                 (void) fprintf(stderr, "  status %d",
833                                                                 reply2.status);
834                             if (nfs_version == NFS_V3)
835                                 (void) fprintf(stderr, "  status %d",
836                                                                 reply3.status);
837                             (void) fprintf(stderr, "\n");
838                         }
839                     }
840                     break;
841             } /* switch */
842         } while (error > 0 && num_out_reqs > 0);
843
844         /* Scan for replies that have timed out */
845         if (num_out_reqs > 0) {
846             sfs_gettime(&curr_time);
847             for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
848                 if (reqp->in_use == FALSE) {
849                     continue;
850                 }
851                 if (reqp->timeout.sec < curr_time.sec ||
852                     (reqp->timeout.sec == curr_time.sec &&
853                      reqp->timeout.usec < curr_time.usec)) {
854
855                     op_ptr->results.bad_calls++;
856                     Ops[TOTAL].results.bad_calls++;
857                     reqp->in_use = FALSE;
858                     num_out_reqs--;
859                     if (DEBUG_CHILD_BIOD) {
860                         (void) fprintf (stderr,
861 "[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n",
862                                 calls, reqp->xid,
863                                 reqp->timeout.sec, reqp->timeout.usec,
864                                 curr_time.sec, curr_time.usec);
865                         if (biod_poll_wait(NFS_client, 0) > 0) {
866                            (void) fprintf(stderr,
867                                 "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls);
868                         }
869                     }
870                 }
871             }
872         }
873
874         /*
875          * We go to sleep waiting for a reply if all the requests have
876          * been sent and there are outstanding requests, or we cannot
877          * send any more requests.
878          */
879         if ((xfer_size <= 0 && num_out_reqs > 0) ||
880             num_out_reqs == max_out_reads) {
881             /*
882              * Find the next outstanding request that will timeout
883              * and take a time differential to use for the poll timeout.
884              * If the differential is less than zero, then we go to the
885              * top of the loop. Note that we are not picky on errors
886              * returned by select, after the sleep we return to the top
887              * of the loop so extensive error/status checking is not
888              * needed.
889              */
890             tmp_time.sec = 0;
891             tmp_time.usec = 0;
892             for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) {
893                 if (reqp->in_use == FALSE) {
894                     continue;
895                 }
896                 if (tmp_time.sec == 0 ||
897                     (reqp->timeout.sec < tmp_time.sec ||
898                      (reqp->timeout.sec == tmp_time.sec &&
899                       reqp->timeout.usec < tmp_time.usec))) {
900                     
901                      tmp_time = reqp->timeout;
902                 }
903             }
904             if (tmp_time.sec == 0 && tmp_time.usec == 0)
905                 continue;
906             sfs_gettime(&curr_time);
907             SUBTIME(tmp_time, curr_time);
908             (void) biod_poll_wait(NFS_client,
909                                         tmp_time.sec * 1000000 + tmp_time.usec);
910         }
911     } /* while not done */
912
913     return(ret);
914
915 } /* op_biod_read */
916
917 #endif
918
919 /*
920  * ----------------------  Async RPC Support Routines  ----------------------
921  */
922
923 /*
924  * biod_clnt_call()
925  *
926  * Returns XID indicating success, 0 indicating failure.
927  */
928 uint32_t
929 biod_clnt_call(
930     CLIENT              *clnt_handlep,
931     uint32_t            proc,
932     xdrproc_t           xargs,
933     void                *argsp)
934 {
935     struct timeval timeout;
936     uint32_t xid;
937
938     /*
939      * Set timeouts to be zero to force message passing semantics.
940      */
941     timeout.tv_sec = 0;
942     timeout.tv_usec = 0;
943
944     if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL,
945                                         &xid, timeout)) != RPC_TIMEDOUT) {
946         clnt_perror(clnt_handlep, "biod_clnt_call failed");
947         return (0);
948     }
949
950     return (xid);
951 } /* biod_clnt_call */
952
953
954 /*
955  * biod_get_reply()
956  *
957  * Returns pointer to the biod_req struct entry that a reply was received
958  * for.  Returns NULL if an error was detected.
959  * NOTES:
960  * 1) This routine should only be called when it is known that there is
961  *    data waiting on the socket.
962  */
963 static struct biod_req *
964 biod_get_reply(
965     CLIENT              *clnt_handlep,
966     xdrproc_t           xresults,
967     void                *resultsp,
968     struct timeval      *tv)
969 {
970     uint32_t            xid;
971     int                 i;
972     int                 cnt = 0;
973     bool_t              res;
974     uint32_t            xids[MAX_BIODS];
975
976     /*
977      * Load list of valid outstanding xids
978      */
979     for (i = 0; i < max_biod_reqs; i++) {
980         if (biod_reqp[i].in_use == TRUE)
981             xids[cnt++] = biod_reqp[i].xid;
982     }
983
984     if (cnt == 0)
985         return (NULL);
986
987     if ((res = clnt_getreply(clnt_handlep, xresults,
988                         resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) {
989         if (DEBUG_CHILD_BIOD) {
990                 if (res == RPC_CANTDECODERES) {
991                         (void) fprintf(stderr, "No xid matched, found %x\n",
992                                 xid);
993                 }
994         }
995         return (NULL);
996     }
997
998     /*
999      * Scan to find XID matched in the outstanding request queue.
1000      */
1001     for (i = 0; i < max_biod_reqs; i++) {
1002         if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) {
1003             sfs_gettime(&(biod_reqp[i].stop));
1004             return (&biod_reqp[i]);
1005         }
1006     }
1007
1008     return ((struct biod_req *)0);
1009 } /* biod_get_reply */
1010
1011 /*
1012  * biod_poll_wait()
1013  *
1014  * Returns -1 on error, 0 for no data available, > 0 to indicate data available
1015  */
1016 int
1017 biod_poll_wait(
1018     CLIENT              *clnt_handlep,
1019     uint32_t            usecs)
1020 {
1021     return (clnt_poll(clnt_handlep, usecs));
1022 } /* biod_poll_wait */
1023