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