| |
| /****************************************************************/ |
| /* */ |
| /* 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,1995,2004 Hewlett-Packard Co. Version 2.4.3"; |
| |
| #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" |
| |
| /* 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"; |
| |
| |
| /* 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); |
| ffluch(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 */ |