diff options
Diffstat (limited to 'src/nettest_dlpi.c')
-rw-r--r-- | src/nettest_dlpi.c | 4038 |
1 files changed, 4038 insertions, 0 deletions
diff --git a/src/nettest_dlpi.c b/src/nettest_dlpi.c new file mode 100644 index 0000000..b93f917 --- /dev/null +++ b/src/nettest_dlpi.c @@ -0,0 +1,4038 @@ + +/****************************************************************/ +/* */ +/* nettest_dlpi.c */ +/* */ +/* the actual test routines... */ +/* */ +/* send_dlpi_co_stream() perform a CO DLPI stream test */ +/* recv_dlpi_co_stream() */ +/* send_dlpi_co_rr() perform a CO DLPI req/res */ +/* recv_dlpi_co_rr() */ +/* send_dlpi_cl_stream() perform a CL DLPI stream test */ +/* recv_dlpi_cl_stream() */ +/* send_dlpi_cl_rr() perform a CL DLPI req/res */ +/* recv_dlpi_cl_rr() */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WANT_DLPI +char nettest_dlpi_id[]="\ +@(#)nettest_dlpi.c (c) Copyright 1993-2012 Hewlett-Packard Co. Version 2.6.0"; + +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <malloc.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/poll.h> +#ifdef __osf__ +#include <sys/dlpihdr.h> +#else /* __osf__ */ +#include <sys/dlpi.h> +#ifdef __hpux__ +#include <sys/dlpi_ext.h> +#endif /* __hpux__ */ +#endif /* __osf__ */ + +#include "netlib.h" +#include "netsh.h" +#include "nettest_dlpi.h" + +/* some stuff for DLPI control messages */ +#define DLPI_DATA_SIZE 2048 + +unsigned long control_data[DLPI_DATA_SIZE]; +struct strbuf control_message = {DLPI_DATA_SIZE, 0, (char *)control_data}; + +/* these are some variables global to all the DLPI tests. declare */ +/* them static to make them global only to this file */ + +static int + rsw_size, /* remote send window size */ + rrw_size, /* remote recv window size */ + lsw_size, /* local send window size */ + lrw_size, /* local recv window size */ + req_size = 100, /* request size */ + rsp_size = 200, /* response size */ + send_size, /* how big are individual sends */ + recv_size; /* how big are individual receives */ + +int + loc_ppa = 4, /* the ppa for the local interface, */ + /* as shown as the NM Id in lanscan */ + rem_ppa = 4, /* the ppa for the remote interface */ + dlpi_sap = 84; /* which 802.2 SAP should we use? */ + +char loc_dlpi_device[32] = "/dev/dlpi"; +char rem_dlpi_device[32] = "/dev/dlpi"; + +char dlpi_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +CO/CL DLPI Test Options:\n\ + -D dev[,dev] Set the local/remote DLPI device file name\n\ + -h Display this text\n\ + -M bytes Set the recv size (DLCO_STREAM, DLCL_STREAM)\n\ + -m bytes Set the send size (DLCO_STREAM, DLCL_STREAM)\n\ + -p loc[,rem] Set the local/remote PPA for the test\n\ + -R bytes Set response size (DLCO_RR, DLCL_RR)\n\ + -r bytes Set request size (DLCO_RR, DLCL_RR)\n\ + -s sap Set the 802.2 sap for the test\n\ + -W send[,recv] Set remote send/recv window sizes\n\ + -w send[,recv] Set local send/recv window sizes\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + + +/* routines that used to be in src/netlib.c but this code is the only + code that uses them. raj 20110111 */ + + +int +put_control(fd, len, pri, ack) + int fd, len, pri, ack; +{ + int error; + int flags = 0; + dl_error_ack_t *err_ack = (dl_error_ack_t *)control_data; + + control_message.len = len; + + if ((error = putmsg(fd, &control_message, 0, pri)) < 0 ) { + fprintf(where,"put_control: putmsg error %d\n",error); + fflush(where); + return(-1); + } + if ((error = getmsg(fd, &control_message, 0, &flags)) < 0) { + fprintf(where,"put_control: getsmg error %d\n",error); + fflush(where); + return(-1); + } + if (err_ack->dl_primitive != ack) { + fprintf(where,"put_control: acknowledgement error wanted %u got %u \n", + ack,err_ack->dl_primitive); + if (err_ack->dl_primitive == DL_ERROR_ACK) { + fprintf(where," dl_error_primitive: %u\n", + err_ack->dl_error_primitive); + fprintf(where," dl_errno: %u\n", + err_ack->dl_errno); + fprintf(where," dl_unix_errno %u\n", + err_ack->dl_unix_errno); + } + fflush(where); + return(-1); + } + + return(0); +} + +int +dl_open(char devfile[], int ppa) +{ + int fd; + dl_attach_req_t *attach_req = (dl_attach_req_t *)control_data; + + if ((fd = open(devfile, O_RDWR)) == -1) { + fprintf(where,"netperf: dl_open: open of %s failed, errno = %d\n", + devfile, + errno); + return(-1); + } + + attach_req->dl_primitive = DL_ATTACH_REQ; + attach_req->dl_ppa = ppa; + + if (put_control(fd, sizeof(dl_attach_req_t), 0, DL_OK_ACK) < 0) { + fprintf(where, + "netperf: dl_open: could not send control message, errno = %d\n", + errno); + return(-1); + } + return(fd); +} + +int +dl_bind(int fd, int sap, int mode, char *dlsap_ptr, int *dlsap_len) +{ + dl_bind_req_t *bind_req = (dl_bind_req_t *)control_data; + dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)control_data; + + bind_req->dl_primitive = DL_BIND_REQ; + bind_req->dl_sap = sap; + bind_req->dl_max_conind = 1; + bind_req->dl_service_mode = mode; + bind_req->dl_conn_mgmt = 0; + bind_req->dl_xidtest_flg = 0; + + if (put_control(fd, sizeof(dl_bind_req_t), 0, DL_BIND_ACK) < 0) { + fprintf(where, + "netperf: dl_bind: could not send control message, errno = %d\n", + errno); + return(-1); + } + + /* at this point, the control_data portion of the control message */ + /* structure should contain a DL_BIND_ACK, which will have a full */ + /* DLSAP in it. we want to extract this and pass it up so that */ + /* it can be passed around. */ + if (*dlsap_len >= bind_ack->dl_addr_length) { + bcopy((char *)bind_ack+bind_ack->dl_addr_offset, + dlsap_ptr, + bind_ack->dl_addr_length); + *dlsap_len = bind_ack->dl_addr_length; + return(0); + } + else { + return (-1); + } +} + +int +dl_connect(int fd, unsigned char *remote_addr, int remote_addr_len) +{ + dl_connect_req_t *connection_req = (dl_connect_req_t *)control_data; + dl_connect_con_t *connection_con = (dl_connect_con_t *)control_data; + struct pollfd pinfo; + + int flags = 0; + + /* this is here on the off chance that we really want some data */ + u_long data_area[512]; + struct strbuf data_message; + + int error; + + data_message.maxlen = 2048; + data_message.len = 0; + data_message.buf = (char *)data_area; + + connection_req->dl_primitive = DL_CONNECT_REQ; + connection_req->dl_dest_addr_length = remote_addr_len; + connection_req->dl_dest_addr_offset = sizeof(dl_connect_req_t); + connection_req->dl_qos_length = 0; + connection_req->dl_qos_offset = 0; + bcopy (remote_addr, + (unsigned char *)control_data + sizeof(dl_connect_req_t), + remote_addr_len); + + /* well, I would call the put_control routine here, but the sequence */ + /* of connection stuff with DLPI is a bit screwey with all this */ + /* message passing - Toto, I don't think were in Berkeley anymore. */ + + control_message.len = sizeof(dl_connect_req_t) + remote_addr_len; + if ((error = putmsg(fd,&control_message,0,0)) !=0) { + fprintf(where,"dl_connect: putmsg failure, errno = %d, error 0x%x \n", + errno,error); + fflush(where); + return(-1); + }; + + pinfo.fd = fd; + pinfo.events = POLLIN | POLLPRI; + pinfo.revents = 0; + + if ((error = getmsg(fd,&control_message,&data_message,&flags)) != 0) { + fprintf(where,"dl_connect: getmsg failure, errno = %d, error 0x%x \n", + errno,error); + fflush(where); + return(-1); + } + while (control_data[0] == DL_TEST_CON) { + /* i suppose we spin until we get an error, or a connection */ + /* indication */ + if((error = getmsg(fd,&control_message,&data_message,&flags)) !=0) { + fprintf(where,"dl_connect: getmsg failure, errno = %d, error = 0x%x\n", + errno,error); + fflush(where); + return(-1); + } + } + + /* we are out - it either worked or it didn't - which was it? */ + if (control_data[0] == DL_CONNECT_CON) { + return(0); + } + else { + return(-1); + } +} + +int +dl_accept(fd, remote_addr, remote_addr_len) + int fd; + unsigned char *remote_addr; + int remote_addr_len; +{ + dl_connect_ind_t *connect_ind = (dl_connect_ind_t *)control_data; + dl_connect_res_t *connect_res = (dl_connect_res_t *)control_data; + int tmp_cor; + int flags = 0; + + /* hang around and wait for a connection request */ + getmsg(fd,&control_message,0,&flags); + while (control_data[0] != DL_CONNECT_IND) { + getmsg(fd,&control_message,0,&flags); + } + + /* now respond to the request. at some point, we may want to be sure */ + /* that the connection came from the correct station address, but */ + /* will assume that we do not have to worry about it just now. */ + + tmp_cor = connect_ind->dl_correlation; + + connect_res->dl_primitive = DL_CONNECT_RES; + connect_res->dl_correlation = tmp_cor; + connect_res->dl_resp_token = 0; + connect_res->dl_qos_length = 0; + connect_res->dl_qos_offset = 0; + connect_res->dl_growth = 0; + + return(put_control(fd, sizeof(dl_connect_res_t), 0, DL_OK_ACK)); + +} + +int +dl_set_window(fd, window) + int fd, window; +{ + return(0); +} + +void +dl_stats(fd) + int fd; +{ +} + +int +dl_send_disc(fd) + int fd; +{ +} + +int +dl_recv_disc(fd) + int fd; +{ +} + + +/* This routine implements the CO unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_dlpi_co_stream() +{ + + char *tput_title = "\ +Recv Send Send \n\ +Window Window Message Elapsed \n\ +Size Size Size Time Throughput \n\ +frames frames bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Window Window Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +frames frames bytes secs. %-8.8s/s %% %% us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; +#endif /* WANT_INTERVALS */ + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *send_ring; + char *message; + char *message_ptr; + struct strbuf send_message; + char dlsap[BUFSIZ]; + int dlsap_len; + int *message_int_ptr; + int message_offset; + int malloc_size; + + int len; + int nummessages; + int send_descriptor; + int bytes_remaining; + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) */ + double bytes_sent; + +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct dlpi_co_stream_request_struct *dlpi_co_stream_request; + struct dlpi_co_stream_response_struct *dlpi_co_stream_response; + struct dlpi_co_stream_results_struct *dlpi_co_stream_result; + + dlpi_co_stream_request = + (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_stream_response = + (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_stream_result = + (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; + + if ( print_headers ) { + fprintf(where,"DLPI CO STREAM TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data descriptor */ + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_co_stream: dlpi stream data descriptor"); + exit(1); + } + + /* bind the puppy and get the assigned dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_co_rr: bind failure\n"); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_co_stream: send_descriptor obtained...\n"); + } + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_stream: window send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where, + "netperf: send_dlpi_co_stream: window recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + + + if (debug) { + fprintf(where, + "netperf: send_dlpi_co_stream: window sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + fflush(where); + } + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* we should pick a default send_size, it should not be larger than */ + /* the min of the two interface MTU's, and should perhaps default to */ + /* the Interface MTU, but for now, we will default it to 1024... if */ + /* someone wants to change this, the should change the corresponding */ + /* lines in the recv_dlpi_co_stream routine */ + + if (send_size == 0) { + send_size = 1024; + } + + /* set-up the data buffer with the requested alignment and offset. */ + /* After we have calculated the proper starting address, we want to */ + /* put that back into the message variable so we go back to the */ + /* proper place. note that this means that only the first send is */ + /* guaranteed to be at the alignment specified by the -a parameter. I */ + /* think that this is a little more "real-world" than what was found */ + /* in previous versions. note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lsw_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + + send_message.maxlen = send_size; + send_message.len = send_size; + send_message.buf = send_ring->buffer_ptr; + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. */ + + netperf_request.content.request_type = DO_DLPI_CO_STREAM; + dlpi_co_stream_request->send_win_size = rsw_size; + dlpi_co_stream_request->recv_win_size = rrw_size; + dlpi_co_stream_request->receive_size = recv_size; + dlpi_co_stream_request->recv_alignment= remote_recv_align; + dlpi_co_stream_request->recv_offset = remote_recv_offset; + dlpi_co_stream_request->measure_cpu = remote_cpu_usage; + dlpi_co_stream_request->cpu_rate = remote_cpu_rate; + dlpi_co_stream_request->ppa = rem_ppa; + dlpi_co_stream_request->sap = dlpi_sap; + dlpi_co_stream_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_co_stream_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_stream_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_co_stream_request->test_length = test_time; + } + else { + dlpi_co_stream_request->test_length = test_bytes; + } +#ifdef DIRTY + dlpi_co_stream_request->dirty_count = rem_dirty_count; + dlpi_co_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + + + if (debug > 1) { + fprintf(where, + "netperf: send_dlpi_co_stream: requesting DLPI CO stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_co_stream_response->recv_win_size; + rsw_size = dlpi_co_stream_response->send_win_size; + remote_cpu_usage= dlpi_co_stream_response->measure_cpu; + remote_cpu_rate = dlpi_co_stream_response->cpu_rate; + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + exit(1); + } + + /* Connect up to the remote port on the data descriptor */ + if(dl_connect(send_descriptor, + dlpi_co_stream_response->station_addr, + dlpi_co_stream_response->station_addr_len) != 0) { + fprintf(where,"recv_dlpi_co_stream: connect failure\n"); + fflush(where); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + +#ifdef DIRTY + /* initialize the random number generator for putting dirty stuff */ + /* into the send buffer. raj */ + srand((int) getpid()); +#endif /* DIRTY */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + message_int_ptr = (int *)message_ptr; + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) + break; + perror("netperf: data send error"); + exit(1); + } + send_ring = send_ring->next; + send_message.buf = send_ring->buffer_ptr; +#ifdef WANT_INTERVALS + for (interval_count = 0; + interval_count < interval_wate; + interval_count++); +#endif /* WANT_INTERVALS */ + + if (debug > 4) { + fprintf(where,"netperf: send_clpi_co_stream: putmsg called "); + fprintf(where,"len is %d\n",send_message.len); + fflush(where); + } + + nummessages++; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. this needs a little work - there is no three-way */ + /* handshake with type two as there is with TCP, so there really */ + /* should be a message exchange here. however, we will finesse it by */ + /* saying that the tests shoudl run for a while. */ + + if (debug) { + fprintf(where,"sending test end signal \n"); + fflush(where); + } + + send_message.len = (send_size - 1); + if (send_message.len == 0) send_message.len = 2; + + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + perror("netperf: data send error"); + exit(1); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ((double) send_size * (double) nummessages) + (double) len; + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where, + "WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where, + "Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where, + "DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where, + "Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_co_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + dlpi_co_stream_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1, /* the format string */ + rrw_size, /* remote recvbuf size */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1, /* the format string */ + rrw_size, /* remote recvbuf size */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)dlpi_co_stream_result->recv_calls, + dlpi_co_stream_result->recv_calls); + } + +} + + +/* This is the server-side routine for the tcp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +int + recv_dlpi_co_stream() +{ + + int data_descriptor; + int flags = 0; + int measure_cpu; + int bytes_received; + int receive_calls; + float elapsed_time; + + struct ring_elt *recv_ring; + char *message_ptr; + char *message; + int *message_int_ptr; + struct strbuf recv_message; + int dirty_count; + int clean_count; + int i; + + struct dlpi_co_stream_request_struct *dlpi_co_stream_request; + struct dlpi_co_stream_response_struct *dlpi_co_stream_response; + struct dlpi_co_stream_results_struct *dlpi_co_stream_results; + + dlpi_co_stream_request = (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_stream_response = (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_stream_results = (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_co_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + netperf_response.content.response_type = DLPI_CO_STREAM_RESPONSE; + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_co_stream: requested alignment of %d\n", + dlpi_co_stream_request->recv_alignment); + fflush(where); + } + + + /* Grab a descriptor to listen on, and then listen on it. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_co_stream: grabbing a descriptor...\n"); + fflush(where); + } + + + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_stream_request->dlpi_device; + lastword = initword + ((dlpi_co_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_co_stream_request->dlpi_device, + dlpi_co_stream_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this descriptor so we can tell the */ + /* initiator how to reach the data descriptor. There may be a desire to */ + /* nail this descriptor to a specific address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_co_stream_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_co_stream_request->sap, + DL_CODLS, + (char *)dlpi_co_stream_response->station_addr, + &dlpi_co_stream_response->station_addr_len) != 0) { + fprintf(where,"recv_dlpi_co_stream: bind failure\n"); + fflush(where); + exit(1); + } + + /* The initiator may have wished-us to modify the socket buffer */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the socket buffer by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_co_stream_request->recv_win_size) { + } + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_co_stream_response->recv_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* what sort of sizes did we end-up with? */ + /* this bit of code whould default to the Interface MTU */ + if (dlpi_co_stream_request->receive_size == 0) { + recv_size = 1024; + } + else { + recv_size = dlpi_co_stream_request->receive_size; + } + + /* tell the other fellow what our receive size became */ + dlpi_co_stream_response->receive_size = recv_size; + + /* just a little prep work for when we may have to behave like the */ + /* sending side... */ + message = (char *)malloc(recv_size * 2); + if (message == NULL) { + printf("malloc(%d) failed!\n", recv_size * 2); + exit(1); + } + + message_ptr = ALIGN_BUFFER(message, dlpi_co_stream_request->recv_alignment, dlpi_co_stream_request->recv_offset); + recv_message.maxlen = recv_size; + recv_message.len = 0; + recv_message.buf = message_ptr; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_co_stream: receive alignment and offset set...\n"); + fflush(where); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + dlpi_co_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_co_stream_request->measure_cpu) { + dlpi_co_stream_response->measure_cpu = 1; + dlpi_co_stream_response->cpu_rate = + calibrate_local_cpu(dlpi_co_stream_request->cpu_rate); + } + + send_response(); + + /* accept a connection on this file descriptor. at some point, */ + /* dl_accept will "do the right thing" with the last two parms, but */ + /* for now it ignores them, so we will pass zeros. */ + + if(dl_accept(data_descriptor, 0, 0) != 0) { + fprintf(where, + "recv_dlpi_co_stream: error in accept, errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where,"netserver:recv_dlpi_co_stream: connection accepted\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_co_stream_request->measure_cpu); + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + dirty_count = dlpi_co_stream_request->dirty_count; + clean_count = dlpi_co_stream_request->clean_count; + message_int_ptr = (int *)message_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + recv_message.len = recv_size; + while (recv_message.len == recv_size) { + if (getmsg(data_descriptor, + 0, + &recv_message, + &flags) != 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + bytes_received += recv_message.len; + receive_calls++; + + if (debug) { + fprintf(where, + "netserver:recv_dlpi_co_stream: getmsg accepted %d bytes\n", + recv_message.len); + fflush(where); + } + + +#ifdef DIRTY + message_int_ptr = (int *)message_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + } + + /* The loop now exits due to zero bytes received. */ + /* should perform a disconnect to signal the sender that */ + /* we have received all the data sent. */ + + if (close(data_descriptor) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(dlpi_co_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_stream: got %d bytes\n", + bytes_received); + fprintf(where, + "recv_dlpi_co_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + dlpi_co_stream_results->bytes_received = bytes_received; + dlpi_co_stream_results->elapsed_time = elapsed_time; + dlpi_co_stream_results->recv_calls = receive_calls; + + if (dlpi_co_stream_request->measure_cpu) { + dlpi_co_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_co_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); +} + +/*********************************/ + +int send_dlpi_co_rr(char remote_host[]) +{ + + char *tput_title = "\ + Local /Remote\n\ + Window Size Request Resp. Elapsed Trans.\n\ + Send Recv Size Size Time Rate \n\ + frames frames bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ + %-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ + %-6d %-6d\n"; + + char *cpu_title = "\ + Local /Remote\n\ + Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ + Send Recv Size Size Time Rate local remote local remote\n\ + frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ + %-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ + %-6d %-6d\n"; + + char *ksink_fmt = "\ + Alignment Offset\n\ + Local Remote Local Remote\n\ + Send Recv Send Recv\n\ + %5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + int dlsap_len; + char dlsap[BUFSIZ]; + + int flags = 0; + char *send_message_ptr; + char *recv_message_ptr; + char *temp_message_ptr; + struct strbuf send_message; + struct strbuf recv_message; + + int nummessages; + int send_descriptor; + int trans_remaining; + double bytes_xferd; + + int rsp_bytes_left; + + /* we assume that station adresses fit within two ints */ + unsigned int remote_address[1]; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct dlpi_co_rr_request_struct *dlpi_co_rr_request; + struct dlpi_co_rr_response_struct *dlpi_co_rr_response; + struct dlpi_co_rr_results_struct *dlpi_co_rr_result; + + dlpi_co_rr_request = + (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_rr_response = + (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_rr_result = + (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + if ( print_headers ) { + fprintf(where,"DLPI CO REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset */ + temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + send_message_ptr = (char *)(( (long) temp_message_ptr + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + send_message_ptr = send_message_ptr + local_send_offset; + send_message.maxlen = req_size+MAXALIGNMENT+MAXOFFSET; + send_message.len = req_size; + send_message.buf = send_message_ptr; + + temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + recv_message_ptr = (char *)(( (long) temp_message_ptr + + (long) local_recv_align - 1) & + ~((long) local_recv_align - 1)); + recv_message_ptr = recv_message_ptr + local_recv_offset; + recv_message.maxlen = rsp_size+MAXALIGNMENT+MAXOFFSET; + recv_message.len = 0; + recv_message.buf = send_message_ptr; + + /*set up the data socket */ + + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_co_rr: tcp stream data descriptor"); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_co_rr: send_descriptor obtained...\n"); + } + + /* bind the puppy and get the assigned dlsap */ + + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_co_rr: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size. The reason we alter the send buffer */ + /* size here rather than when the connection is made is to take care */ + /* of decreases in buffer size. Decreasing the window size after */ + /* connection establishment is a TCP no-no. Also, by setting the */ + /* buffer (window) size before the connection is established, we can */ + /* control the TCP MSS (segment size). The MSS is never more that 1/2 */ + /* the minimum receive buffer size at each half of the connection. */ + /* This is why we are altering the receive buffer size on the sending */ + /* size of a unidirectional transfer. If the user has not requested */ + /* that the socket buffers be altered, we will try to find-out what */ + /* their values are. If we cannot touch the socket buffer in any way, */ + /* we will set the values to -1 to indicate that. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: socket send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: socket recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + + + if (debug) { + fprintf(where,"netperf: send_dlpi_co_rr: socket sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + } + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_DLPI_CO_RR; + dlpi_co_rr_request->recv_win_size = rrw_size; + dlpi_co_rr_request->send_win_size = rsw_size; + dlpi_co_rr_request->recv_alignment = remote_recv_align; + dlpi_co_rr_request->recv_offset = remote_recv_offset; + dlpi_co_rr_request->send_alignment = remote_send_align; + dlpi_co_rr_request->send_offset = remote_send_offset; + dlpi_co_rr_request->request_size = req_size; + dlpi_co_rr_request->response_size = rsp_size; + dlpi_co_rr_request->measure_cpu = remote_cpu_usage; + dlpi_co_rr_request->cpu_rate = remote_cpu_rate; + dlpi_co_rr_request->ppa = rem_ppa; + dlpi_co_rr_request->sap = dlpi_sap; + dlpi_co_rr_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_co_rr_request->dlpi_device, + rem_dlpi_device); +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_rr_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_co_rr_request->test_length = test_time; + } + else { + dlpi_co_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_co_rr_response->recv_win_size; + rsw_size = dlpi_co_rr_response->send_win_size; + remote_cpu_usage= dlpi_co_rr_response->measure_cpu; + remote_cpu_rate = dlpi_co_rr_response->cpu_rate; + + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /*Connect up to the remote port on the data descriptor */ + + if(dl_connect(send_descriptor, + dlpi_co_rr_response->station_addr, + dlpi_co_rr_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_co_rr: connect failure\n"); + fflush(where); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_dlpi_co_rr: putmsg error"); + exit(1); + } + + if (debug) { + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + /* receive the response */ + /* this needs some work with streams buffers if we are going to */ + /* support requests and responses larger than the MTU of the */ + /* network, but this can wait until later */ + rsp_bytes_left = rsp_size; + recv_message.len = rsp_size; + while(rsp_bytes_left > 0) { + if((getmsg(send_descriptor, + 0, + &recv_message, + &flags)) < 0) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_dlpi_co_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= recv_message.len; + } + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + + /* At this point we used to call shutdown onthe data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_co_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + dlpi_co_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lsw_size, /* local sendbuf size */ + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rsw_size, + rrw_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lsw_size, + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rsw_size, /* remote recvbuf size */ + rrw_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt); + } + /* The test is over. Kill the data descriptor */ + + if (close(send_descriptor) == -1) { + perror("send_dlpi_co_rr: cannot shutdown tcp stream descriptor"); + } + +} + +void + send_dlpi_cl_stream(char remote_host[]) +{ + /************************************************************************/ + /* */ + /* UDP Unidirectional Send Test */ + /* */ + /************************************************************************/ + char *tput_title = + "Window Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +frames bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.2f\n\ +%5d %-7.2f %7d %7.2f\n\n"; + + + char *cpu_title = + "Window Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +frames bytes secs # # %s/sec %% us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f\n"; + + char *cpu_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ +%5d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; + + int messages_recvd; + float elapsed_time, + local_cpu_utilization, + remote_cpu_utilization; + + float local_service_demand, remote_service_demand; + double local_thruput, remote_thruput; + double bytes_sent; + double bytes_recvd; + + + int *message_int_ptr; + char *message_ptr; + char *message; + char sctl_data[BUFSIZ]; + struct strbuf send_message; + struct strbuf sctl_message; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + + char dlsap[BUFSIZ]; + int dlsap_len; + int message_offset; + int message_max_offset; + int failed_sends; + int failed_cows; + int messages_sent; + int data_descriptor; + + +#ifdef WANT_INTERVALS + int interval_count; +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; + struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; + struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; + + dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; + + if ( print_headers ) { + printf("DLPI CL UNIDIRECTIONAL SEND TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + printf(cpu_title,format_units()); + else + printf(tput_title,format_units()); + } + + failed_sends = 0; + messages_sent = 0; + times_up = 0; + + /*set up the data descriptor */ + + data_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (data_descriptor < 0){ + perror("send_dlpi_cl_stream: data descriptor"); + exit(1); + } + + /* bind the puppy and get the assigned dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(data_descriptor, + dlpi_sap, DL_CLDLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_cl_stream: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size (SNDBUF size) */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_stream: descriptor send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_stream: descriptor recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* now, we want to see if we need to set the send_size */ + if (send_size == 0) { + send_size = 1024; + } + + + /* set-up the data buffer with the requested alignment and offset, */ + /* most of the numbers here are just a hack to pick something nice */ + /* and big in an attempt to never try to send a buffer a second time */ + /* before it leaves the node...unless the user set the width */ + /* explicitly. */ + if (send_width == 0) send_width = 32; + message = (char *)malloc(send_size * (send_width + 1) + local_send_align + local_send_offset); + if (message == NULL) { + printf("malloc(%d) failed!\n", send_size * (send_width + 1) + local_send_align + local_send_offset); + exit(1); + } + message_ptr = (char *)(( (long) message + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + message_ptr = message_ptr + local_send_offset; + message = message_ptr; + send_message.maxlen = send_size; + send_message.len = send_size; + send_message.buf = message; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + /* if the user supplied a cpu rate, this call will complete rather */ + /* quickly, otherwise, the cpu rate will be retured to us for */ + /* possible display. The Library will keep it's own copy of this data */ + /* for use elsewhere. We will only display it. (Does that make it */ + /* "opaque" to us?) */ + + if (local_cpu_usage) + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + + /* Tell the remote end to set up the data connection. The server */ + /* sends back the port number and alters the socket parameters there. */ + /* Of course this is a datagram service so no connection is actually */ + /* set up, the server just sets up the socket and binds it. */ + + netperf_request.content.request_type = DO_DLPI_CL_STREAM; + dlpi_cl_stream_request->recv_win_size = rrw_size; + dlpi_cl_stream_request->message_size = send_size; + dlpi_cl_stream_request->recv_alignment = remote_recv_align; + dlpi_cl_stream_request->recv_offset = remote_recv_offset; + dlpi_cl_stream_request->measure_cpu = remote_cpu_usage; + dlpi_cl_stream_request->cpu_rate = remote_cpu_rate; + dlpi_cl_stream_request->ppa = rem_ppa; + dlpi_cl_stream_request->sap = dlpi_sap; + dlpi_cl_stream_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_cl_stream_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_stream_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_cl_stream_request->test_length = test_time; + } + else { + dlpi_cl_stream_request->test_length = test_bytes * -1; + } + + + send_request(); + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dlpi_cl_stream: remote data connection done.\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dlpi_cl_stream: error on remote"); + exit(1); + } + + /* place some of the remote's addressing information into the send */ + /* structure so our sends can be sent to the correct place. Also get */ + /* some of the returned socket buffer information for user display. */ + + /* set-up the destination addressing control info */ + data_req->dl_primitive = DL_UNITDATA_REQ; + bcopy((char *)(dlpi_cl_stream_response->station_addr), + ((char *)data_req + sizeof(dl_unitdata_req_t)), + dlpi_cl_stream_response->station_addr_len); + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + data_req->dl_dest_addr_length = dlpi_cl_stream_response->station_addr_len; + /* there is a dl_priority structure too, but I am ignoring it for */ + /* the time being. */ + /* however... it is best to put some value in there lest some code + get grumpy about it - fix from Nicolas Thomas */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_req->dl_dest_addr_length; + + rrw_size = dlpi_cl_stream_response->recv_win_size; + rsw_size = dlpi_cl_stream_response->send_win_size; + remote_cpu_rate = dlpi_cl_stream_response->cpu_rate; + + + /* set up the timer to call us after test_time */ + start_timer(test_time); + + /* Get the start count for the idle counter and the start time */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + interval_count = interval_burst; +#endif /* WANT_INTERVALS */ + + /* Send datagrams like there was no tomorrow */ + while (!times_up) { +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + message_int_ptr = (int *)message_ptr; + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = 4; + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + if (putmsg(data_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + break; + } + if (errno == ENOBUFS) { + /* we might not ever hit this with STREAMS, it would probably */ + /* be better to do a getinfo request at the end of the test to */ + /* get all sorts of gory statistics. in the meantime, we will */ + /* keep this code in place. */ + failed_sends++; + continue; + } + perror("send_dlpi_cl_stream: data send error"); + if (debug) { + fprintf(where,"messages_sent %u\n",messages_sent); + fflush(where); + } + exit(1); + } + messages_sent++; + + /* now we want to move our pointer to the next position in the */ + /* data buffer...since there was a successful send */ + + +#ifdef WANT_INTERVALS + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call the sleep routine for some milliseconds, if our */ + /* timer popped while we were in there, we want to */ + /* break out of the loop. */ + if (msec_sleep(interval_wate)) { + break; + } + interval_count = interval_burst; + } + +#endif /* WANT_INTERVALS */ + + } + + /* This is a timed test, so the remote will be returning to us after */ + /* a time. We should not need to send any "strange" messages to tell */ + /* the remote that the test is completed, unless we decide to add a */ + /* number of messages to the test. */ + + /* the test is over, so get stats and stuff */ + cpu_stop(local_cpu_usage, + &elapsed_time); + + /* Get the statistics from the remote end */ + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dlpi_cl_stream: remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dlpi_cl_stream: error on remote"); + exit(1); + } + + bytes_sent = send_size * messages_sent; + local_thruput = calc_thruput(bytes_sent); + + messages_recvd = dlpi_cl_stream_results->messages_recvd; + bytes_recvd = send_size * messages_recvd; + + /* we asume that the remote ran for as long as we did */ + + remote_thruput = calc_thruput(bytes_recvd); + + /* print the results for this descriptor and message size */ + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) We pass zeros for the local */ + /* cpu utilization and elapsed time to tell the routine to use */ + /* the libraries own values for those. */ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + /* The local calculations could use variables being kept by */ + /* the local netlib routines. The remote calcuations need to */ + /* have a few things passed to them. */ + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"REMOTE CPU usage numbers based on process information only!\n"); + fflush(where); + } + + remote_cpu_utilization = dlpi_cl_stream_results->cpu_util; + remote_service_demand = calc_service_demand(bytes_recvd, + 0.0, + remote_cpu_utilization, + dlpi_cl_stream_results->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1, /* the format string */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + messages_sent, + failed_sends, + local_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rrw_size, + elapsed_time, + messages_recvd, + remote_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_thruput); + break; + case 1: + fprintf(where, + tput_fmt_1, /* the format string */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + messages_sent, + failed_sends, + local_thruput, + rrw_size, /* remote recvbuf size */ + elapsed_time, + messages_recvd, + remote_thruput + ); + break; + } + } +} + +int + recv_dlpi_cl_stream() +{ + + char *message; + int data_descriptor; + int len; + char *message_ptr; + char rctl_data[BUFSIZ]; + struct strbuf recv_message; + struct strbuf rctl_message; + int flags = 0; + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int bytes_received = 0; + float elapsed_time; + + int message_size; + int messages_recvd = 0; + int measure_cpu; + + struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; + struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; + struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; + + dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_cl_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen descriptor with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_STREAM_RESPONSE; + + if (debug > 2) { + fprintf(where,"recv_dlpi_cl_stream: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: requested alignment of %d\n", + dlpi_cl_stream_request->recv_alignment); + fflush(where); + } + + message_ptr = ALIGN_BUFFER(message, dlpi_cl_stream_request->recv_alignment, dlpi_cl_stream_request->recv_offset); + + if (dlpi_cl_stream_request->message_size > 0) { + recv_message.maxlen = dlpi_cl_stream_request->message_size; + } + else { + recv_message.maxlen = 4096; + } + recv_message.len = 0; + recv_message.buf = message_ptr; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_cl_stream: receive alignment and offset set...\n"); + fflush(where); + } + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: grabbing a descriptor...\n"); + fflush(where); + } + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_stream_request->dlpi_device; + lastword = initword + ((dlpi_cl_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_cl_stream_request->dlpi_device, + dlpi_cl_stream_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* The initiator may have wished-us to modify the window */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the sizes by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_cl_stream_request->recv_win_size) { + dlpi_cl_stream_response->recv_win_size = -1; + } + +#else /* the system won't let us play with the buffers */ + + dlpi_cl_stream_response->recv_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + dlpi_cl_stream_response->test_length = dlpi_cl_stream_request->test_length; + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_cl_stream_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_cl_stream_request->sap, + DL_CLDLS, + (char *)dlpi_cl_stream_response->station_addr, + &dlpi_cl_stream_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_cl_stream: bind failure\n"); + fflush(where); + exit(1); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + dlpi_cl_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_cl_stream_request->measure_cpu) { + /* We will pass the rate into the calibration routine. If the */ + /* user did not specify one, it will be 0.0, and we will do a */ + /* "real" calibration. Otherwise, all it will really do is */ + /* store it away... */ + dlpi_cl_stream_response->measure_cpu = 1; + dlpi_cl_stream_response->cpu_rate = calibrate_local_cpu(dlpi_cl_stream_request->cpu_rate); + } + + message_size = dlpi_cl_stream_request->message_size; + test_time = dlpi_cl_stream_request->test_length; + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_cl_stream_request->measure_cpu); + + /* The loop will exit when the timer pops, or if we happen to recv a */ + /* message of less than send_size bytes... */ + + times_up = 0; + start_timer(test_time + PAD_TIME); + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: about to enter inner sanctum.\n"); + fflush(where); + } + + while (!times_up) { + if((getmsg(data_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "dlpi_recv_cl_stream: getmsg failure: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fflush(where); + netperf_response.content.serv_errno = 996; + send_response(); + exit(1); + } + messages_recvd++; + } + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: got %d messages.\n",messages_recvd); + fflush(where); + } + + + /* The loop now exits due timer or < send_size bytes received. */ + + cpu_stop(dlpi_cl_stream_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended on a timer, subtract the PAD_TIME */ + elapsed_time -= (float)PAD_TIME; + } + else { + stop_timer(); + } + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: test ended in %f seconds.\n",elapsed_time); + fflush(where); + } + + + /* We will count the "off" message */ + bytes_received = (messages_recvd * message_size) + len; + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_stream: got %d bytes\n", + bytes_received); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_STREAM_RESULTS; + dlpi_cl_stream_results->bytes_received = bytes_received; + dlpi_cl_stream_results->messages_recvd = messages_recvd; + dlpi_cl_stream_results->elapsed_time = elapsed_time; + if (dlpi_cl_stream_request->measure_cpu) { + dlpi_cl_stream_results->cpu_util = calc_cpu_util(elapsed_time); + } + else { + dlpi_cl_stream_results->cpu_util = -1.0; + } + + if (debug > 1) { + fprintf(where, + "recv_dlpi_cl_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +int send_dlpi_cl_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Window Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +frames frames bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + float elapsed_time; + + int dlsap_len; + int flags = 0; + char *send_message_ptr; + char *recv_message_ptr; + char *temp_message_ptr; + char sctl_data[BUFSIZ]; + char rctl_data[BUFSIZ]; + char dlsap[BUFSIZ]; + struct strbuf send_message; + struct strbuf recv_message; + struct strbuf sctl_message; + struct strbuf rctl_message; + + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int nummessages; + int send_descriptor; + int trans_remaining; + int bytes_xferd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + +#ifdef WANT_INTERVALS + /* timing stuff */ +#define MAX_KEPT_TIMES 1024 + int time_index = 0; + int unused_buckets; + int kept_times[MAX_KEPT_TIMES]; + int sleep_usecs; + unsigned int total_times=0; + struct timezone dummy_zone; + struct timeval send_time; + struct timeval recv_time; + struct timeval sleep_timeval; +#endif /* WANT_INTERVALS */ + + struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; + struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; + struct dlpi_cl_rr_results_struct *dlpi_cl_rr_result; + + dlpi_cl_rr_request = + (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_rr_response = + (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_rr_result = + (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; + + /* we want to zero out the times, so we can detect unused entries. */ +#ifdef WANT_INTERVALS + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + kept_times[time_index] = 0; + time_index += 1; + } + time_index = 0; +#endif /* WANT_INTERVALS */ + + if (print_headers) { + fprintf(where,"DLPI CL REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + + /* set-up the data buffer with the requested alignment and offset */ + temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + send_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + send_message_ptr = send_message_ptr + local_send_offset; + send_message.maxlen = req_size; + send_message.len = req_size; + send_message.buf = send_message_ptr; + + temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + recv_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_recv_align - 1) & + ~((long) local_recv_align - 1)); + recv_message_ptr = recv_message_ptr + local_recv_offset; + recv_message.maxlen = rsp_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + /* lets get ourselves a file descriptor */ + + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_cl_rr: dlpi cl rr send descriptor"); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_cl_rr: send_descriptor obtained...\n"); + } + + /* bind the sap to the descriptor and get the dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, + DL_CLDLS, + dlsap, + &dlsap_len) != 0) { + fprintf(where,"send_dlpi_cl_rr: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size. If the user has not requested that */ + /* the socket buffers be altered, we will try to find-out what their */ + /* values are. If we cannot touch the socket buffer in any way, we */ + /* will set the values to -1 to indicate that. The receive socket */ + /* must have enough space to hold addressing information so += a */ + /* sizeof struct sockaddr_in to it. */ + + /* this is actually nothing code, and should be replaced with the */ + /* alalagous calls in the STREAM test where the window size is set */ + /* with the HP DLPI Extension. raj 8/94 */ +#ifdef SO_SNDBUF + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: local window size altered from system default...\n"); + fprintf(where," window: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: remote window size altered from system default...\n"); + fprintf(where," remote: %d\n",lrw_size); + } + } + + + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + + if (debug) { + fprintf(where,"netperf: send_dlpi_cl_rr: socket sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + } + +#else /* SO_SNDBUF */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* SO_SNDBUF */ + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. If */ + /* there is no idle counter in the kernel idle loop, the */ + /* local_cpu_rate will be set to -1. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_DLPI_CL_RR; + dlpi_cl_rr_request->recv_win_size = rrw_size; + dlpi_cl_rr_request->send_win_size = rsw_size; + dlpi_cl_rr_request->recv_alignment = remote_recv_align; + dlpi_cl_rr_request->recv_offset = remote_recv_offset; + dlpi_cl_rr_request->send_alignment = remote_send_align; + dlpi_cl_rr_request->send_offset = remote_send_offset; + dlpi_cl_rr_request->request_size = req_size; + dlpi_cl_rr_request->response_size = rsp_size; + dlpi_cl_rr_request->measure_cpu = remote_cpu_usage; + dlpi_cl_rr_request->cpu_rate = remote_cpu_rate; + dlpi_cl_rr_request->ppa = rem_ppa; + dlpi_cl_rr_request->sap = dlpi_sap; + dlpi_cl_rr_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_cl_rr_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_rr_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_cl_rr_request->test_length = test_time; + } + else { + dlpi_cl_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: requesting DLPI CL request/response test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_cl_rr_response->recv_win_size; + rsw_size = dlpi_cl_rr_response->send_win_size; + remote_cpu_usage= dlpi_cl_rr_response->measure_cpu; + remote_cpu_rate = dlpi_cl_rr_response->cpu_rate; + + /* set-up the destination addressing control info */ + data_req->dl_primitive = DL_UNITDATA_REQ; + bcopy((char *)(dlpi_cl_rr_response->station_addr), + ((char *)data_req + sizeof(dl_unitdata_req_t)), + dlpi_cl_rr_response->station_addr_len); + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + data_req->dl_dest_addr_length = dlpi_cl_rr_response->station_addr_len; + /* there is a dl_priority structure too, but I am ignoring it for */ + /* the time being. */ + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_req->dl_dest_addr_length; + /* famous last words - some DLPI providers get unhappy if the + priority stuff is not initialized. fix from Nicolas Thomas. */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ +#ifdef WANT_INTERVALS + gettimeofday(&send_time,&dummy_zone); +#endif /* WANT_INTERVALS */ + if(putmsg(send_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + /* there is more we could do here, but it can wait */ + perror("send_dlpi_cl_rr: data send error"); + exit(1); + } + + /* receive the response. at some point, we will need to handle */ + /* sending responses which are greater than the datalink MTU. we */ + /* may also want to add some DLPI error checking, but for now we */ + /* will ignore that and just let errors stop the test with little */ + /* indication of what might actually be wrong. */ + + if((getmsg(send_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "send_dlpi_cl_rr: recv error: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fflush(where); + exit(1); + } +#ifdef WANT_INTERVALS + gettimeofday(&recv_time,&dummy_zone); + + /* now we do some arithmatic on the two timevals */ + if (recv_time.tv_usec < send_time.tv_usec) { + /* we wrapped around a second */ + recv_time.tv_usec += 1000000; + recv_time.tv_sec -= 1; + } + + /* and store it away */ + kept_times[time_index] = (recv_time.tv_sec - send_time.tv_sec) * 1000000; + kept_times[time_index] += (recv_time.tv_usec - send_time.tv_usec); + + /* at this point, we may wish to sleep for some period of */ + /* time, so we see how long that last transaction just took, */ + /* and sleep for the difference of that and the interval. We */ + /* will not sleep if the time would be less than a */ + /* millisecond. */ + if (interval_usecs > 0) { + sleep_usecs = interval_usecs - kept_times[time_index]; + if (sleep_usecs > 1000) { + /* we sleep */ + sleep_timeval.tv_sec = sleep_usecs / 1000000; + sleep_timeval.tv_usec = sleep_usecs % 1000000; + select(0, + 0, + 0, + 0, + &sleep_timeval); + } + } + + /* now up the time index */ + time_index = (time_index +1)%MAX_KEPT_TIMES; +#endif /* WANT_INTERVALS */ + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where,"Transaction %d completed\n",nummessages); + fflush(where); + } + + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a UDP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_cl_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + dlpi_cl_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lsw_size, /* local sendbuf size */ + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rsw_size, + rrw_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lsw_size, + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rsw_size, /* remote recvbuf size */ + rrw_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* UDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_INTERVALS + kept_times[MAX_KEPT_TIMES] = 0; + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + if (kept_times[time_index] > 0) { + total_times += kept_times[time_index]; + } + else + unused_buckets++; + time_index += 1; + } + total_times /= (MAX_KEPT_TIMES-unused_buckets); + fprintf(where, + "Average response time %d usecs\n", + total_times); +#endif + } +} + +int + recv_dlpi_cl_rr() +{ + + char *message; + int data_descriptor; + int flags = 0; + int measure_cpu; + + char *recv_message_ptr; + char *send_message_ptr; + char sctl_data[BUFSIZ]; + char rctl_data[BUFSIZ]; + char dlsap[BUFSIZ]; + struct strbuf send_message; + struct strbuf recv_message; + struct strbuf sctl_message; + struct strbuf rctl_message; + + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int trans_received; + int trans_remaining; + float elapsed_time; + + struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; + struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; + struct dlpi_cl_rr_results_struct *dlpi_cl_rr_results; + + dlpi_cl_rr_request = + (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_rr_response = + (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_rr_results = + (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_cl_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen descriptor with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the descriptor sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: requested recv alignment of %d offset %d\n", + dlpi_cl_rr_request->recv_alignment, + dlpi_cl_rr_request->recv_offset); + fprintf(where, + "recv_dlpi_cl_rr: requested send alignment of %d offset %d\n", + dlpi_cl_rr_request->send_alignment, + dlpi_cl_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->recv_alignment, dlpi_cl_rr_request->recv_offset); + recv_message.maxlen = dlpi_cl_rr_request->request_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + send_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->send_alignment, dlpi_cl_rr_request->send_offset); + send_message.maxlen = dlpi_cl_rr_request->response_size; + send_message.len = dlpi_cl_rr_request->response_size; + send_message.buf = send_message_ptr; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: receive alignment and offset set...\n"); + fprintf(where,"recv_dlpi_cl_rr: grabbing a socket...\n"); + fflush(where); + } + + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_rr_request->dlpi_device; + lastword = initword + ((dlpi_cl_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_cl_rr_request->dlpi_device, + dlpi_cl_rr_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + + /* The initiator may have wished-us to modify the window */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the sizes by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_cl_rr_request->recv_win_size) { + } + + if (dlpi_cl_rr_request->send_win_size) { + } + + /* Now, we will find-out what the sizes actually became, and report */ + /* them back to the user. If the calls fail, we will just report a -1 */ + /* back to the initiator for the buffer size. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_cl_rr_response->recv_win_size = -1; + dlpi_cl_rr_response->send_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_cl_rr_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_cl_rr_request->sap, + DL_CLDLS, + (char *)dlpi_cl_rr_response->station_addr, + &dlpi_cl_rr_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_cl_rr: bind failure\n"); + fflush(where); + exit(1); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + dlpi_cl_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_cl_rr_request->measure_cpu) { + dlpi_cl_rr_response->measure_cpu = 1; + dlpi_cl_rr_response->cpu_rate = calibrate_local_cpu(dlpi_cl_rr_request->cpu_rate); + } + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start receiving. */ + + cpu_start(dlpi_cl_rr_request->measure_cpu); + + if (dlpi_cl_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(dlpi_cl_rr_request->test_length + PAD_TIME); + } +else { + times_up = 1; + trans_remaining = dlpi_cl_rr_request->test_length * -1; +} + + while ((!times_up) || (trans_remaining > 0)) { + + /* receive the request from the other side. at some point we need */ + /* to handle "logical" requests and responses which are larger */ + /* than the data link MTU */ + + if((getmsg(data_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "dlpi_recv_cl_rr: getmsg failure: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fprintf(where, + " recevied %u transactions\n", + trans_received); + fflush(where); + netperf_response.content.serv_errno = 995; + send_response(); + exit(1); + } + + /* Now, send the response to the remote. first copy the dlsap */ + /* information from the receive to the sending control message */ + + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + bcopy((char *)data_ind + data_ind->dl_src_addr_offset, + (char *)data_req + data_req->dl_dest_addr_offset, + data_ind->dl_src_addr_length); + data_req->dl_dest_addr_length = data_ind->dl_src_addr_length; + data_req->dl_primitive = DL_UNITDATA_REQ; + /* be sure to initialize the priority fields. fix from Nicholas + Thomas */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_ind->dl_src_addr_length; + if(putmsg(data_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + /* there is more we could do here, but it can wait */ + fprintf(where, + "dlpi_recv_cl_rr: putmsg failure: errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = 993; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: Transaction %d complete.\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(dlpi_cl_rr_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + dlpi_cl_rr_results->bytes_received = (trans_received * + (dlpi_cl_rr_request->request_size + + dlpi_cl_rr_request->response_size)); + dlpi_cl_rr_results->trans_received = trans_received; + dlpi_cl_rr_results->elapsed_time = elapsed_time; + if (dlpi_cl_rr_request->measure_cpu) { + dlpi_cl_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +int + recv_dlpi_co_rr() +{ + + char *message; + SOCKET s_listen,data_descriptor; + + int measure_cpu; + + int flags = 0; + char *recv_message_ptr; + char *send_message_ptr; + struct strbuf send_message; + struct strbuf recv_message; + + int trans_received; + int trans_remaining; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct dlpi_co_rr_request_struct *dlpi_co_rr_request; + struct dlpi_co_rr_response_struct *dlpi_co_rr_response; + struct dlpi_co_rr_results_struct *dlpi_co_rr_results; + + dlpi_co_rr_request = (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_rr_response = (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_rr_results = (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_co_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CO_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: requested recv alignment of %d offset %d\n", + dlpi_co_rr_request->recv_alignment, + dlpi_co_rr_request->recv_offset); + fprintf(where, + "recv_dlpi_co_rr: requested send alignment of %d offset %d\n", + dlpi_co_rr_request->send_alignment, + dlpi_co_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->recv_alignment, dlpi_co_rr_request->recv_offset); + recv_message.maxlen = dlpi_co_rr_request->request_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + send_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->send_alignment, dlpi_co_rr_request->send_offset); + send_message.maxlen = dlpi_co_rr_request->response_size; + send_message.len = dlpi_co_rr_request->response_size; + send_message.buf = send_message_ptr; + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: receive alignment and offset set...\n"); + fprintf(where,"recv_dlpi_co_rr: send_message.buf %x .len %d .maxlen %d\n", + send_message.buf,send_message.len,send_message.maxlen); + fprintf(where,"recv_dlpi_co_rr: recv_message.buf %x .len %d .maxlen %d\n", + recv_message.buf,recv_message.len,recv_message.maxlen); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: grabbing a socket...\n"); + fflush(where); + } + + /* lets grab a file descriptor for a particular link */ + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_rr_request->dlpi_device; + lastword = initword + ((dlpi_co_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + if ((data_descriptor = dl_open(dlpi_co_rr_request->dlpi_device, + dlpi_co_rr_request->ppa)) < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* bind the file descriptor to a sap and get the resultant dlsap */ + dlpi_co_rr_response->station_addr_len = 14; /*arbitrary needs fixing */ + if (dl_bind(data_descriptor, + dlpi_co_rr_request->sap, + DL_CODLS, + (char *)dlpi_co_rr_response->station_addr, + &dlpi_co_rr_response->station_addr_len) != 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* The initiator may have wished-us to modify the socket buffer */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the socket buffer by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_co_rr_request->recv_win_size) { + /* SMOP */ + } + + if (dlpi_co_rr_request->send_win_size) { + /* SMOP */ + } + + /* Now, we will find-out what the sizes actually became, and report */ + /* them back to the user. If the calls fail, we will just report a -1 */ + /* back to the initiator for the buffer size. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_co_rr_response->recv_win_size = -1; + dlpi_co_rr_response->send_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* we may have been requested to enable the copy avoidance features. */ + /* can we actually do this with DLPI, the world wonders */ + + if (dlpi_co_rr_request->so_rcvavoid) { +#ifdef SO_RCV_COPYAVOID + dlpi_co_rr_response->so_rcvavoid = 0; +#else + /* it wasn't compiled in... */ + dlpi_co_rr_response->so_rcvavoid = 0; +#endif + } + + if (dlpi_co_rr_request->so_sndavoid) { +#ifdef SO_SND_COPYAVOID + dlpi_co_rr_response->so_sndavoid = 0; +#else + /* it wasn't compiled in... */ + dlpi_co_rr_response->so_sndavoid = 0; +#endif + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + dlpi_co_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_co_rr_request->measure_cpu) { + dlpi_co_rr_response->measure_cpu = 1; + dlpi_co_rr_response->cpu_rate = calibrate_local_cpu(dlpi_co_rr_request->cpu_rate); + } + + send_response(); + + /* accept a connection on this file descriptor. at some point, */ + /* dl_accept will "do the right thing" with the last two parms, but */ + /* for now it ignores them, so we will pass zeros. */ + + if(dl_accept(data_descriptor, 0, 0) != 0) { + fprintf(where, + "recv_dlpi_co_rr: error in accept, errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_co_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (dlpi_co_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(dlpi_co_rr_request->test_length + PAD_TIME); + } +else { + times_up = 1; + trans_remaining = dlpi_co_rr_request->test_length * -1; +} + + while ((!times_up) || (trans_remaining > 0)) { + request_bytes_remaining = dlpi_co_rr_request->request_size; + + /* receive the request from the other side. there needs to be some */ + /* more login in place for handling messages larger than link mtu, */ + /* but that can wait for later */ + while(request_bytes_remaining > 0) { + if((getmsg(data_descriptor, + 0, + &recv_message, + &flags)) < 0) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + + if (debug) { + fprintf(where,"failed getmsg call errno %d\n",errno); + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= recv_message.len; + } + } + + if (timed_out) { + /* we hit the end of the test based on time - lets bail out of */ + /* here now... */ + break; + } + + if (debug) { + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + /* Now, send the response to the remote */ + if((putmsg(data_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = 994; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(dlpi_co_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + dlpi_co_rr_results->bytes_received = (trans_received * + (dlpi_co_rr_request->request_size + + dlpi_co_rr_request->response_size)); + dlpi_co_rr_results->trans_received = trans_received; + dlpi_co_rr_results->elapsed_time = elapsed_time; + if (dlpi_co_rr_request->measure_cpu) { + dlpi_co_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +/* this routine will display the usage string for the DLPI tests */ +void + print_dlpi_usage() + +{ + fwrite(dlpi_usage, sizeof(char), strlen(dlpi_usage), stdout); +} + + +/* this routine will scan the command line for DLPI test arguments */ +void + scan_dlpi_args(int argc, char *argv[]) +{ + extern int optind, opterrs; /* index of first unused arg */ + extern char *optarg; /* pointer to option string */ + + int c; + + char arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (no_control) { + fprintf(where, + "The DLPI tests do not know how to run with no control connection\n"); + exit(-1); + } + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form first, (see the routine break_args.. */ + +#define DLPI_ARGS "D:hM:m:p:r:s:W:w:" + + while ((c= getopt(argc, argv, DLPI_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_dlpi_usage(); + exit(1); + case 'D': + /* set the dlpi device file name(s) */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strcpy(loc_dlpi_device,arg1); + if (arg2[0]) + strcpy(rem_dlpi_device,arg2); + break; + case 'm': + /* set the send size */ + send_size = atoi(optarg); + break; + case 'M': + /* set the recv size */ + recv_size = atoi(optarg); + break; + case 'p': + /* set the local/remote ppa */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + loc_ppa = atoi(arg1); + if (arg2[0]) + rem_ppa = atoi(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = atoi(arg1); + if (arg2[0]) + rsp_size = atoi(arg2); + break; + case 's': + /* set the 802.2 sap for the test */ + dlpi_sap = atoi(optarg); + break; + case 'w': + /* set local window sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lsw_size = atoi(arg1); + if (arg2[0]) + lrw_size = atoi(arg2); + break; + case 'W': + /* set remote window sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rsw_size = atoi(arg1); + if (arg2[0]) + rrw_size = atoi(arg2); + break; + }; + } +} + + +#endif /* WANT_DLPI */ |