#ifndef lint static char sfs_c_bioSid[] = "@(#)sfs_c_bio.c 2.1 97/10/23"; #endif /* * Copyright (c) 1992-1997,2001 by Standard Performance Evaluation Corporation * All rights reserved. * Standard Performance Evaluation Corporation (SPEC) * 6585 Merchant Place, Suite 100 * Warrenton, VA 20187 * * This product contains benchmarks acquired from several sources who * understand and agree with SPEC's goal of creating fair and objective * benchmarks to measure computer performance. * * This copyright notice is placed here only to protect SPEC in the * event the source is misused in any manner that is contrary to the * spirit, the goals and the intent of SPEC. * * The source code is provided to the user or company under the license * agreement for the SPEC Benchmark Suite for this product. */ /* * ---------------------- sfs_c_bio.c --------------------- * * Routines that attempt to simulate biod behavior * * The routines contained here model biod behavior. Simply call * biod_init() to replace regular calls to op_read() and op_write() * with calls to op_biod_read() and op_biod_write(). The variables * max_out_writes and max_out_reads control the maximum number of * outstanding writes and reads respectively. * *.Exported Routines * int biod_init(int, int); * void biod_turn_on(void); * void op_biod_write(int, int, int); * void op_biod_read(int); * *.Local Routines * uint32_t biod_clnt_call(CLIENT *, uint32_t, * xdrproc_t, void *); * struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, * void *, struct timeval *); * int biod_poll_wait(CLIENT *, uint32_t); * *.Revision_History * 03-May-94 Robinson * History now kept in SCCS * 03-Mar-92 0.1.0 Corbin * Added biod behavior */ /* * ------------------------- Include Files ------------------------- */ /* * ANSI C headers */ #include #include #include #include #include #include #include #include "sfs_c_def.h" /* * Information associated with outstanding read/write requests */ struct biod_req { uint32_t xid; /* RPC transmission ID */ bool_t in_use; /* Indicates if the entry is in use */ unsigned int count; /* Count saved for Dump routines */ unsigned int offset; /* Offset saved for Dump routines */ struct ladtime start; /* Time RPC call was made */ struct ladtime stop; /* Time RPC reply was received */ struct ladtime timeout; /* Time RPC call will time out */ }; /* * ---------------------- Static Declarations ---------------------- */ static int max_out_writes; static int max_out_reads; static int max_biod_reqs = 0; static struct biod_req *biod_reqp; /* forward definitions for local functions */ static uint32_t biod_clnt_call(CLIENT *, uint32_t, xdrproc_t, void *); static struct biod_req *biod_get_reply(CLIENT *, xdrproc_t, void *, struct timeval *); static int biod_poll_wait(CLIENT *, uint32_t); static int op_biod_write(int, int, int); static int op_biod_read(int); /* * ---------------------- BIOD Support Routines ---------------------- */ /* * biod_init() * * This function is called during the initialization phase. It performs * the following tasks: * - Allocate memory to hold outstanding biod request information * * Returns 0 for OK, -1 for failure */ int biod_init( int out_writes, int out_reads) { max_out_writes = MAXIMUM(1, out_writes); max_out_reads = MAXIMUM(1, out_reads); max_biod_reqs = MAXIMUM(out_writes, out_reads); biod_reqp = (struct biod_req *) calloc(max_biod_reqs, sizeof (struct biod_req)); if (biod_reqp == (struct biod_req *)0) { (void) fprintf(stderr, "%s: biod_init calloc failed.\n", sfs_Myname); (void) fflush(stderr); return (-1); } return (0); } /* biod_init */ /* * - Change the operation functions for reads and writes to use the * biod routines. This step should be done last to allow callers * to still run with the old op functions if the biod initialization * fails. */ void biod_turn_on(void) { Ops[WRITE].funct = op_biod_write; Ops[READ].funct = op_biod_read; } /* * biod_term() * * This function is called during the termination phase to free any resources * allocated by the biod_init() routine. It performs the following tasks: * - Frees memory associated with outstanding biod request information * - Frees the biod client handle */ void biod_term(void) { if (max_biod_reqs) { free(biod_reqp); } } /* biod_term */ /* * Perform and RPC biod style write operation of length 'xfer_size'. * If 'append_flag' is true, then write the data to the end of the file. */ static int op_biod_write( int xfer_size, int append_flag, int stab_flag) { sfs_op_type *op_ptr; /* per operation info */ static char *buf = NULL; /* the data buffer */ unsigned int size; /* size of data write */ int max_cnt; attrstat reply2; /* the reply */ writeargs args2; WRITE3res reply3; /* the reply */ WRITE3args args3; struct ladtime curr_time; struct ladtime tmp_time; struct ladtime call_timeout; struct biod_req *reqp; int ret; /* ret val == call success */ int num_out_reqs; /* # of outstanding writes */ int i; int error; int32_t offset; static int calls = 0; calls++; if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) return (0); /* * Initialize write buffer to known value */ if (buf == NULL) { buf = init_write_buffer(); } /* * For now we treat DATA_SYNC to be the same as FILE_SYNC. * If it is not a V3 op then it must always be stable */ if (stab_flag == DATA_SYNC || nfs_version != NFS_V3) stab_flag = FILE_SYNC; op_ptr = &Ops[WRITE]; ret = 0; /* set up the arguments */ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, NFS_FHSIZE); (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, sizeof (nfs_fh3)); args2.beginoffset = 0; /* unused */ if (append_flag == 1) { args2.offset = Cur_file_ptr->attributes2.size; args3.offset = Cur_file_ptr->attributes3.size; } else { if (fh_size(Cur_file_ptr) > xfer_size) { offset = Bytes_per_block * (sfs_random() % (((fh_size(Cur_file_ptr) - xfer_size) / Bytes_per_block) + 1)); args2.offset = offset; args3.offset._p._u = 0; args3.offset._p._l = offset; } else { args2.offset = 0; args3.offset._p._u = args3.offset._p._l = 0; } } size = Bytes_per_block; args2.totalcount = size; /* unused */ args2.data.data_len = size; args2.data.data_val = buf; args3.data.data_len = size; args3.data.data_val = buf; args3.count = size; args3.stable = stab_flag; /* Calculate the number of NFS writes required */ max_cnt = xfer_size / Bytes_per_block; if ((xfer_size % Bytes_per_block) != 0) { max_cnt++; } /* check our stats to see if this would overflow */ if (!Timed_run) { if (op_ptr->target_calls > 0 && (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; } } if (DEBUG_CHILD_OPS) { (void) fprintf(stderr, "write: %d buffers xfer_size %d\n", max_cnt, xfer_size); (void) fflush(stderr); } /* Mark all request slots as not in use */ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { reqp->in_use = FALSE; } if (Current_test_phase < Warmup_phase) { call_timeout.sec = Nfs_timers[Init].tv_sec; call_timeout.usec = Nfs_timers[Init].tv_usec; } else { call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; } /* capture length for possible dump */ Dump_length = fh_size(Cur_file_ptr); /* make the call(s) now */ num_out_reqs = 0; while (xfer_size > 0 || num_out_reqs > 0) { /* * Send out calls async until either the maximum number of outstanding * requests has been reached or there are no more requests to make. */ while (num_out_reqs < max_out_writes && xfer_size > 0) { /* find an empty write request slot */ for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { if (reqp->in_use == FALSE) { break; } } if (xfer_size < size) { size = xfer_size; args2.data.data_len = xfer_size; args2.totalcount = xfer_size; /* unused */ args3.data.data_len = xfer_size; args3.count = xfer_size; } xfer_size -= size; sfs_gettime(&reqp->start); if (nfs_version == NFS_V3) { reqp->xid = biod_clnt_call(NFS_client, (uint32_t)NFSPROC3_WRITE, xdr_WRITE3args, (char *) &args3); if (reqp->xid != 0) { /* capture count and offset for possible dump */ reqp->count = args3.data.data_len; reqp->offset = args3.offset._p._l; reqp->timeout = reqp->start; ADDTIME(reqp->timeout, call_timeout); reqp->in_use = TRUE; num_out_reqs++; } } if (nfs_version == NFS_VERSION) { reqp->xid = biod_clnt_call(NFS_client, (uint32_t)NFSPROC_WRITE, xdr_write, (char *) &args2); if (reqp->xid != 0) { /* capture count and offset for possible dump */ reqp->count = args2.data.data_len; reqp->offset = args2.offset; reqp->timeout = reqp->start; ADDTIME(reqp->timeout, call_timeout); reqp->in_use = TRUE; num_out_reqs++; } } if (DEBUG_CHILD_BIOD) { (void) fprintf (stderr, "[%d]:Biod write started xid %x start (%d.%06d) timeo (%d.%06d)\n", calls, reqp->xid, reqp->start.sec, reqp->start.usec, reqp->timeout.sec, reqp->timeout.usec); } args2.offset += size; args3.offset._p._l += size; if (biod_poll_wait(NFS_client, 0) > 0) { break; } } /* while can make an async call */ /* * Process replies while there is data on the socket buffer. * Just do polls on the select, no sleeping occurs in this loop. */ do { error = biod_poll_wait(NFS_client, 0); switch (error) { case -1: if (errno == EINTR) { error = 1; continue; } if (DEBUG_CHILD_BIOD) { (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n", sfs_Myname, calls); (void) fflush(stderr); } break; case 0: break; default: if (nfs_version == NFS_VERSION) reqp = biod_get_reply(NFS_client, xdr_write, (char *) &reply2, &Nfs_timers[op_ptr->call_class]); if (nfs_version == NFS_V3) reqp = biod_get_reply(NFS_client, xdr_WRITE3res, (char *) &reply3, &Nfs_timers[op_ptr->call_class]); /* * If biod_get_reply returns NULL then we got an RPC * level error, probably a dropped fragment or the * remains of a previous partial request. */ if (reqp == (struct biod_req *)NULL) { error = 0; break; } /* * We have a valid response, check if procedure completed * correctly. */ if ((nfs_version == NFS_VERSION && reply2.status == NFS_OK) || (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) { Cur_file_ptr->state = Exists; /* * In updating attributes we may get replies out * of order. We blindly update the attributes * which may cause old attributes to be stored. * XXX We should check for old attributes. */ if (nfs_version == NFS_VERSION) Cur_file_ptr->attributes2 = reply2.attrstat_u.attributes; if (nfs_version == NFS_V3) Cur_file_ptr->attributes3 = reply3.res_u.ok.file_wcc.after.attr; if (DEBUG_CHILD_RPC) { (void) fprintf(stderr, "%s: WRITE %s %d bytes offset %d \n", sfs_Myname, Cur_filename, reqp->count, reqp->offset); (void) fflush(stderr); } /* capture count and offset for possible dump */ Dump_count = reqp->count; Dump_offset = reqp->offset; sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); op_ptr->results.good_calls++; Ops[TOTAL].results.good_calls++; ret++; reqp->in_use = FALSE; num_out_reqs--; if (DEBUG_CHILD_BIOD) { (void) fprintf (stderr, "[%d]:Biod write succeded xid %x start (%d.%06d) timeo (%d.%06d) stop (%d.%06d)\n", calls, reqp->xid, reqp->start.sec, reqp->start.usec, reqp->timeout.sec, reqp->timeout.usec, reqp->stop.sec, reqp->stop.usec); } } else { op_ptr->results.bad_calls++; Ops[TOTAL].results.bad_calls++; reqp->in_use = FALSE; num_out_reqs--; if (DEBUG_CHILD_BIOD) { (void) fprintf (stderr, "[%d]:Biod write failed xid %x start (%d.%06d) timeo (%d.%06d)\n", calls, reqp->xid, reqp->start.sec, reqp->start.usec, reqp->timeout.sec, reqp->timeout.usec); (void) fprintf(stderr, "[%d]:BIOD WRITE FAILED: xid %x", calls, reqp->xid); if (nfs_version == NFS_VERSION) (void) fprintf(stderr, " status %d", reply2.status); if (nfs_version == NFS_V3) (void) fprintf(stderr, " status %d", reply3.status); (void) fprintf(stderr, "\n"); } } break; } } while (error > 0 && num_out_reqs > 0); /* Scan for replies that have timed out */ if (num_out_reqs > 0) { sfs_gettime(&curr_time); for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { if (reqp->in_use == FALSE) { continue; } if (reqp->timeout.sec < curr_time.sec || (reqp->timeout.sec == curr_time.sec && reqp->timeout.usec < curr_time.usec)) { op_ptr->results.bad_calls++; Ops[TOTAL].results.bad_calls++; reqp->in_use = FALSE; num_out_reqs--; if (DEBUG_CHILD_BIOD) { (void) fprintf (stderr, "[%d]:Biod write timed out %x start (%d.%06d) timeo (%d.%06d) now (%d.%06d)\n", calls, reqp->xid, reqp->start.sec, reqp->start.usec, reqp->timeout.sec, reqp->timeout.usec, curr_time.sec, curr_time.usec); if (biod_poll_wait(NFS_client, 0) > 0) { (void) fprintf(stderr, "[%d]:BIOD WRITE TIMEOUT - data on input queue!\n", calls); } } } } } /* * We go to sleep waiting for a reply if all the requests have * been sent and there are outstanding requests, or we cannot * send any more requests. */ if ((xfer_size <= 0 && num_out_reqs > 0) || num_out_reqs == max_out_writes) { /* * Find the next outstanding request that will timeout * and take a time differential to use for the poll timeout. * If the differential is less than zero, then we go to the * top of the loop. Note that we are not picky on errors * returned by select, after the sleep we return to the top * of the loop so extensive error/status checking is not * needed. */ tmp_time.sec = 0; tmp_time.usec = 0; for (reqp = biod_reqp, i = 0; i < max_out_writes; i++, reqp++) { if (reqp->in_use == FALSE) { continue; } if (tmp_time.sec == 0 || (reqp->timeout.sec < tmp_time.sec || (reqp->timeout.sec == tmp_time.sec && reqp->timeout.usec < tmp_time.usec))) { tmp_time = reqp->timeout; } } if (tmp_time.sec == 0 && tmp_time.usec == 0) continue; sfs_gettime(&curr_time); SUBTIME(tmp_time, curr_time); (void) biod_poll_wait(NFS_client, tmp_time.sec * 1000000 + tmp_time.usec); } } /* while not done */ /* * If we have not gotten an error and we were asked for an async write * send a commit operation. */ if (ret && stab_flag != FILE_SYNC) ret += (*Ops[COMMIT].funct)(); return (ret); } /* op_biod_write */ /* * perform an RPC read operation of length 'xfer_size' */ static int op_biod_read( int xfer_size) { sfs_op_type *op_ptr; /* per operation info */ int max_cnt; /* packet ctrs */ char buf[DEFAULT_MAX_BUFSIZE];/* data buffer */ readargs args2; readres reply2; /* the reply */ READ3args args3; READ3res reply3; /* the reply */ int size; struct ladtime curr_time; struct ladtime call_timeout; struct ladtime tmp_time; struct biod_req *reqp; int ret; /* ret val == call success */ int num_out_reqs; /* # of outstanding writes */ int i; int error; int32_t offset; static int calls = 0; calls++; if (nfs_version != NFS_VERSION && nfs_version != NFS_V3) return (0); op_ptr = &Ops[READ]; ret = 0; /* set up the arguments */ (void) memmove((char *) &args2.file, (char *) &Cur_file_ptr->fh2, NFS_FHSIZE); (void) memmove((char *) &args3.file, (char *) &Cur_file_ptr->fh3, sizeof (nfs_fh3)); /* * Don't allow a read of less than one block size */ if (xfer_size < Bytes_per_block) xfer_size = Bytes_per_block; /* Calculate the number of NFS reads required */ max_cnt = xfer_size / Bytes_per_block; if ((xfer_size % Bytes_per_block) != 0) { max_cnt++; } /* check our stats to see if this would overflow */ if (!Timed_run) { if (op_ptr->target_calls > 0 && (op_ptr->results.good_calls + max_cnt) > op_ptr->target_calls) { max_cnt = op_ptr->target_calls - op_ptr->results.good_calls; } } args2.offset = 0; args3.offset._p._l = args3.offset._p._u = 0; /* * randomly choose an offset that is a multiple of the block size * and constrained by making the transfer fit within the file */ if (fh_size(Cur_file_ptr) > xfer_size) { offset = Bytes_per_block * (sfs_random() % (((fh_size(Cur_file_ptr) - xfer_size) / Bytes_per_block) + 1)); args2.offset = offset; args3.offset._p._u = 0; args3.offset._p._l = offset; } size = Bytes_per_block; args2.count = size; args3.count = size; args2.totalcount = size; /* unused */ /* Have lower layers fill in the data directly. */ reply2.readres_u.reply.data.data_val = buf; reply3.res_u.ok.data.data_val = buf; if (DEBUG_CHILD_OPS) { (void) fprintf(stderr, "read: %d buffers xfer_size %d\n", max_cnt, xfer_size); (void) fflush(stderr); } /* Mark all request slots as not in use */ for (reqp = biod_reqp, i = 0; i < max_biod_reqs; i++, reqp++) { reqp->in_use = FALSE; } if (Current_test_phase < Warmup_phase) { call_timeout.sec = Nfs_timers[Init].tv_sec; call_timeout.usec = Nfs_timers[Init].tv_usec; } else { call_timeout.sec = Nfs_timers[op_ptr->call_class].tv_sec; call_timeout.usec = Nfs_timers[op_ptr->call_class].tv_usec; } /* capture length for possible dump */ Dump_length = fh_size(Cur_file_ptr); /* make the call(s) now */ num_out_reqs = 0; while (xfer_size > 0 || num_out_reqs > 0) { /* * Send out calls async until either the maximum number of outstanding * requests has been reached or there are no more requests to make. */ while (num_out_reqs < max_out_reads && xfer_size > 0) { /* find an empty read request slot */ for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { if (reqp->in_use == FALSE) { break; } } if (xfer_size < size) { size = xfer_size; args2.count = xfer_size; args3.count = xfer_size; args2.totalcount = xfer_size; /* unused */ } xfer_size -= size; sfs_gettime(&reqp->start); if (nfs_version == NFS_VERSION) { reqp->xid = biod_clnt_call(NFS_client, (uint32_t)NFSPROC_READ, xdr_read, (char *) &args2); if (reqp->xid != 0) { /* capture count and offset for possible dump */ reqp->count = args2.count; reqp->offset = args2.offset; reqp->timeout = reqp->start; ADDTIME(reqp->timeout, call_timeout); reqp->in_use = TRUE; num_out_reqs++; } } else if (nfs_version == NFS_V3) { reqp->xid = biod_clnt_call(NFS_client, (uint32_t)NFSPROC3_READ, xdr_READ3args, (char *) &args3); if (reqp->xid != 0) { /* capture count and offset for possible dump */ reqp->count = args3.count; reqp->offset = args3.offset._p._l; reqp->timeout = reqp->start; ADDTIME(reqp->timeout, call_timeout); reqp->in_use = TRUE; num_out_reqs++; } } args2.offset += size; args3.offset._p._l += size; if (biod_poll_wait(NFS_client, 0) > 0) { break; } } /* while can make an async call */ /* * Process replies while there is data on the socket buffer. * Just do polls on the select, no sleeping occurs in this loop. */ do { error = biod_poll_wait(NFS_client, 0); switch (error) { case -1: if (errno == EINTR) { error = 1; continue; } if (DEBUG_CHILD_BIOD) { (void) fprintf(stderr, "%s:[%d]: biod_poll_wait error\n", sfs_Myname, calls); (void) fflush(stderr); } break; case 0: break; default: if (nfs_version == NFS_VERSION) reqp = biod_get_reply(NFS_client, xdr_read, (char *) &reply2, &Nfs_timers[op_ptr->call_class]); if (nfs_version == NFS_V3) reqp = biod_get_reply(NFS_client, xdr_READ3res, (char *) &reply3, &Nfs_timers[op_ptr->call_class]); /* * If biod_get_reply returns NULL then we got an RPC * level error, probably a dropped fragment or the * remains of a previous partial request. */ if (reqp == (struct biod_req *)NULL) { error = 0; break; } /* * We have a valid response, check if procedure completed * correctly. */ if ((nfs_version == NFS_VERSION && reply2.status == NFS_OK) || (nfs_version == NFS_V3 && reply3.status == NFS3_OK)) { Cur_file_ptr->state = Exists; if (DEBUG_CHILD_RPC) { (void) fprintf(stderr, "%s: READ %s %d bytes offset %d\n", sfs_Myname, Cur_filename, reqp->count, reqp->offset); (void) fflush(stderr); } /* * In updating attributes we may get replies out * of order. We blindly update the attributes * which may cause old attributes to be stored. * XXX We should check for old attributes. */ if (nfs_version == NFS_VERSION) { Cur_file_ptr->attributes2 = reply2.readres_u.reply.attributes; /* capture count and offset for possible dump */ Dump_count = reply2.readres_u.reply.data.data_len; } if (nfs_version == NFS_V3) { Cur_file_ptr->attributes3 = reply3.res_u.ok.file_attributes.attr; /* capture count and offset for possible dump */ Dump_count = reply3.res_u.ok.data.data_len; } Dump_offset = reqp->offset; sfs_elapsedtime(op_ptr, &reqp->start, &reqp->stop); op_ptr->results.good_calls++; Ops[TOTAL].results.good_calls++; ret++; reqp->in_use = FALSE; num_out_reqs--; } else { op_ptr->results.bad_calls++; Ops[TOTAL].results.bad_calls++; reqp->in_use = FALSE; num_out_reqs--; if (DEBUG_CHILD_BIOD) { (void) fprintf(stderr, "[%d]:BIOD READ FAILED: xid %x", calls, reqp->xid); if (nfs_version == NFS_VERSION) (void) fprintf(stderr, " status %d", reply2.status); if (nfs_version == NFS_V3) (void) fprintf(stderr, " status %d", reply3.status); (void) fprintf(stderr, "\n"); } } break; } /* switch */ } while (error > 0 && num_out_reqs > 0); /* Scan for replies that have timed out */ if (num_out_reqs > 0) { sfs_gettime(&curr_time); for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { if (reqp->in_use == FALSE) { continue; } if (reqp->timeout.sec < curr_time.sec || (reqp->timeout.sec == curr_time.sec && reqp->timeout.usec < curr_time.usec)) { op_ptr->results.bad_calls++; Ops[TOTAL].results.bad_calls++; reqp->in_use = FALSE; num_out_reqs--; if (DEBUG_CHILD_BIOD) { (void) fprintf (stderr, "[%d]:Biod read timed out %x (%d.%06d) now (%d.%06d)\n", calls, reqp->xid, reqp->timeout.sec, reqp->timeout.usec, curr_time.sec, curr_time.usec); if (biod_poll_wait(NFS_client, 0) > 0) { (void) fprintf(stderr, "[%d]:BIOD READ TIMEOUT - data on input queue!\n", calls); } } } } } /* * We go to sleep waiting for a reply if all the requests have * been sent and there are outstanding requests, or we cannot * send any more requests. */ if ((xfer_size <= 0 && num_out_reqs > 0) || num_out_reqs == max_out_reads) { /* * Find the next outstanding request that will timeout * and take a time differential to use for the poll timeout. * If the differential is less than zero, then we go to the * top of the loop. Note that we are not picky on errors * returned by select, after the sleep we return to the top * of the loop so extensive error/status checking is not * needed. */ tmp_time.sec = 0; tmp_time.usec = 0; for (reqp = biod_reqp, i = 0; i < max_out_reads; i++, reqp++) { if (reqp->in_use == FALSE) { continue; } if (tmp_time.sec == 0 || (reqp->timeout.sec < tmp_time.sec || (reqp->timeout.sec == tmp_time.sec && reqp->timeout.usec < tmp_time.usec))) { tmp_time = reqp->timeout; } } if (tmp_time.sec == 0 && tmp_time.usec == 0) continue; sfs_gettime(&curr_time); SUBTIME(tmp_time, curr_time); (void) biod_poll_wait(NFS_client, tmp_time.sec * 1000000 + tmp_time.usec); } } /* while not done */ return(ret); } /* op_biod_read */ /* * ---------------------- Async RPC Support Routines ---------------------- */ /* * biod_clnt_call() * * Returns XID indicating success, 0 indicating failure. */ static uint32_t biod_clnt_call( CLIENT *clnt_handlep, uint32_t proc, xdrproc_t xargs, void *argsp) { struct timeval timeout; uint32_t xid; /* * Set timeouts to be zero to force message passing semantics. */ timeout.tv_sec = 0; timeout.tv_usec = 0; if ((clnt_call(clnt_handlep, proc, xargs, argsp, NULL, &xid, timeout)) != RPC_TIMEDOUT) { clnt_perror(clnt_handlep, "biod_clnt_call failed"); return (0); } return (xid); } /* biod_clnt_call */ /* * biod_get_reply() * * Returns pointer to the biod_req struct entry that a reply was received * for. Returns NULL if an error was detected. * NOTES: * 1) This routine should only be called when it is known that there is * data waiting on the socket. */ static struct biod_req * biod_get_reply( CLIENT *clnt_handlep, xdrproc_t xresults, void *resultsp, struct timeval *tv) { uint32_t xid; int i; int cnt = 0; bool_t res; uint32_t xids[MAX_BIODS]; /* * Load list of valid outstanding xids */ for (i = 0; i < max_biod_reqs; i++) { if (biod_reqp[i].in_use == TRUE) xids[cnt++] = biod_reqp[i].xid; } if (cnt == 0) return (NULL); if ((res = clnt_getreply(clnt_handlep, xresults, resultsp, cnt, xids, &xid, tv)) != RPC_SUCCESS) { if (DEBUG_CHILD_BIOD) { if (res == RPC_CANTDECODERES) { (void) fprintf(stderr, "No xid matched, found %x\n", xid); } } return (NULL); } /* * Scan to find XID matched in the outstanding request queue. */ for (i = 0; i < max_biod_reqs; i++) { if (biod_reqp[i].in_use == TRUE && biod_reqp[i].xid == xid) { sfs_gettime(&(biod_reqp[i].stop)); return (&biod_reqp[i]); } } return ((struct biod_req *)0); } /* biod_get_reply */ /* * biod_poll_wait() * * Returns -1 on error, 0 for no data available, > 0 to indicate data available */ static int biod_poll_wait( CLIENT *clnt_handlep, uint32_t usecs) { return (clnt_poll(clnt_handlep, usecs)); } /* biod_poll_wait */