aboutsummaryrefslogtreecommitdiff
path: root/src/nettest_sdp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nettest_sdp.c')
-rw-r--r--src/nettest_sdp.c3553
1 files changed, 3553 insertions, 0 deletions
diff --git a/src/nettest_sdp.c b/src/nettest_sdp.c
new file mode 100644
index 0000000..44ddb23
--- /dev/null
+++ b/src/nettest_sdp.c
@@ -0,0 +1,3553 @@
+#ifndef lint
+char nettest_sdp[]="\
+@(#)nettest_sdp.c (c) Copyright 2007-2012 Hewlett-Packard Co. Version 2.6.0";
+#else
+#define DIRTY
+#define WANT_HISTOGRAM
+#define WANT_INTERVALS
+#endif /* lint */
+
+/****************************************************************/
+/* */
+/* nettest_sdp.c */
+/* */
+/* */
+/* scan_sdp_args() get the sdp command line args */
+/* */
+/* the actual test routines... */
+/* */
+/* send_sdp_stream() perform a sdp stream test */
+/* recv_sdp_stream() */
+/* send_sdp_rr() perform a sdp request/response */
+/* recv_sdp_rr() */
+/* */
+/* relies on create_data_socket in nettest_bsd.c */
+/****************************************************************/
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(WANT_SDP)
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#ifdef NOSTDLIBH
+#include <malloc.h>
+#else /* NOSTDLIBH */
+#include <stdlib.h>
+#endif /* NOSTDLIBH */
+
+#if !defined(__VMS)
+#include <sys/ipc.h>
+#endif /* !defined(__VMS) */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+/* would seem that not all sdp.h files define a MSG_EOF, but that
+ MSG_EOF can be the same as MSG_FIN so lets work with that
+ assumption. initial find by Jon Pedersen. raj 2006-02-01 */
+#ifndef MSG_EOF
+#ifdef MSG_FIN
+#define MSG_EOF MSG_FIN
+#else
+#error Must have either MSG_EOF or MSG_FIN defined
+#endif
+#endif
+
+#include "netlib.h"
+#include "netsh.h"
+/* get some of the functions from nettest_bsd.c */
+#include "nettest_bsd.h"
+#include "nettest_sdp.h"
+
+#ifdef WANT_HISTOGRAM
+#ifdef __sgi
+#include <sys/time.h>
+#endif /* __sgi */
+#include "hist.h"
+#endif /* WANT_HISTOGRAM */
+
+#ifdef WANT_FIRST_BURST
+extern int first_burst_size;
+#endif /* WANT_FIRST_BURST */
+
+
+
+/* these variables are specific to SDP tests. declare */
+/* them static to make them global only to this file. */
+
+static int
+ msg_count = 0, /* number of messages to transmit on association */
+ non_block = 0, /* default to blocking sockets */
+ num_associations = 1; /* number of associations on the endpoint */
+
+static int confidence_iteration;
+static char local_cpu_method;
+static char remote_cpu_method;
+
+#ifdef WANT_HISTOGRAM
+static struct timeval time_one;
+static struct timeval time_two;
+static HIST time_hist;
+#endif /* WANT_HISTOGRAM */
+
+
+char sdp_usage[] = "\n\
+Usage: netperf [global options] -- [test options] \n\
+\n\
+SDP Sockets Test Options:\n\
+ -b number Send number requests at the start of _RR tests\n\
+ -D [L][,R] Set SDP_NODELAY locally and/or remotely\n\
+ -h Display this text\n\
+ -H name,fam Use name (or IP) and family as target of data connection\n\
+ -L name,fam Use name (or IP) and family as source of data connextion\n\
+ -m bytes Set the size of each sent message\n\
+ -M bytes Set the size of each received messages\n\
+ -P local[,remote] Set the local/remote port for the data socket\n\
+ -r req,[rsp] Set request/response sizes (_RR tests)\n\
+ -s send[,recv] Set local socket send/recv buffer sizes\n\
+ -S send[,recv] Set remote socket send/recv buffer sizes\n\
+ -V Enable copy avoidance if supported\n\
+ -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\
+ -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\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 is intended to retrieve interesting aspects of sdp */
+ /* for the data connection. at first, it attempts to retrieve the */
+ /* maximum segment size. later, it might be modified to retrieve */
+ /* other information, but it must be information that can be */
+ /* retrieved quickly as it is called during the timing of the test. */
+ /* for that reason, a second routine may be created that can be */
+ /* called outside of the timing loop */
+static
+void
+get_sdp_info(int socket, int * mss)
+{
+
+#ifdef TCP_MAXSEG
+ netperf_socklen_t sock_opt_len;
+
+ sock_opt_len = sizeof(netperf_socklen_t);
+ if (getsockopt(socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_MAXSEG,
+ (char *)mss,
+ &sock_opt_len) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: get_sdp_info: getsockopt TCP_MAXSEG: errno %d\n",
+ errno);
+ fflush(where);
+ *mss = -1;
+ }
+#else
+ *mss = -1;
+#endif /* TCP_MAXSEG */
+
+}
+
+void
+send_sdp_stream(char remote_host[])
+{
+
+ char *tput_title = "\
+Recv Send Send \n\
+Socket Socket Message Elapsed \n\
+Size Size Size Time Throughput \n\
+bytes bytes bytes secs. %s/sec \n\n";
+
+ char *tput_fmt_0 =
+ "%7.2f %s\n";
+
+ char *tput_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f %s\n";
+
+ char *cpu_title = "\
+Recv Send Send Utilization Service Demand\n\
+Socket Socket Message Elapsed Send Recv Send Recv\n\
+Size Size Size Time Throughput local remote local remote\n\
+bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f %c %s\n";
+
+ char *cpu_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\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";
+
+ char *ksink_fmt2 = "\n\
+Maximum\n\
+Segment\n\
+Size (bytes)\n\
+%6d\n";
+
+
+ float elapsed_time;
+
+ /* 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;
+
+ int len;
+ unsigned int nummessages = 0;
+ SOCKET send_socket;
+ int bytes_remaining;
+ int sdp_mss = -1; /* possibly uninitialized on printf far below */
+
+ /* with links like fddi, one can send > 32 bits worth of bytes */
+ /* during a test... ;-) at some point, this should probably become a */
+ /* 64bit integral type, but those are not entirely common yet */
+
+ unsigned long long local_bytes_sent = 0;
+ double bytes_sent = 0.0;
+
+ float local_cpu_utilization;
+ float local_service_demand;
+ float remote_cpu_utilization;
+ float remote_service_demand;
+
+ double thruput;
+
+ struct addrinfo *remote_res;
+ struct addrinfo *local_res;
+
+ struct sdp_stream_request_struct *sdp_stream_request;
+ struct sdp_stream_response_struct *sdp_stream_response;
+ struct sdp_stream_results_struct *sdp_stream_result;
+
+ sdp_stream_request =
+ (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data;
+ sdp_stream_response =
+ (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data;
+ sdp_stream_result =
+ (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ time_hist = HIST_new();
+ }
+#endif /* WANT_HISTOGRAM */
+ /* 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. */
+
+ /* complete_addrinfos will either succede or exit the process */
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("SDP STREAM TEST",local_res,remote_res);
+ }
+
+ send_ring = NULL;
+ confidence_iteration = 1;
+ init_stat();
+
+ /* we have a great-big while loop which controls the number of times */
+ /* we run a particular test. this is for the calculation of a */
+ /* confidence interval (I really should have stayed awake during */
+ /* probstats :). If the user did not request confidence measurement */
+ /* (no confidence is the default) then we will only go though the */
+ /* loop once. the confidence stuff originates from the folks at IBM */
+
+ while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
+ (confidence_iteration <= iteration_min)) {
+
+ /* initialize a few counters. we have to remember that we might be */
+ /* going through the loop more than once. */
+
+ nummessages = 0;
+ bytes_sent = 0.0;
+ times_up = 0;
+
+ /*set up the data socket */
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_sdp_stream: sdp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_sdp_stream: send_socket obtained...\n");
+ }
+
+ /* at this point, we have either retrieved the socket buffer sizes, */
+ /* or have tried to set them, so now, we may want to set the send */
+ /* size based on that (because the user either did not use a -m */
+ /* option, or used one with an argument of 0). If the socket buffer */
+ /* size is not available, we will set the send size to 4KB - no */
+ /* particular reason, just arbitrary... */
+ if (send_size == 0) {
+ if (lss_size > 0) {
+ send_size = lss_size;
+ }
+ else {
+ send_size = 4096;
+ }
+ }
+
+ /* set-up the data buffer ring with the requested alignment and offset. */
+ /* 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 = (lss_size/send_size) + 1;
+ if (send_width == 1) send_width++;
+ }
+
+ if (send_ring == NULL) {
+ /* only allocate the send ring once. this is a networking test, */
+ /* not a memory allocation test. this way, we do not need a */
+ /* deallocate_buffer_ring() routine, and I don't feel like */
+ /* writing one anyway :) raj 11/94 */
+ send_ring = allocate_buffer_ring(send_width,
+ send_size,
+ local_send_align,
+ local_send_offset);
+ }
+
+ /* 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);
+ }
+
+ if (!no_control) {
+ /* 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 1, which
+ will be no alignment alterations. */
+
+ netperf_request.content.request_type = DO_SDP_STREAM;
+ sdp_stream_request->send_buf_size = rss_size_req;
+ sdp_stream_request->recv_buf_size = rsr_size_req;
+ sdp_stream_request->receive_size = recv_size;
+ sdp_stream_request->no_delay = rem_nodelay;
+ sdp_stream_request->recv_alignment = remote_recv_align;
+ sdp_stream_request->recv_offset = remote_recv_offset;
+ sdp_stream_request->measure_cpu = remote_cpu_usage;
+ sdp_stream_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ sdp_stream_request->test_length = test_time;
+ }
+ else {
+ sdp_stream_request->test_length = test_bytes;
+ }
+ sdp_stream_request->so_rcvavoid = rem_rcvavoid;
+ sdp_stream_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ sdp_stream_request->dirty_count = rem_dirty_count;
+ sdp_stream_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ sdp_stream_request->port = atoi(remote_data_port);
+ sdp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_sdp_stream: requesting SDP 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 SDP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = sdp_stream_response->recv_buf_size;
+ rss_size = sdp_stream_response->send_buf_size;
+ rem_nodelay = sdp_stream_response->no_delay;
+ remote_cpu_usage= sdp_stream_response->measure_cpu;
+ remote_cpu_rate = sdp_stream_response->cpu_rate;
+
+ /* we have to make sure that the server port number is in
+ network order */
+ set_port_number(remote_res,
+ (short)sdp_stream_response->data_port_number);
+
+ rem_rcvavoid = sdp_stream_response->so_rcvavoid;
+ rem_sndavoid = sdp_stream_response->so_sndavoid;
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+
+ exit(1);
+ }
+ }
+
+#ifdef WANT_DEMO
+ DEMO_STREAM_SETUP(lss_size,rsr_size)
+#endif
+
+ /*Connect up to the remote port on the data socket */
+ if (connect(send_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen) == INVALID_SOCKET){
+ perror("netperf: send_sdp_stream: data socket connect failed");
+ 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;
+ /* in previous revisions, we had the same code repeated throught */
+ /* all the test suites. this was unnecessary, and meant more */
+ /* work for me when I wanted to switch to POSIX signals, so I */
+ /* have abstracted this out into a routine in netlib.c. if you */
+ /* are experiencing signal problems, you might want to look */
+ /* there. raj 11/94 */
+ 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 only start the interval timer if we are using the
+ timer-timed intervals rather than the sit and spin ones. raj
+ 2006-02-06 */
+#if defined(WANT_INTERVALS)
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+ /* before we start, initialize a few variables */
+
+#ifdef WANT_DEMO
+ if (demo_mode) {
+ HIST_timestamp(demo_one_ptr);
+ }
+#endif
+
+
+ /* 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. */
+
+ while ((!times_up) || (bytes_remaining > 0)) {
+
+#ifdef DIRTY
+ access_buffer(send_ring->buffer_ptr,
+ send_size,
+ loc_dirty_count,
+ loc_clean_count);
+#endif /* DIRTY */
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before we go into send and then again just
+ after we come out raj 8/94 */
+ /* but lets only do this if there is going to be a histogram
+ displayed */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ if((len=send(send_socket,
+ send_ring->buffer_ptr,
+ send_size,
+ 0)) != send_size) {
+ if ((len >=0) || SOCKET_EINTR(len)) {
+ /* the test was interrupted, must be the end of test */
+ break;
+ }
+ perror("netperf: data send error");
+ printf("len was %d\n",len);
+ exit(1);
+ }
+
+ local_bytes_sent += send_size;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp the exit from the send call and update the histogram */
+ HIST_timestamp(&time_two);
+ HIST_add(time_hist,delta_micro(&time_one,&time_two));
+ }
+#endif /* WANT_HISTOGRAM */
+
+#ifdef WANT_DEMO
+ DEMO_STREAM_INTERVAL(send_size)
+#endif
+
+#if defined(WANT_INTERVALS)
+ INTERVALS_WAIT();
+#endif /* WANT_INTERVALS */
+
+ /* now we want to move our pointer to the next position in the */
+ /* data buffer...we may also want to wrap back to the "beginning" */
+ /* of the bufferspace, so we will mod the number of messages sent */
+ /* by the send width, and use that to calculate the offset to add */
+ /* to the base pointer. */
+ nummessages++;
+ send_ring = send_ring->next;
+ 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. */
+
+ /* but first, if the verbosity is greater than 1, find-out what */
+ /* the SDP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ sdp_mss = -1;
+ get_sdp_info(send_socket,&sdp_mss);
+ }
+
+ if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) {
+ perror("netperf: cannot shutdown sdp stream socket");
+ exit(1);
+ }
+
+ /* hang a recv() off the socket to block until the remote has */
+ /* brought all the data up into the application. it will do a */
+ /* shutdown to cause a FIN to be sent our way. We will assume that */
+ /* any exit from the recv() call is good... raj 4/93 */
+
+ recv(send_socket, send_ring->buffer_ptr, send_size, 0);
+
+ /* 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 and how */
+ /* long did we really */
+ /* run? */
+
+ /* we are finished with the socket, so close it to prevent hitting */
+ /* the limit on maximum open files. */
+
+ close(send_socket);
+
+ if (!no_control) {
+ /* 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);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+
+ 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
+ SDP 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 = ntohd(sdp_stream_result->bytes_received);
+ }
+ else {
+ bytes_sent = (double)local_bytes_sent;
+ }
+
+ 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) {
+
+ 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 = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+
+ remote_cpu_utilization = sdp_stream_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ sdp_stream_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+ }
+ else {
+ /* we were not measuring cpu, for the confidence stuff, we */
+ /* should make it -1.0 */
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* at this point, we want to calculate the confidence information. */
+ /* if debugging is on, calculate_confidence will print-out the */
+ /* parameters we pass it */
+
+ calculate_confidence(confidence_iteration,
+ elapsed_time,
+ thruput,
+ local_cpu_utilization,
+ remote_cpu_utilization,
+ local_service_demand,
+ remote_service_demand);
+
+
+ confidence_iteration++;
+ }
+
+ /* at this point, we have finished making all the runs that we */
+ /* will be making. so, we should extract what the calcuated values */
+ /* are for all the confidence stuff. we could make the values */
+ /* global, but that seemed a little messy, and it did not seem worth */
+ /* all the mucking with header files. so, we create a routine much */
+ /* like calcualte_confidence, which just returns the mean values. */
+ /* raj 11/94 */
+
+ retrieve_confident_values(&elapsed_time,
+ &thruput,
+ &local_cpu_utilization,
+ &remote_cpu_utilization,
+ &local_service_demand,
+ &remote_service_demand);
+
+ /* 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... */
+
+ if (confidence < 0) {
+ /* we did not hit confidence, but were we asked to look for it? */
+ if (iteration_max > 1) {
+ display_confidence();
+ }
+ }
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ local_cpu_method = format_cpu_method(cpu_method);
+ remote_cpu_method = format_cpu_method(sdp_stream_result->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ remote_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,
+ cpu_title,
+ format_units(),
+ local_cpu_method,
+ remote_cpu_method);
+ }
+
+ fprintf(where,
+ cpu_fmt_1, /* the format string */
+ rsr_size, /* remote recvbuf size */
+ lss_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 */
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ thruput,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,tput_title,format_units());
+ }
+ fprintf(where,
+ tput_fmt_1, /* the format string */
+ rsr_size, /* remote recvbuf size */
+ lss_size, /* local sendbuf size */
+ send_size, /* how large were the sends */
+ elapsed_time, /* how long did it take */
+ thruput, /* how fast did it go */
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ 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 */
+ /* SDP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ /* this stuff needs to be worked-out in the presence of confidence */
+ /* intervals and multiple iterations of the test... raj 11/94 */
+
+ 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)sdp_stream_result->recv_calls,
+ sdp_stream_result->recv_calls);
+ fprintf(where,
+ ksink_fmt2,
+ sdp_mss);
+ fflush(where);
+#ifdef WANT_HISTOGRAM
+ fprintf(where,"\n\nHistogram of time spent in send() call.\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+ }
+
+}
+
+
+
+/* This routine implements the netperf-side SDP unidirectional data
+ transfer test (a.k.a. stream) for the sockets interface where the
+ data flow is from the netserver to the netperf. It receives its
+ parameters via global variables from the shell and writes its
+ output to the standard output. */
+
+
+void
+send_sdp_maerts(char remote_host[])
+{
+
+ char *tput_title = "\
+Recv Send Send \n\
+Socket Socket Message Elapsed \n\
+Size Size Size Time Throughput \n\
+bytes bytes bytes secs. %s/sec \n\n";
+
+ char *tput_fmt_0 =
+ "%7.2f %s\n";
+
+ char *tput_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f \n %s";
+
+ char *cpu_title = "\
+Recv Send Send Utilization Service Demand\n\
+Socket Socket Message Elapsed Send Recv Send Recv\n\
+Size Size Size Time Throughput local remote local remote\n\
+bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f %c %s\n";
+
+ char *cpu_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n";
+
+ char *ksink_fmt = "\n\
+Alignment Offset %-8.8s %-8.8s Recvs %-8.8s Sends\n\
+Local Remote Local Remote Xfered Per Per\n\
+Recv Send Recv Send Recv (avg) Send (avg)\n\
+%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n";
+
+ char *ksink_fmt2 = "\n\
+Maximum\n\
+Segment\n\
+Size (bytes)\n\
+%6d\n";
+
+
+ float elapsed_time;
+
+ /* what we want is to have a buffer space that is at least one */
+ /* recv-size greater than our recv 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 *recv_ring;
+
+ int len;
+ unsigned int nummessages = 0;
+ SOCKET recv_socket;
+ int bytes_remaining;
+ int sdp_mss = -1; /* possibly uninitialized on printf far below */
+
+ /* with links like fddi, one can recv > 32 bits worth of bytes */
+ /* during a test... ;-) at some point, this should probably become a */
+ /* 64bit integral type, but those are not entirely common yet */
+ double bytes_sent = 0.0;
+ unsigned long long local_bytes_recvd = 0;
+
+ float local_cpu_utilization;
+ float local_service_demand;
+ float remote_cpu_utilization;
+ float remote_service_demand;
+
+ double thruput;
+
+ struct addrinfo *remote_res;
+ struct addrinfo *local_res;
+
+ struct sdp_maerts_request_struct *sdp_maerts_request;
+ struct sdp_maerts_response_struct *sdp_maerts_response;
+ struct sdp_maerts_results_struct *sdp_maerts_result;
+
+ sdp_maerts_request =
+ (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data;
+ sdp_maerts_response =
+ (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data;
+ sdp_maerts_result =
+ (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ time_hist = HIST_new();
+ }
+#endif /* WANT_HISTOGRAM */
+ /* 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. */
+
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("SDP MAERTS TEST",local_res,remote_res);
+ }
+
+ recv_ring = NULL;
+ confidence_iteration = 1;
+ init_stat();
+
+ /* we have a great-big while loop which controls the number of times */
+ /* we run a particular test. this is for the calculation of a */
+ /* confidence interval (I really should have stayed awake during */
+ /* probstats :). If the user did not request confidence measurement */
+ /* (no confidence is the default) then we will only go though the */
+ /* loop once. the confidence stuff originates from the folks at IBM */
+
+ while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
+ (confidence_iteration <= iteration_min)) {
+
+ /* initialize a few counters. we have to remember that we might be */
+ /* going through the loop more than once. */
+
+ nummessages = 0;
+ bytes_sent = 0.0;
+ times_up = 0;
+
+ /*set up the data socket */
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ recv_socket = create_data_socket(local_res);
+
+ if (recv_socket == INVALID_SOCKET){
+ perror("netperf: send_sdp_maerts: sdp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_sdp_maerts: recv_socket obtained...\n");
+ }
+
+ /* at this point, we have either retrieved the socket buffer sizes, */
+ /* or have tried to set them, so now, we may want to set the recv */
+ /* size based on that (because the user either did not use a -m */
+ /* option, or used one with an argument of 0). If the socket buffer */
+ /* size is not available, we will set the recv size to 4KB - no */
+ /* particular reason, just arbitrary... */
+ if (recv_size == 0) {
+ if (lsr_size > 0) {
+ recv_size = lsr_size;
+ }
+ else {
+ recv_size = 4096;
+ }
+ }
+
+ /* set-up the data buffer ring with the requested alignment and offset. */
+ /* note also that we have allocated a quantity */
+ /* of memory that is at least one recv-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 */
+ /* recv_size is bigger than the socket size, so we must check... the */
+ /* user may have wanted to explicitly set the "width" of our recv */
+ /* buffers, we should respect that wish... */
+ if (recv_width == 0) {
+ recv_width = (lsr_size/recv_size) + 1;
+ if (recv_width == 1) recv_width++;
+ }
+
+ if (recv_ring == NULL) {
+ /* only allocate the recv ring once. this is a networking test, */
+ /* not a memory allocation test. this way, we do not need a */
+ /* deallocate_buffer_ring() routine, and I don't feel like */
+ /* writing one anyway :) raj 11/94 */
+ recv_ring = allocate_buffer_ring(recv_width,
+ recv_size,
+ local_recv_align,
+ local_recv_offset);
+ }
+
+ /* 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);
+ }
+
+ if (!no_control) {
+ /* 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 1, which
+ will be no alignment alterations. */
+
+ netperf_request.content.request_type = DO_SDP_MAERTS;
+ sdp_maerts_request->send_buf_size = rss_size_req;
+ sdp_maerts_request->recv_buf_size = rsr_size_req;
+ sdp_maerts_request->send_size = send_size;
+ sdp_maerts_request->no_delay = rem_nodelay;
+ sdp_maerts_request->send_alignment = remote_send_align;
+ sdp_maerts_request->send_offset = remote_send_offset;
+ sdp_maerts_request->measure_cpu = remote_cpu_usage;
+ sdp_maerts_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ sdp_maerts_request->test_length = test_time;
+ }
+ else {
+ sdp_maerts_request->test_length = test_bytes;
+ }
+ sdp_maerts_request->so_rcvavoid = rem_rcvavoid;
+ sdp_maerts_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ sdp_maerts_request->dirty_count = rem_dirty_count;
+ sdp_maerts_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ sdp_maerts_request->port = atoi(remote_data_port);
+ sdp_maerts_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_sdp_maerts: requesting SDP maerts 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 SDP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = sdp_maerts_response->recv_buf_size;
+ rss_size = sdp_maerts_response->send_buf_size;
+ rem_nodelay = sdp_maerts_response->no_delay;
+ remote_cpu_usage= sdp_maerts_response->measure_cpu;
+ remote_cpu_rate = sdp_maerts_response->cpu_rate;
+ send_size = sdp_maerts_response->send_size;
+
+ /* we have to make sure that the server port number is in
+ network order */
+ set_port_number(remote_res,
+ (short)sdp_maerts_response->data_port_number);
+ rem_rcvavoid = sdp_maerts_response->so_rcvavoid;
+ rem_sndavoid = sdp_maerts_response->so_sndavoid;
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+
+ exit(1);
+ }
+ }
+
+#ifdef WANT_DEMO
+ DEMO_STREAM_SETUP(lsr_size,rss_size)
+#endif
+
+ /*Connect up to the remote port on the data socket */
+ if (connect(recv_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen) == INVALID_SOCKET){
+ perror("netperf: send_sdp_maerts: data socket connect failed");
+ 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 maerts 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;
+ /* in previous revisions, we had the same code repeated throught */
+ /* all the test suites. this was unnecessary, and meant more */
+ /* work for me when I wanted to switch to POSIX signals, so I */
+ /* have abstracted this out into a routine in netlib.c. if you */
+ /* are experiencing signal problems, you might want to look */
+ /* there. raj 11/94 */
+ if (!no_control) {
+ /* this is a netperf to netserver test, netserver will close
+ to tell us the test is over, so use PAD_TIME to avoid
+ causing the netserver fits. */
+ start_timer(test_time + PAD_TIME);
+ }
+ else {
+ /* this is a netperf to data source test, no PAD_TIME */
+ start_timer(test_time);
+ }
+ }
+ else {
+ /* The tester wanted to recv a number of bytes. we don't do that
+ in a SDP_MAERTS test. sorry. raj 2002-06-21 */
+ printf("netperf: send_sdp_maerts: test must be timed\n");
+ exit(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);
+
+#ifdef WANT_INTERVALS
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+ /* before we start, initialize a few variables */
+
+#ifdef WANT_DEMO
+ if (demo_mode) {
+ HIST_timestamp(demo_one_ptr);
+ }
+#endif
+
+ /* the test will continue until we either get a zero-byte recv()
+ on the socket or our failsafe timer expires. most of the time
+ we trust that we get a zero-byte recieve from the socket. raj
+ 2002-06-21 */
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before we go into recv and then again just
+ after we come out raj 8/94 */
+ /* but only if we are actually going to display a histogram. raj
+ 2006-02-07 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ while ((!times_up) && (len=recv(recv_socket,
+ recv_ring->buffer_ptr,
+ recv_size,
+ 0)) > 0 ) {
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp the exit from the recv call and update the histogram */
+ HIST_timestamp(&time_two);
+ HIST_add(time_hist,delta_micro(&time_one,&time_two));
+ }
+#endif /* WANT_HISTOGRAM */
+
+#ifdef DIRTY
+ access_buffer(recv_ring->buffer_ptr,
+ recv_size,
+ loc_dirty_count,
+ loc_clean_count);
+#endif /* DIRTY */
+
+#ifdef WANT_DEMO
+ DEMO_STREAM_INTERVAL(len);
+#endif
+
+#ifdef WANT_INTERVALS
+ INTERVALS_WAIT();
+#endif /* WANT_INTERVALS */
+
+ /* now we want to move our pointer to the next position in the */
+ /* data buffer...we may also want to wrap back to the "beginning" */
+ /* of the bufferspace, so we will mod the number of messages sent */
+ /* by the recv width, and use that to calculate the offset to add */
+ /* to the base pointer. */
+ nummessages++;
+ recv_ring = recv_ring->next;
+ if (bytes_remaining) {
+ bytes_remaining -= len;
+ }
+
+ local_bytes_recvd += len;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* make sure we timestamp just before we go into recv */
+ /* raj 2004-06-15 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ }
+
+ /* an EINTR is to be expected when this is a no_control test */
+ if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) {
+ perror("send_sdp_maerts: data recv error");
+ printf("len was %d\n",len);
+ exit(1);
+ }
+
+ /* if we get here, it must mean we had a recv return of 0 before
+ the watchdog timer expired, or the watchdog timer expired and
+ this was a no_control test */
+
+ /* The test is over. Flush the buffers to the remote end. We do a
+ graceful release to tell the remote we have all the data. */
+
+ /* but first, if the verbosity is greater than 1, find-out what */
+ /* the SDP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ sdp_mss = -1;
+ get_sdp_info(recv_socket,&sdp_mss);
+ }
+
+ if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) {
+ perror("netperf: cannot shutdown sdp maerts socket");
+ exit(1);
+ }
+
+ stop_timer();
+
+ /* this call will always give us the local 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 and how */
+ /* long did we really */
+ /* run? */
+
+ /* we are finished with the socket, so close it to prevent hitting */
+ /* the limit on maximum open files. */
+
+ close(recv_socket);
+
+ if (!no_control) {
+ /* 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);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+
+ 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
+ SDP maerts 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 recv_size, so we
+ really didn't recv what he asked for ;-) */
+
+ bytes_sent = ntohd(sdp_maerts_result->bytes_sent);
+ }
+ else {
+ bytes_sent = (double)local_bytes_recvd;
+ }
+
+
+ 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) {
+
+ 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 = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+
+ remote_cpu_utilization = sdp_maerts_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ sdp_maerts_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+ }
+ else {
+ /* we were not measuring cpu, for the confidence stuff, we */
+ /* should make it -1.0 */
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* at this point, we want to calculate the confidence information. */
+ /* if debugging is on, calculate_confidence will print-out the */
+ /* parameters we pass it */
+
+ calculate_confidence(confidence_iteration,
+ elapsed_time,
+ thruput,
+ local_cpu_utilization,
+ remote_cpu_utilization,
+ local_service_demand,
+ remote_service_demand);
+
+
+ confidence_iteration++;
+ }
+
+ /* at this point, we have finished making all the runs that we */
+ /* will be making. so, we should extract what the calcuated values */
+ /* are for all the confidence stuff. we could make the values */
+ /* global, but that seemed a little messy, and it did not seem worth */
+ /* all the mucking with header files. so, we create a routine much */
+ /* like calcualte_confidence, which just returns the mean values. */
+ /* raj 11/94 */
+
+ retrieve_confident_values(&elapsed_time,
+ &thruput,
+ &local_cpu_utilization,
+ &remote_cpu_utilization,
+ &local_service_demand,
+ &remote_service_demand);
+
+ /* 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... */
+
+ if (confidence < 0) {
+ /* we did not hit confidence, but were we asked to look for it? */
+ if (iteration_max > 1) {
+ display_confidence();
+ }
+ }
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ local_cpu_method = format_cpu_method(cpu_method);
+ remote_cpu_method = format_cpu_method(sdp_maerts_result->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ remote_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,
+ cpu_title,
+ format_units(),
+ local_cpu_method,
+ remote_cpu_method);
+ }
+
+ fprintf(where,
+ cpu_fmt_1, /* the format string */
+ rsr_size, /* remote recvbuf size */
+ lss_size, /* local sendbuf size */
+ send_size, /* how large were the recvs */
+ 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 */
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ thruput,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,tput_title,format_units());
+ }
+ fprintf(where,
+ tput_fmt_1, /* the format string */
+ lsr_size, /* local recvbuf size */
+ rss_size, /* remot sendbuf size */
+ send_size, /* how large were the recvs */
+ elapsed_time, /* how long did it take */
+ thruput, /* how fast did it go */
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ 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 */
+ /* SDP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ /* this stuff needs to be worked-out in the presence of confidence */
+ /* intervals and multiple iterations of the test... raj 11/94 */
+
+ fprintf(where,
+ ksink_fmt,
+ "Bytes",
+ "Bytes",
+ "Bytes",
+ local_recv_align,
+ remote_recv_align,
+ local_recv_offset,
+ remote_recv_offset,
+ bytes_sent,
+ bytes_sent / (double)nummessages,
+ nummessages,
+ bytes_sent / (double)sdp_maerts_result->send_calls,
+ sdp_maerts_result->send_calls);
+ fprintf(where,
+ ksink_fmt2,
+ sdp_mss);
+ fflush(where);
+#ifdef WANT_HISTOGRAM
+ fprintf(where,"\n\nHistogram of time spent in recv() call.\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+ }
+
+}
+/* This is the server-side routine for the sdp stream test. It is */
+/* implemented as one routine. I could break things-out somewhat, but */
+/* didn't feel it was necessary. */
+
+void
+recv_sdp_stream()
+{
+
+ struct sockaddr_in myaddr_in, peeraddr_in;
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ int len;
+ unsigned int receive_calls;
+ float elapsed_time;
+ double bytes_received;
+
+ struct ring_elt *recv_ring;
+
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+#ifdef DO_SELECT
+ fd_set readfds;
+ struct timeval timeout;
+#endif /* DO_SELECT */
+
+ struct sdp_stream_request_struct *sdp_stream_request;
+ struct sdp_stream_response_struct *sdp_stream_response;
+ struct sdp_stream_results_struct *sdp_stream_results;
+
+#ifdef DO_SELECT
+ FD_ZERO(&readfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+#endif /* DO_SELECT */
+
+ sdp_stream_request =
+ (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data;
+ sdp_stream_response =
+ (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data;
+ sdp_stream_results =
+ (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_sdp_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. */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_stream: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = SDP_STREAM_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_sdp_stream: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* We now alter the message_ptr variable to be at the desired */
+ /* alignment with the desired offset. */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_stream: requested alignment of %d\n",
+ sdp_stream_request->recv_alignment);
+ fflush(where);
+ }
+
+ /* create_data_socket expects to find some things in the global */
+ /* variables, so set the globals based on the values in the request. */
+ /* once the socket has been created, we will set the response values */
+ /* based on the updated value of those globals. raj 7/94 */
+ lss_size_req = sdp_stream_request->send_buf_size;
+ lsr_size_req = sdp_stream_request->recv_buf_size;
+ loc_nodelay = sdp_stream_request->no_delay;
+ loc_rcvavoid = sdp_stream_request->so_rcvavoid;
+ loc_sndavoid = sdp_stream_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(sdp_stream_request->ipfamily),
+ sdp_stream_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(sdp_stream_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+#ifdef WIN32
+ /* The test timer can fire during operations on the listening socket,
+ so to make the start_timer below work we have to move
+ it to close s_listen while we are blocked on accept. */
+ win_kludge_socket2 = s_listen;
+#endif
+
+ /* what sort of sizes did we end-up with? */
+ if (sdp_stream_request->receive_size == 0) {
+ if (lsr_size > 0) {
+ recv_size = lsr_size;
+ }
+ else {
+ recv_size = 4096;
+ }
+ }
+ else {
+ recv_size = sdp_stream_request->receive_size;
+ }
+
+ /* we want to set-up our recv_ring in a manner analagous to what we */
+ /* do on the sending side. this is more for the sake of symmetry */
+ /* than for the needs of say copy avoidance, but it might also be */
+ /* more realistic - this way one could conceivably go with a */
+ /* double-buffering scheme when taking the data an putting it into */
+ /* the filesystem or something like that. raj 7/94 */
+
+ if (recv_width == 0) {
+ recv_width = (lsr_size/recv_size) + 1;
+ if (recv_width == 1) recv_width++;
+ }
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ recv_size,
+ sdp_stream_request->recv_alignment,
+ sdp_stream_request->recv_offset);
+
+ if (debug) {
+ fprintf(where,"recv_sdp_stream: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Now, let's set-up the socket to listen for connections */
+ if (listen(s_listen, 5) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+
+ /* now get the port number assigned by the system */
+ addrlen = sizeof(myaddr_in);
+ if (getsockname(s_listen,
+ (struct sockaddr *)&myaddr_in,
+ &addrlen) == SOCKET_ERROR){
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+ /* Now myaddr_in contains the port and the internet address this is */
+ /* returned to the sender also implicitly telling the sender that the */
+ /* socket buffer sizing has been done. */
+
+ sdp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
+ 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. */
+
+ sdp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (sdp_stream_request->measure_cpu) {
+ sdp_stream_response->measure_cpu = 1;
+ sdp_stream_response->cpu_rate =
+ calibrate_local_cpu(sdp_stream_request->cpu_rate);
+ }
+ else {
+ sdp_stream_response->measure_cpu = 0;
+ }
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ sdp_stream_response->send_buf_size = lss_size;
+ sdp_stream_response->recv_buf_size = lsr_size;
+ sdp_stream_response->no_delay = loc_nodelay;
+ sdp_stream_response->so_rcvavoid = loc_rcvavoid;
+ sdp_stream_response->so_sndavoid = loc_sndavoid;
+ sdp_stream_response->receive_size = recv_size;
+
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ if ((s_data=accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ /* Let's just punt. The remote will be given some information */
+ close(s_listen);
+ exit(1);
+ }
+
+#ifdef KLUDGE_SOCKET_OPTIONS
+ /* this is for those systems which *INCORRECTLY* fail to pass */
+ /* attributes across an accept() call. Including this goes against */
+ /* my better judgement :( raj 11/95 */
+
+ kludge_socket_options(s_data);
+
+#endif /* KLUDGE_SOCKET_OPTIONS */
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(sdp_stream_request->measure_cpu);
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ /* there used to be an #ifdef DIRTY call to access_buffer() here,
+ but we have switched from accessing the buffer before the recv()
+ call to accessing the buffer after the recv() call. The
+ accessing before was, IIRC, related to having dirty data when
+ doing page-flipping copy avoidance. */
+
+ bytes_received = 0;
+ receive_calls = 0;
+
+ while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) {
+ if (len == SOCKET_ERROR )
+ {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ bytes_received += len;
+ receive_calls++;
+
+#ifdef DIRTY
+ /* we access the buffer after the recv() call now, rather than before */
+ access_buffer(recv_ring->buffer_ptr,
+ recv_size,
+ sdp_stream_request->dirty_count,
+ sdp_stream_request->clean_count);
+#endif /* DIRTY */
+
+
+ /* move to the next buffer in the recv_ring */
+ recv_ring = recv_ring->next;
+
+#ifdef PAUSE
+ sleep(1);
+#endif /* PAUSE */
+
+#ifdef DO_SELECT
+ FD_SET(s_data,&readfds);
+ select(s_data+1,&readfds,NULL,NULL,&timeout);
+#endif /* DO_SELECT */
+
+ }
+
+ /* perform a shutdown to signal the sender that */
+ /* we have received all the data sent. raj 4/93 */
+
+ if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+ cpu_stop(sdp_stream_request->measure_cpu,&elapsed_time);
+
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_sdp_stream: got %g bytes\n",
+ bytes_received);
+ fprintf(where,
+ "recv_sdp_stream: got %d recvs\n",
+ receive_calls);
+ fflush(where);
+ }
+
+ sdp_stream_results->bytes_received = htond(bytes_received);
+ sdp_stream_results->elapsed_time = elapsed_time;
+ sdp_stream_results->recv_calls = receive_calls;
+
+ sdp_stream_results->cpu_method = cpu_method;
+ sdp_stream_results->num_cpus = lib_num_loc_cpus;
+
+ if (sdp_stream_request->measure_cpu) {
+ sdp_stream_results->cpu_util = calc_cpu_util(0.0);
+ };
+
+ if (debug) {
+ fprintf(where,
+ "recv_sdp_stream: test complete, sending results.\n");
+ fprintf(where,
+ " bytes_received %g receive_calls %d\n",
+ bytes_received,
+ receive_calls);
+ fprintf(where,
+ " len %d\n",
+ len);
+ fflush(where);
+ }
+
+ send_response();
+
+ /* we are now done with the sockets */
+ close(s_data);
+ close(s_listen);
+
+ }
+
+/* This is the server-side routine for the sdp maerts test. It is
+ implemented as one routine. I could break things-out somewhat, but
+ didn't feel it was necessary. */
+
+void
+recv_sdp_maerts()
+{
+
+ struct sockaddr_in myaddr_in, peeraddr_in;
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ int len;
+ unsigned int send_calls;
+ float elapsed_time;
+ double bytes_sent = 0.0 ;
+
+ struct ring_elt *send_ring;
+
+ struct sdp_maerts_request_struct *sdp_maerts_request;
+ struct sdp_maerts_response_struct *sdp_maerts_response;
+ struct sdp_maerts_results_struct *sdp_maerts_results;
+
+ sdp_maerts_request =
+ (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data;
+ sdp_maerts_response =
+ (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data;
+ sdp_maerts_results =
+ (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_sdp_maerts: 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_sdp_maerts: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = SDP_MAERTS_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_sdp_maerts: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* We now alter the message_ptr variable to be at the desired */
+ /* alignment with the desired offset. */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_maerts: requested alignment of %d\n",
+ sdp_maerts_request->send_alignment);
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_maerts: grabbing a socket...\n");
+ fflush(where);
+ }
+
+ /* create_data_socket expects to find some things in the global */
+ /* variables, so set the globals based on the values in the request. */
+ /* once the socket has been created, we will set the response values */
+ /* based on the updated value of those globals. raj 7/94 */
+ lss_size_req = sdp_maerts_request->send_buf_size;
+ lsr_size_req = sdp_maerts_request->recv_buf_size;
+ loc_nodelay = sdp_maerts_request->no_delay;
+ loc_rcvavoid = sdp_maerts_request->so_rcvavoid;
+ loc_sndavoid = sdp_maerts_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(sdp_maerts_request->ipfamily),
+ sdp_maerts_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(sdp_maerts_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+#ifdef WIN32
+ /* The test timer can fire during operations on the listening socket,
+ so to make the start_timer below work we have to move
+ it to close s_listen while we are blocked on accept. */
+ win_kludge_socket2 = s_listen;
+#endif
+
+
+ /* what sort of sizes did we end-up with? */
+ if (sdp_maerts_request->send_size == 0) {
+ if (lss_size > 0) {
+ send_size = lss_size;
+ }
+ else {
+ send_size = 4096;
+ }
+ }
+ else {
+ send_size = sdp_maerts_request->send_size;
+ }
+
+ /* we want to set-up our recv_ring in a manner analagous to what we */
+ /* do on the recving side. this is more for the sake of symmetry */
+ /* than for the needs of say copy avoidance, but it might also be */
+ /* more realistic - this way one could conceivably go with a */
+ /* double-buffering scheme when taking the data an putting it into */
+ /* the filesystem or something like that. raj 7/94 */
+
+ if (send_width == 0) {
+ send_width = (lsr_size/send_size) + 1;
+ if (send_width == 1) send_width++;
+ }
+
+ send_ring = allocate_buffer_ring(send_width,
+ send_size,
+ sdp_maerts_request->send_alignment,
+ sdp_maerts_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_sdp_maerts: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Now, let's set-up the socket to listen for connections */
+ if (listen(s_listen, 5) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+
+ /* now get the port number assigned by the system */
+ addrlen = sizeof(myaddr_in);
+ if (getsockname(s_listen,
+ (struct sockaddr *)&myaddr_in,
+ &addrlen) == SOCKET_ERROR){
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+ /* Now myaddr_in contains the port and the internet address this is */
+ /* returned to the sender also implicitly telling the sender that the */
+ /* socket buffer sizing has been done. */
+
+ sdp_maerts_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
+ 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. */
+
+ sdp_maerts_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (sdp_maerts_request->measure_cpu) {
+ sdp_maerts_response->measure_cpu = 1;
+ sdp_maerts_response->cpu_rate =
+ calibrate_local_cpu(sdp_maerts_request->cpu_rate);
+ }
+ else {
+ sdp_maerts_response->measure_cpu = 0;
+ }
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ sdp_maerts_response->send_buf_size = lss_size;
+ sdp_maerts_response->recv_buf_size = lsr_size;
+ sdp_maerts_response->no_delay = loc_nodelay;
+ sdp_maerts_response->so_rcvavoid = loc_rcvavoid;
+ sdp_maerts_response->so_sndavoid = loc_sndavoid;
+ sdp_maerts_response->send_size = send_size;
+
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ /* we will start the timer before the accept() to be somewhat
+ analagous to the starting of the timer before the connect() call
+ in the SDP_STREAM test. raj 2002-06-21 */
+
+ start_timer(sdp_maerts_request->test_length);
+
+ /* Now it's time to start receiving data on the connection. We will
+ first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(sdp_maerts_request->measure_cpu);
+
+
+ if ((s_data=accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ /* Let's just punt. The remote will be given some information */
+ close(s_listen);
+ exit(1);
+ }
+
+#ifdef KLUDGE_SOCKET_OPTIONS
+
+ /* this is for those systems which *INCORRECTLY* fail to pass
+ attributes across an accept() call. Including this goes against
+ my better judgement :( raj 11/95 */
+
+ kludge_socket_options(s_data);
+
+#endif /* KLUDGE_SOCKET_OPTIONS */
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ bytes_sent = 0.0;
+ send_calls = 0;
+
+ len = 0; /* nt-lint; len is not initialized (printf far below) if
+ times_up initially true.*/
+ times_up = 0; /* must remember to initialize this little beauty */
+ 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. */
+
+ access_buffer(send_ring->buffer_ptr,
+ send_size,
+ sdp_maerts_request->dirty_count,
+ sdp_maerts_request->clean_count);
+
+#endif /* DIRTY */
+
+ if((len=send(s_data,
+ send_ring->buffer_ptr,
+ send_size,
+ 0)) != send_size) {
+ if ((len >=0) || SOCKET_EINTR(len)) {
+ /* the test was interrupted, must be the end of test */
+ break;
+ }
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+ bytes_sent += len;
+ send_calls++;
+
+ /* more to the next buffer in the send_ring */
+ send_ring = send_ring->next;
+
+ }
+
+ /* perform a shutdown to signal the sender that */
+ /* we have received all the data sent. raj 4/93 */
+
+ if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+ /* hang a recv() off the socket to block until the remote has
+ brought all the data up into the application. it will do a
+ shutdown to cause a FIN to be sent our way. We will assume that
+ any exit from the recv() call is good... raj 4/93 */
+
+ recv(s_data, send_ring->buffer_ptr, send_size, 0);
+
+
+ cpu_stop(sdp_maerts_request->measure_cpu,&elapsed_time);
+
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_sdp_maerts: got %g bytes\n",
+ bytes_sent);
+ fprintf(where,
+ "recv_sdp_maerts: got %d sends\n",
+ send_calls);
+ fflush(where);
+ }
+
+ sdp_maerts_results->bytes_sent = htond(bytes_sent);
+ sdp_maerts_results->elapsed_time = elapsed_time;
+ sdp_maerts_results->send_calls = send_calls;
+
+ if (sdp_maerts_request->measure_cpu) {
+ sdp_maerts_results->cpu_util = calc_cpu_util(0.0);
+ };
+
+ if (debug) {
+ fprintf(where,
+ "recv_sdp_maerts: test complete, sending results.\n");
+ fprintf(where,
+ " bytes_sent %g send_calls %d\n",
+ bytes_sent,
+ send_calls);
+ fprintf(where,
+ " len %d\n",
+ len);
+ fflush(where);
+ }
+
+ sdp_maerts_results->cpu_method = cpu_method;
+ sdp_maerts_results->num_cpus = lib_num_loc_cpus;
+ send_response();
+
+ /* we are now done with the sockets */
+ close(s_data);
+ close(s_listen);
+
+ }
+
+
+ /* this routine implements the sending (netperf) side of the SDP_RR */
+ /* test. */
+
+void
+send_sdp_rr(char remote_host[])
+{
+
+ char *tput_title = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed Trans.\n\
+Send Recv Size Size Time Rate \n\
+bytes Bytes bytes bytes secs. per sec \n\n";
+
+ char *tput_fmt_0 =
+ "%7.2f %s\n";
+
+ char *tput_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %7.2f %s\n";
+ char *tput_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *cpu_title = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\
+Send Recv Size Size Time Rate local remote local remote\n\
+bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f %c %s\n";
+
+ char *cpu_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\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 len;
+ char *temp_message_ptr;
+ int nummessages;
+ SOCKET send_socket;
+ int trans_remaining;
+ double bytes_xferd;
+
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+
+ int rsp_bytes_left;
+ int rsp_bytes_recvd;
+
+ float local_cpu_utilization;
+ float local_service_demand;
+ float remote_cpu_utilization;
+ float remote_service_demand;
+ double thruput;
+
+ struct addrinfo *local_res;
+ struct addrinfo *remote_res;
+
+ struct sdp_rr_request_struct *sdp_rr_request;
+ struct sdp_rr_response_struct *sdp_rr_response;
+ struct sdp_rr_results_struct *sdp_rr_result;
+
+#ifdef WANT_FIRST_BURST
+#define REQUEST_CWND_INITIAL 2
+ /* "in the beginning..." the WANT_FIRST_BURST stuff was like both
+ Unix and the state of New Jersey - both were simple an unspoiled.
+ then it was realized that some stacks are quite picky about
+ initial congestion windows and a non-trivial initial burst of
+ requests would not be individual segments even with TCP_NODELAY
+ set. so, we have to start tracking a poor-man's congestion window
+ up here in window space because we want to try to make something
+ happen that frankly, we cannot guarantee with the specification
+ of SDP. ain't that grand?-) raj 2006-01-30 */
+ int requests_outstanding = 0;
+ int request_cwnd = REQUEST_CWND_INITIAL; /* we ass-u-me that having
+ three requests
+ outstanding at the
+ beginning of the test
+ is ok with SDP stacks
+ of interest. the first
+ two will come from our
+ first_burst loop, and
+ the third from our
+ regularly scheduled
+ send */
+#endif
+
+ sdp_rr_request =
+ (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data;
+ sdp_rr_response=
+ (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data;
+ sdp_rr_result =
+ (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ time_hist = HIST_new();
+ }
+#endif /* WANT_HISTOGRAM */
+
+ /* 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. */
+
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("SDP REQUEST/RESPONSE TEST",local_res,remote_res);
+ }
+
+ /* initialize a few counters */
+
+ send_ring = NULL;
+ recv_ring = NULL;
+ confidence_iteration = 1;
+ init_stat();
+
+ /* we have a great-big while loop which controls the number of times */
+ /* we run a particular test. this is for the calculation of a */
+ /* confidence interval (I really should have stayed awake during */
+ /* probstats :). If the user did not request confidence measurement */
+ /* (no confidence is the default) then we will only go though the */
+ /* loop once. the confidence stuff originates from the folks at IBM */
+
+ while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
+ (confidence_iteration <= iteration_min)) {
+
+ /* initialize a few counters. we have to remember that we might be */
+ /* going through the loop more than once. */
+
+ nummessages = 0;
+ bytes_xferd = 0.0;
+ times_up = 0;
+ timed_out = 0;
+ trans_remaining = 0;
+
+#ifdef WANT_FIRST_BURST
+ /* we have to remember to reset the number of transactions
+ outstanding and the "congestion window for each new
+ iteration. raj 2006-01-31 */
+ requests_outstanding = 0;
+ request_cwnd = REQUEST_CWND_INITIAL;
+#endif
+
+
+ /* set-up the data buffers with the requested alignment and offset. */
+ /* since this is a request/response test, default the send_width and */
+ /* recv_width to 1 and not two raj 7/94 */
+
+ if (send_width == 0) send_width = 1;
+ if (recv_width == 0) recv_width = 1;
+
+ if (send_ring == NULL) {
+ send_ring = allocate_buffer_ring(send_width,
+ req_size,
+ local_send_align,
+ local_send_offset);
+ }
+
+ if (recv_ring == NULL) {
+ recv_ring = allocate_buffer_ring(recv_width,
+ rsp_size,
+ local_recv_align,
+ local_recv_offset);
+ }
+
+ /*set up the data socket */
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_sdp_rr: sdp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_sdp_rr: send_socket obtained...\n");
+ }
+
+ /* 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);
+ }
+
+ if (!no_control) {
+ /* 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_SDP_RR;
+ sdp_rr_request->recv_buf_size = rsr_size_req;
+ sdp_rr_request->send_buf_size = rss_size_req;
+ sdp_rr_request->recv_alignment = remote_recv_align;
+ sdp_rr_request->recv_offset = remote_recv_offset;
+ sdp_rr_request->send_alignment = remote_send_align;
+ sdp_rr_request->send_offset = remote_send_offset;
+ sdp_rr_request->request_size = req_size;
+ sdp_rr_request->response_size = rsp_size;
+ sdp_rr_request->no_delay = rem_nodelay;
+ sdp_rr_request->measure_cpu = remote_cpu_usage;
+ sdp_rr_request->cpu_rate = remote_cpu_rate;
+ sdp_rr_request->so_rcvavoid = rem_rcvavoid;
+ sdp_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ sdp_rr_request->test_length = test_time;
+ }
+ else {
+ sdp_rr_request->test_length = test_trans * -1;
+ }
+ sdp_rr_request->port = atoi(remote_data_port);
+ sdp_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_sdp_rr: requesting SDP rr 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 SDP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = sdp_rr_response->recv_buf_size;
+ rss_size = sdp_rr_response->send_buf_size;
+ rem_nodelay = sdp_rr_response->no_delay;
+ remote_cpu_usage = sdp_rr_response->measure_cpu;
+ remote_cpu_rate = sdp_rr_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ set_port_number(remote_res,(short)sdp_rr_response->data_port_number);
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+
+ exit(1);
+ }
+ }
+
+#ifdef WANT_DEMO
+ DEMO_RR_SETUP(1000)
+#endif
+
+ /*Connect up to the remote port on the data socket */
+ if (connect(send_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen) == INVALID_SOCKET){
+ perror("netperf: data socket connect failed");
+
+ 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);
+
+#ifdef WANT_INTERVALS
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+ /* 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. */
+
+#ifdef WANT_DEMO
+ if (demo_mode) {
+ HIST_timestamp(demo_one_ptr);
+ }
+#endif
+
+ while ((!times_up) || (trans_remaining > 0)) {
+ /* send the request. we assume that if we use a blocking socket, */
+ /* the request will be sent at one shot. */
+
+#ifdef WANT_FIRST_BURST
+ /* we can inject no more than request_cwnd, which will grow with
+ time, and no more than first_burst_size. we don't use <= to
+ account for the "regularly scheduled" send call. of course
+ that makes it more a "max_outstanding_ than a
+ "first_burst_size" but for now we won't fix the names. also,
+ I suspect the extra check against < first_burst_size is
+ redundant since later I expect to make sure that request_cwnd
+ can never get larger than first_burst_size, but just at the
+ moment I'm feeling like a belt and suspenders kind of
+ programmer. raj 2006-01-30 */
+ while ((first_burst_size > 0) &&
+ (requests_outstanding < request_cwnd) &&
+ (requests_outstanding < first_burst_size)) {
+ if (debug) {
+ fprintf(where,
+ "injecting, req_outstndng %d req_cwnd %d burst %d\n",
+ requests_outstanding,
+ request_cwnd,
+ first_burst_size);
+ }
+ if ((len = send(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ 0)) != req_size) {
+ /* we should never hit the end of the test in the first burst */
+ perror("send_sdp_rr: initial burst data send error");
+ exit(-1);
+ }
+ requests_outstanding += 1;
+ }
+
+#endif /* WANT_FIRST_BURST */
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before our call to send, and then again just
+ after the receive raj 8/94 */
+ /* but only if we are actually going to display one. raj
+ 2007-02-07 */
+
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ if ((len = send(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ 0)) != req_size) {
+ if (SOCKET_EINTR(len) || (errno == 0)) {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_sdp_rr: data send error");
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+#ifdef WANT_FIRST_BURST
+ requests_outstanding += 1;
+#endif
+
+ /* receive the response */
+ rsp_bytes_left = rsp_size;
+ temp_message_ptr = recv_ring->buffer_ptr;
+ while(rsp_bytes_left > 0) {
+ if((rsp_bytes_recvd=recv(send_socket,
+ temp_message_ptr,
+ rsp_bytes_left,
+ 0)) == SOCKET_ERROR) {
+ if ( SOCKET_EINTR(rsp_bytes_recvd) ) {
+ /* We hit the end of a timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_sdp_rr: data recv error");
+ exit(1);
+ }
+ rsp_bytes_left -= rsp_bytes_recvd;
+ temp_message_ptr += rsp_bytes_recvd;
+ }
+ recv_ring = recv_ring->next;
+
+#ifdef WANT_FIRST_BURST
+ /* so, since we've gotten a response back, update the
+ bookkeeping accordingly. there is one less request
+ outstanding and we can put one more out there than before. */
+ requests_outstanding -= 1;
+ if (request_cwnd < first_burst_size) {
+ request_cwnd += 1;
+ if (debug) {
+ fprintf(where,
+ "incr req_cwnd to %d first_burst %d reqs_outstndng %d\n",
+ request_cwnd,
+ first_burst_size,
+ requests_outstanding);
+ }
+ }
+#endif
+ if (timed_out) {
+ /* we may have been in a nested while loop - we need */
+ /* another call to break. */
+ break;
+ }
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ HIST_timestamp(&time_two);
+ HIST_add(time_hist,delta_micro(&time_one,&time_two));
+ }
+#endif /* WANT_HISTOGRAM */
+
+#ifdef WANT_DEMO
+ DEMO_RR_INTERVAL(1);
+#endif
+
+#ifdef WANT_INTERVALS
+ INTERVALS_WAIT();
+#endif /* WANT_INTERVALS */
+
+ nummessages++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug > 3) {
+ if ((nummessages % 100) == 0) {
+ fprintf(where,
+ "Transaction %d completed\n",
+ nummessages);
+ fflush(where);
+ }
+ }
+ }
+
+ /* At this point we used to call shutdown on the 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? */
+
+ if (!no_control) {
+ /* Get the statistics from the remote end. The remote will have
+ calculated CPU utilization. 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);
+ fprintf(where,"netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+ exit(1);
+ }
+ }
+
+ /* We now calculate what our throughput was for the test. */
+
+ bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages);
+ thruput = nummessages/elapsed_time;
+
+ 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) {
+ 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 = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+ remote_cpu_utilization = sdp_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,
+ sdp_rr_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ }
+ else {
+ /* we were not measuring cpu, for the confidence stuff, we */
+ /* should make it -1.0 */
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* at this point, we want to calculate the confidence information.
+ if debugging is on, calculate_confidence will print-out the
+ parameters we pass it */
+
+ calculate_confidence(confidence_iteration,
+ elapsed_time,
+ thruput,
+ local_cpu_utilization,
+ remote_cpu_utilization,
+ local_service_demand,
+ remote_service_demand);
+
+
+ confidence_iteration++;
+
+ /* we are now done with the socket, so close it */
+ close(send_socket);
+
+ }
+
+ retrieve_confident_values(&elapsed_time,
+ &thruput,
+ &local_cpu_utilization,
+ &remote_cpu_utilization,
+ &local_service_demand,
+ &remote_service_demand);
+
+ /* 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... */
+
+ if (confidence < 0) {
+ /* we did not hit confidence, but were we asked to look for it? */
+ if (iteration_max > 1) {
+ display_confidence();
+ }
+ }
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ local_cpu_method = format_cpu_method(cpu_method);
+ remote_cpu_method = format_cpu_method(sdp_rr_result->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ remote_cpu_method,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ }
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,
+ cpu_title,
+ local_cpu_method,
+ remote_cpu_method);
+ }
+
+ fprintf(where,
+ cpu_fmt_1_line_1, /* the format string */
+ lss_size, /* local sendbuf size */
+ lsr_size,
+ req_size, /* how large were the requests */
+ rsp_size, /* guess */
+ elapsed_time, /* how long was the test */
+ thruput,
+ local_cpu_utilization, /* local cpu */
+ remote_cpu_utilization, /* remote cpu */
+ local_service_demand, /* local service demand */
+ remote_service_demand, /* remote service demand */
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ fprintf(where,
+ cpu_fmt_1_line_2,
+ rss_size,
+ rsr_size);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ thruput,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,tput_title,format_units());
+ }
+
+ fprintf(where,
+ tput_fmt_1_line_1, /* the format string */
+ lss_size,
+ lsr_size,
+ req_size, /* how large were the requests */
+ rsp_size, /* how large were the responses */
+ elapsed_time, /* how long did it take */
+ thruput,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ fprintf(where,
+ tput_fmt_1_line_2,
+ rss_size, /* remote recvbuf size */
+ rsr_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. */
+
+ /* how to handle the verbose information in the presence of */
+ /* confidence intervals is yet to be determined... raj 11/94 */
+ 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 */
+ /* SDP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ fprintf(where,
+ ksink_fmt,
+ local_send_align,
+ remote_recv_offset,
+ local_send_offset,
+ remote_recv_offset);
+
+#ifdef WANT_HISTOGRAM
+ fprintf(where,"\nHistogram of request/response times\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+
+ }
+
+}
+ /* this routine implements the receive (netserver) side of a SDP_RR */
+ /* test */
+void
+recv_sdp_rr()
+{
+
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct sockaddr_in myaddr_in,
+ peeraddr_in;
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ char *temp_message_ptr;
+ int trans_received;
+ int trans_remaining;
+ int bytes_sent;
+ int request_bytes_recvd;
+ int request_bytes_remaining;
+ int timed_out = 0;
+ int sock_closed = 0;
+ float elapsed_time;
+
+ struct sdp_rr_request_struct *sdp_rr_request;
+ struct sdp_rr_response_struct *sdp_rr_response;
+ struct sdp_rr_results_struct *sdp_rr_results;
+
+ sdp_rr_request =
+ (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data;
+ sdp_rr_response =
+ (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data;
+ sdp_rr_results =
+ (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_sdp_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_sdp_rr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = SDP_RR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_sdp_rr: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* allocate the recv and send rings with the requested alignments */
+ /* and offsets. raj 7/94 */
+ if (debug) {
+ fprintf(where,"recv_sdp_rr: requested recv alignment of %d offset %d\n",
+ sdp_rr_request->recv_alignment,
+ sdp_rr_request->recv_offset);
+ fprintf(where,"recv_sdp_rr: requested send alignment of %d offset %d\n",
+ sdp_rr_request->send_alignment,
+ sdp_rr_request->send_offset);
+ fflush(where);
+ }
+
+ /* at some point, these need to come to us from the remote system */
+ if (send_width == 0) send_width = 1;
+ if (recv_width == 0) recv_width = 1;
+
+ send_ring = allocate_buffer_ring(send_width,
+ sdp_rr_request->response_size,
+ sdp_rr_request->send_alignment,
+ sdp_rr_request->send_offset);
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ sdp_rr_request->request_size,
+ sdp_rr_request->recv_alignment,
+ sdp_rr_request->recv_offset);
+
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_rr: grabbing a socket...\n");
+ fflush(where);
+ }
+
+ /* create_data_socket expects to find some things in the global */
+ /* variables, so set the globals based on the values in the request. */
+ /* once the socket has been created, we will set the response values */
+ /* based on the updated value of those globals. raj 7/94 */
+ lss_size_req = sdp_rr_request->send_buf_size;
+ lsr_size_req = sdp_rr_request->recv_buf_size;
+ loc_nodelay = sdp_rr_request->no_delay;
+ loc_rcvavoid = sdp_rr_request->so_rcvavoid;
+ loc_sndavoid = sdp_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(sdp_rr_request->ipfamily),
+ sdp_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(sdp_rr_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ /* fake things out by changing local_res->ai_family to AF_INET_SDP */
+ local_res->ai_family = AF_INET_SDP;
+ local_res->ai_protocol = 0;
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+
+ exit(1);
+ }
+
+
+#ifdef WIN32
+ /* The test timer can fire during operations on the listening socket,
+ so to make the start_timer below work we have to move
+ it to close s_listen while we are blocked on accept. */
+ win_kludge_socket2 = s_listen;
+#endif
+
+
+ /* Now, let's set-up the socket to listen for connections */
+ if (listen(s_listen, 5) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+
+ /* now get the port number assigned by the system */
+ addrlen = sizeof(myaddr_in);
+ if (getsockname(s_listen,
+ (struct sockaddr *)&myaddr_in,
+ &addrlen) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+ /* Now myaddr_in contains the port and the internet address this is */
+ /* returned to the sender also implicitly telling the sender that the */
+ /* socket buffer sizing has been done. */
+
+ sdp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
+ 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. */
+
+ sdp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */
+ sdp_rr_response->measure_cpu = 0;
+
+ if (sdp_rr_request->measure_cpu) {
+ sdp_rr_response->measure_cpu = 1;
+ sdp_rr_response->cpu_rate = calibrate_local_cpu(sdp_rr_request->cpu_rate);
+ }
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ sdp_rr_response->send_buf_size = lss_size;
+ sdp_rr_response->recv_buf_size = lsr_size;
+ sdp_rr_response->no_delay = loc_nodelay;
+ sdp_rr_response->so_rcvavoid = loc_rcvavoid;
+ sdp_rr_response->so_sndavoid = loc_sndavoid;
+ sdp_rr_response->test_length = sdp_rr_request->test_length;
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ if ((s_data = accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ /* Let's just punt. The remote will be given some information */
+ close(s_listen);
+
+ exit(1);
+ }
+
+#ifdef KLUDGE_SOCKET_OPTIONS
+ /* this is for those systems which *INCORRECTLY* fail to pass */
+ /* attributes across an accept() call. Including this goes against */
+ /* my better judgement :( raj 11/95 */
+
+ kludge_socket_options(s_data);
+
+#endif /* KLUDGE_SOCKET_OPTIONS */
+
+#ifdef WIN32
+ /* this is used so the timer thread can close the socket out from */
+ /* under us, which to date is the easiest/cleanest/least */
+ /* Windows-specific way I can find to force the winsock calls to */
+ /* return WSAEINTR with the test is over. anything that will run on */
+ /* 95 and NT and is closer to what netperf expects from Unix signals */
+ /* and such would be appreciated raj 1/96 */
+ win_kludge_socket = s_data;
+#endif /* WIN32 */
+
+ if (debug) {
+ fprintf(where,"recv_sdp_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(sdp_rr_request->measure_cpu);
+
+ /* The loop will exit when we hit the end of the test time, or when */
+ /* we have exchanged the requested number of transactions. */
+
+ if (sdp_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(sdp_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = sdp_rr_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+ temp_message_ptr = recv_ring->buffer_ptr;
+ request_bytes_remaining = sdp_rr_request->request_size;
+ while(request_bytes_remaining > 0) {
+ if((request_bytes_recvd=recv(s_data,
+ temp_message_ptr,
+ request_bytes_remaining,
+ 0)) == SOCKET_ERROR) {
+ if (SOCKET_EINTR(request_bytes_recvd))
+ {
+ timed_out = 1;
+ break;
+ }
+
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ else if( request_bytes_recvd == 0 ) {
+ if (debug) {
+ fprintf(where,"zero is my hero\n");
+ fflush(where);
+ }
+ sock_closed = 1;
+ break;
+ }
+ else {
+ request_bytes_remaining -= request_bytes_recvd;
+ temp_message_ptr += request_bytes_recvd;
+ }
+ }
+
+ recv_ring = recv_ring->next;
+
+ if ((timed_out) || (sock_closed)) {
+ /* we hit the end of the test based on time - or the socket
+ closed on us along the way. bail out of here now... */
+ if (debug) {
+ fprintf(where,"yo5\n");
+ fflush(where);
+ }
+ break;
+ }
+
+ /* Now, send the response to the remote */
+ if((bytes_sent=send(s_data,
+ send_ring->buffer_ptr,
+ sdp_rr_request->response_size,
+ 0)) == SOCKET_ERROR) {
+ if (SOCKET_EINTR(bytes_sent)) {
+ /* the test timer has popped */
+ timed_out = 1;
+ fprintf(where,"yo6\n");
+ fflush(where);
+ break;
+ }
+ netperf_response.content.serv_errno = 992;
+ send_response();
+ exit(1);
+ }
+
+ send_ring = send_ring->next;
+
+ trans_received++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+ }
+
+
+ /* The loop now exits due to timeout or transaction count being */
+ /* reached */
+
+ cpu_stop(sdp_rr_request->measure_cpu,&elapsed_time);
+
+ stop_timer();
+
+ 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_sdp_rr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ sdp_rr_results->bytes_received = (trans_received *
+ (sdp_rr_request->request_size +
+ sdp_rr_request->response_size));
+ sdp_rr_results->trans_received = trans_received;
+ sdp_rr_results->elapsed_time = elapsed_time;
+ sdp_rr_results->cpu_method = cpu_method;
+ sdp_rr_results->num_cpus = lib_num_loc_cpus;
+ if (sdp_rr_request->measure_cpu) {
+ sdp_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_sdp_rr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ /* we are now done with the sockets */
+ close(s_data);
+ close(s_listen);
+
+ send_response();
+
+}
+
+
+
+void
+print_sdp_usage()
+{
+
+ printf("%s",sdp_usage);
+ exit(1);
+
+}
+void
+scan_sdp_args(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+
+#define SOCKETS_ARGS "b:DhH:I:L:m:M:P:r:s:S:V46"
+
+ extern char *optarg; /* pointer to option string */
+
+ int c;
+
+ char
+ arg1[BUFSIZ], /* argument holders */
+ arg2[BUFSIZ];
+
+ if (no_control) {
+ fprintf(where,
+ "The SDP tests do not know how to deal with no control tests\n");
+ exit(-1);
+ }
+
+ strncpy(local_data_port,"0",sizeof(local_data_port));
+ strncpy(remote_data_port,"0",sizeof(remote_data_port));
+
+ /* 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.. */
+
+ while ((c= getopt(argc, argv, SOCKETS_ARGS)) != EOF) {
+ switch (c) {
+ case '?':
+ case '4':
+ remote_data_family = AF_INET;
+ local_data_family = AF_INET;
+ break;
+ case '6':
+#if defined(AF_INET6)
+ remote_data_family = AF_INET6;
+ local_data_family = AF_INET6;
+#else
+ fprintf(stderr,
+ "This netperf was not compiled on an IPv6 capable host!\n");
+ fflush(stderr);
+ exit(-1);
+#endif
+ break;
+ case 'h':
+ print_sdp_usage();
+ exit(1);
+ case 'b':
+#ifdef WANT_FIRST_BURST
+ first_burst_size = atoi(optarg);
+#else /* WANT_FIRST_BURST */
+ printf("Initial request burst functionality not compiled-in!\n");
+#endif /* WANT_FIRST_BURST */
+ break;
+ case 'D':
+ /* set the nodelay flag */
+ loc_nodelay = 1;
+ rem_nodelay = 1;
+ break;
+ case 'H':
+ break_args_explicit(optarg,arg1,arg2);
+ if (arg1[0]) {
+ /* make sure we leave room for the NULL termination boys and
+ girls. raj 2005-02-82 */
+ remote_data_address = malloc(strlen(arg1)+1);
+ strncpy(remote_data_address,arg1,strlen(arg1));
+ }
+ if (arg2[0])
+ remote_data_family = parse_address_family(arg2);
+ break;
+ case 'L':
+ break_args_explicit(optarg,arg1,arg2);
+ if (arg1[0]) {
+ /* make sure we leave room for the NULL termination boys and
+ girls. raj 2005-02-82 */
+ local_data_address = malloc(strlen(arg1)+1);
+ strncpy(local_data_address,arg1,strlen(arg1));
+ }
+ if (arg2[0])
+ local_data_family = parse_address_family(arg2);
+ break;
+ case 'P':
+ /* set the local and remote data port numbers for the tests to
+ allow them to run through those blankety blank end-to-end
+ breaking firewalls. raj 2004-06-15 */
+ break_args(optarg,arg1,arg2);
+ if (arg1[0])
+ strncpy(local_data_port,arg1,sizeof(local_data_port));
+ if (arg2[0])
+ strncpy(remote_data_port,arg2,sizeof(remote_data_port));
+ break;
+ case 's':
+ /* set local socket sizes */
+ break_args(optarg,arg1,arg2);
+ if (arg1[0])
+ lss_size_req = convert(arg1);
+ if (arg2[0])
+ lsr_size_req = convert(arg2);
+ break;
+ case 'S':
+ /* set remote socket sizes */
+ break_args(optarg,arg1,arg2);
+ if (arg1[0])
+ rss_size_req = convert(arg1);
+ if (arg2[0])
+ rsr_size_req = convert(arg2);
+ break;
+ case 'r':
+ /* set the request/response sizes */
+ break_args(optarg,arg1,arg2);
+ if (arg1[0])
+ req_size = convert(arg1);
+ if (arg2[0])
+ rsp_size = convert(arg2);
+ break;
+ case 'm':
+ /* set size of the buffer for each sent message */
+ send_size = convert(optarg);
+ break;
+ case 'M':
+ /* set the size of the buffer for each received message */
+ recv_size = convert(optarg);
+ break;
+ case 't':
+ /* set the test name */
+ strcpy(test_name,optarg);
+ break;
+ case 'W':
+ /* set the "width" of the user space data */
+ /* buffer. This will be the number of */
+ /* send_size buffers malloc'd in the */
+ /* *_STREAM test. It may be enhanced to set */
+ /* both send and receive "widths" but for now */
+ /* it is just the sending *_STREAM. */
+ send_width = convert(optarg);
+ break;
+ case 'V':
+ /* we want to do copy avoidance and will set */
+ /* it for everything, everywhere, if we really */
+ /* can. of course, we don't know anything */
+ /* about the remote... */
+#ifdef SO_SND_COPYAVOID
+ loc_sndavoid = 1;
+#else
+ loc_sndavoid = 0;
+ printf("Local send copy avoidance not available.\n");
+#endif
+#ifdef SO_RCV_COPYAVOID
+ loc_rcvavoid = 1;
+#else
+ loc_rcvavoid = 0;
+ printf("Local recv copy avoidance not available.\n");
+#endif
+ rem_sndavoid = 1;
+ rem_rcvavoid = 1;
+ break;
+ case 'N':
+ /* this opton allows the user to set the number of
+ * messages to send. This in effect modifies the test
+ * time. If we know the message size, then the we can
+ * express the test time as message_size * number_messages
+ */
+ msg_count = convert (optarg);
+ if (msg_count > 0)
+ test_time = 0;
+ break;
+ case 'B':
+ non_block = 1;
+ break;
+ case 'T':
+ num_associations = atoi(optarg);
+ if (num_associations <= 1) {
+ printf("Number of SDP associations must be >= 1\n");
+ exit(1);
+ }
+ break;
+ };
+ }
+}
+
+#endif /* WANT_SDP */