aboutsummaryrefslogtreecommitdiff
path: root/src/nettest_bsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nettest_bsd.c')
-rw-r--r--src/nettest_bsd.c13202
1 files changed, 13202 insertions, 0 deletions
diff --git a/src/nettest_bsd.c b/src/nettest_bsd.c
new file mode 100644
index 0000000..b605325
--- /dev/null
+++ b/src/nettest_bsd.c
@@ -0,0 +1,13202 @@
+#ifndef lint
+char nettest_id[]="\
+@(#)nettest_bsd.c (c) Copyright 1993-2012 Hewlett-Packard Co. Version 2.6.0";
+#endif /* lint */
+
+
+/****************************************************************/
+/* */
+/* nettest_bsd.c */
+/* */
+/* the BSD sockets parsing routine... */
+/* ...with the addition of Windows NT, this is now also */
+/* a Winsock test... sigh :) */
+/* */
+/* scan_sockets_args() */
+/* */
+/* the actual test routines... */
+/* */
+/* send_tcp_stream() perform a tcp stream test */
+/* recv_tcp_stream() */
+/* send_tcp_maerts() perform a tcp stream test */
+/* recv_tcp_maerts() in the other direction */
+/* send_tcp_rr() perform a tcp request/response */
+/* recv_tcp_rr() */
+/* send_tcp_conn_rr() an RR test including connect */
+/* recv_tcp_conn_rr() */
+/* send_tcp_cc() a connect/disconnect test with */
+/* recv_tcp_cc() no RR */
+/* send_tcp_mss() just report the mss */
+/* send_udp_stream() perform a udp stream test */
+/* recv_udp_stream() */
+/* send_udp_rr() perform a udp request/response */
+/* recv_udp_rr() */
+/* loc_cpu_rate() determine the local cpu maxrate */
+/* rem_cpu_rate() find the remote cpu maxrate */
+/* */
+/****************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#ifndef WIN32
+#include <errno.h>
+#include <signal.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef NOSTDLIBH
+#include <malloc.h>
+#endif /* NOSTDLIBH */
+
+
+#ifndef WIN32
+#if !defined(__VMS)
+#include <sys/ipc.h>
+#endif /* !__VMS */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#ifdef HAVE_NETINET_SCTP_H
+#include <netinet/sctp.h>
+#endif
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#else /* WIN32 */
+#include <process.h>
+#define netperf_socklen_t socklen_t
+#include <winsock2.h>
+#include "missing\stdint.h"
+/* while it is unlikely that anyone running Windows 2000 or NT 4 is
+ going to be trying to compile this, if they are they will want to
+ define DONT_IPV6 in the sources file */
+#ifndef DONT_IPV6
+#include <ws2tcpip.h>
+#endif
+#include <windows.h>
+
+#define sleep(x) Sleep((x)*1000)
+
+#define __func__ __FUNCTION__
+#endif /* WIN32 */
+
+/* We don't want to use bare constants in the shutdown() call. In the
+ extremely unlikely event that SHUT_WR isn't defined, we will define
+ it to the value we used to be passing to shutdown() anyway. raj
+ 2007-02-08 */
+#if !defined(SHUT_WR)
+#define SHUT_WR 1
+#endif
+
+#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO)
+# include "missing/getaddrinfo.h"
+#endif
+
+#include "netlib.h"
+#include "netsh.h"
+#include "nettest_bsd.h"
+
+#if defined(WANT_HISTOGRAM) || defined(WANT_DEMO)
+#include "hist.h"
+#endif /* WANT_HISTOGRAM */
+
+
+/* make first_burst_size unconditional so we can use it easily enough
+ when calculating transaction latency for the TCP_RR test. raj
+ 2007-06-08 however, change its default value so one can tell in
+ "omni" output whether or not WANT_BURST was enabled. raj
+ 2008-01-28 */
+#if defined(WANT_FIRST_BURST)
+int first_burst_size=0;
+#else
+int first_burst_size=-1;
+#endif
+
+#if defined(HAVE_SENDFILE) && (defined(__linux) || defined(__sun))
+#include <sys/sendfile.h>
+#endif /* HAVE_SENDFILE && (__linux || __sun) */
+
+
+
+/* these variables are specific to the BSD sockets tests, but can
+ * be used elsewhere if needed. They are externed through nettest_bsd.h
+ */
+
+int
+ socket_type, /* used initially by the "omni" tests */
+ rss_size_req = -1, /* requested remote socket send buffer size */
+ rsr_size_req = -1, /* requested remote socket recv buffer size */
+ rss_size, /* initial remote socket send buffer size */
+ rsr_size, /* initial remote socket recv buffer size */
+ rss_size_end = -1, /* final remote socket send buffer size */
+ rsr_size_end = -1, /* final remote socket recv buffer size */
+ lss_size_req = -1, /* requested local socket send buffer size */
+ lsr_size_req = -1, /* requested local socket recv buffer size */
+ lss_size, /* local socket send buffer size */
+ lsr_size, /* local socket recv buffer size */
+ lss_size_end = -1, /* final local socket send buffer size */
+ lsr_size_end = -1, /* final local socket recv buffer size */
+ req_size = 1, /* request size */
+ rsp_size = 1, /* response size */
+ send_size, /* how big are individual sends */
+ recv_size, /* how big are individual receives */
+ transport_mss_req = -1; /* what maximum segment size is wanted */
+
+static int confidence_iteration;
+static char local_cpu_method;
+static char remote_cpu_method;
+
+/* these will control the width of port numbers we try to use in the */
+/* TCP_CRR and/or TCP_TRR tests. raj 3/95 */
+static int client_port_min = 5000;
+static int client_port_max = 65535;
+
+ /* different options for the sockets */
+
+int
+ loc_nodelay, /* don't/do use NODELAY locally */
+ rem_nodelay, /* don't/do use NODELAY remotely */
+#ifdef TCP_CORK
+ loc_tcpcork=0, /* don't/do use TCP_CORK locally */
+ rem_tcpcork=0, /* don't/do use TCP_CORK remotely */
+#else
+ loc_tcpcork=-1,
+ rem_tcpcork=-1,
+#endif /* TCP_CORK */
+ loc_sndavoid, /* avoid send copies locally */
+ loc_rcvavoid, /* avoid recv copies locally */
+ rem_sndavoid, /* avoid send copies remotely */
+ rem_rcvavoid, /* avoid recv_copies remotely */
+ local_connected = 0, /* local socket type, connected/non-connected */
+ remote_connected = 0, /* remote socket type, connected/non-connected */
+ routing_allowed = 1; /* set/clear SO_DONTROUTE on data socket */
+
+int multicast_ttl = -1; /* should we set the multicast TTL to a value? */
+
+int want_keepalive = 0;
+
+#ifdef WANT_HISTOGRAM
+#ifdef HAVE_GETHRTIME
+static hrtime_t time_one;
+static hrtime_t time_two;
+#elif HAVE_GET_HRT
+#include "hrt.h"
+static hrt_t time_one;
+static hrt_t time_two;
+#elif defined(WIN32)
+static LARGE_INTEGER time_one;
+static LARGE_INTEGER time_two;
+#else
+static struct timeval time_one;
+static struct timeval time_two;
+#endif /* HAVE_GETHRTIME */
+static HIST time_hist;
+#endif /* WANT_HISTOGRAM */
+
+#ifdef WANT_INTERVALS
+int interval_count;
+#ifndef WANT_SPIN
+#ifdef WIN32
+#define INTERVALS_INIT() \
+ if (interval_burst) { \
+ /* zero means that we never pause, so we never should need the \
+ interval timer. we used to use it for demo mode, but we deal \
+ with that with a variant on watching the clock rather than \
+ waiting for a timer. raj 2006-02-06 */ \
+ start_itimer(interval_wate); \
+ } \
+ interval_count = interval_burst;
+#else
+sigset_t signal_set;
+#define INTERVALS_INIT() \
+ if (interval_burst) { \
+ /* zero means that we never pause, so we never should need the \
+ interval timer. we used to use it for demo mode, but we deal \
+ with that with a variant on watching the clock rather than \
+ waiting for a timer. raj 2006-02-06 */ \
+ start_itimer(interval_wate); \
+ } \
+ interval_count = interval_burst; \
+ /* get the signal set for the call to sigsuspend */ \
+ if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { \
+ fprintf(where, \
+ "%s: unable to get sigmask errno %d\n", \
+ __func__, \
+ errno); \
+ fflush(where); \
+ exit(1); \
+ }
+#endif /* WIN32 */
+
+#ifdef WIN32
+#define INTERVALS_WAIT() \
+ /* in this case, the interval count is the count-down counter \
+ to decide to sleep for a little bit */ \
+ if ((interval_burst) && (--interval_count == 0)) { \
+ /* call WaitForSingleObject and wait for the interval timer to get us \
+ out */ \
+ if (debug > 1) { \
+ fprintf(where,"about to suspend\n"); \
+ fflush(where); \
+ } \
+ if (WaitForSingleObject(WinTimer, INFINITE) != WAIT_OBJECT_0) { \
+ fprintf(where, "WaitForSingleObject failed (%d)\n", GetLastError()); \
+ fflush(where); \
+ exit(1); \
+ } \
+ interval_count = interval_burst; \
+ }
+#else
+#define INTERVALS_WAIT() \
+ /* in this case, the interval count is the count-down couter \
+ to decide to sleep for a little bit */ \
+ if ((interval_burst) && (--interval_count == 0)) { \
+ /* call sigsuspend and wait for the interval timer to get us \
+ out */ \
+ if (debug > 1) { \
+ fprintf(where,"about to suspend\n"); \
+ fflush(where); \
+ } \
+ if (sigsuspend(&signal_set) == EFAULT) { \
+ fprintf(where, \
+ "%s: fault with sigsuspend.\n", \
+ __func__); \
+ fflush(where); \
+ exit(1); \
+ } \
+ interval_count = interval_burst; \
+ }
+#endif /* WIN32 */
+#else
+/* first out timestamp */
+#ifdef HAVE_GETHRTIME
+static hrtime_t intvl_one;
+static hrtime_t intvl_two;
+static hrtime_t *intvl_one_ptr = &intvl_one;
+static hrtime_t *intvl_two_ptr = &intvl_two;
+static hrtime_t *temp_intvl_ptr = &intvl_one;
+#elif defined(WIN32)
+static LARGE_INTEGER intvl_one;
+static LARGE_INTEGER intvl_two;
+static LARGE_INTEGER *intvl_one_ptr = &intvl_one;
+static LARGE_INTEGER *intvl_two_ptr = &intvl_two;
+static LARGE_INTEGER *temp_intvl_ptr = &intvl_one;
+#else
+static struct timeval intvl_one;
+static struct timeval intvl_two;
+static struct timeval *intvl_one_ptr = &intvl_one;
+static struct timeval *intvl_two_ptr = &intvl_two;
+static struct timeval *temp_intvl_ptr = &intvl_one;
+#endif
+
+#define INTERVALS_INIT() \
+ if (interval_burst) { \
+ HIST_timestamp(intvl_one_ptr); \
+ } \
+ interval_count = interval_burst; \
+
+#define INTERVALS_WAIT() \
+ /* in this case, the interval count is the count-down couter \
+ to decide to sleep for a little bit */ \
+ if ((interval_burst) && (--interval_count == 0)) { \
+ /* call sigsuspend and wait for the interval timer to get us \
+ out */ \
+ if (debug > 1) { \
+ fprintf(where,"about to spin suspend\n"); \
+ fflush(where); \
+ } \
+ HIST_timestamp(intvl_two_ptr); \
+ while(delta_micro(intvl_one_ptr,intvl_two_ptr) < interval_usecs) { \
+ HIST_timestamp(intvl_two_ptr); \
+ } \
+ temp_intvl_ptr = intvl_one_ptr; \
+ intvl_one_ptr = intvl_two_ptr; \
+ intvl_two_ptr = temp_intvl_ptr; \
+ interval_count = interval_burst; \
+ }
+#endif
+#endif
+
+
+char sockets_usage[] = "\n\
+Usage: netperf [global options] -- [test options] \n\
+\n\
+TCP/UDP BSD Sockets Test Options:\n\
+ -b number Send number requests at start of _RR tests\n\
+ -C Set TCP_CORK when available\n\
+ -D [L][,R] Set TCP_NODELAY locally and/or remotely (TCP_*)\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 connection\n\
+ -m bytes Set the send size (TCP_STREAM, UDP_STREAM)\n\
+ -M bytes Set the recv size (TCP_STREAM, UDP_STREAM)\n\
+ -n Use the connected socket for UDP locally\n\
+ -N Use the connected socket for UDP remotely\n\
+ -p min[,max] Set the min/max port numbers for TCP_CRR, TCP_TRR\n\
+ -P local[,remote] Set the local/remote port for the data socket\n\
+ -r req,[rsp] Set request/response sizes (TCP_RR, UDP_RR)\n\
+ -s send[,recv] Set local socket send/recv buffer sizes\n\
+ -S send[,recv] Set remote socket send/recv buffer sizes\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";
+
+
+
+/* these routines convert between the AF address space and the NF
+ address space since the numeric values of AF_mumble are not the
+ same across the platforms. raj 2005-02-08 */
+
+int
+nf_to_af(int nf) {
+ switch(nf) {
+ case NF_INET:
+ return AF_INET;
+ case NF_UNSPEC:
+ return AF_UNSPEC;
+ case NF_INET6:
+#if defined(AF_INET6)
+ return AF_INET6;
+#else
+ return AF_UNSPEC;
+#endif
+ case NF_RDS:
+#if defined(AF_RDS)
+ return AF_RDS;
+#else
+ return AF_UNSPEC;
+#endif
+ default:
+ return AF_UNSPEC;
+ }
+}
+
+int
+af_to_nf(int af) {
+
+ switch(af) {
+ case AF_INET:
+ return NF_INET;
+ case AF_UNSPEC:
+ return NF_UNSPEC;
+#if defined(AF_INET6)
+ case AF_INET6:
+ return NF_INET6;
+#endif
+#if defined(AF_RDS)
+ case AF_RDS:
+ return NF_RDS;
+#endif
+ default:
+ return NF_UNSPEC;
+ }
+}
+
+
+/* these routines will convert between the hosts' socket types and
+ those netperf uses. we need this because different platforms can
+ have different values for SOCK_STREAM, SOCK_DGRAM and the
+ like... */
+
+int
+nst_to_hst(int nst) {
+ switch(nst) {
+#ifdef SOCK_STREAM
+ case NST_STREAM:
+ return SOCK_STREAM;
+ break; /* ok, this may not be necessary :) */
+#endif
+#ifdef SOCK_DGRAM
+ case NST_DGRAM:
+ return SOCK_DGRAM;
+ break;
+#endif
+#ifdef SOCK_DCCP
+ case NST_DCCP:
+ return SOCK_DCCP;
+ break;
+#endif
+#ifdef SOCK_SEQPACKET
+ case NST_SEQPACKET:
+ return NST_SEQPACKET;
+#endif
+ default:
+ return -1;
+ }
+}
+
+int
+hst_to_nst(int hst) {
+
+ switch(hst) {
+#ifdef SOCK_STREAM
+ case SOCK_STREAM:
+ return NST_STREAM;
+ break;
+#endif
+#ifdef SOCK_DGRAM
+ case SOCK_DGRAM:
+ return NST_DGRAM;
+ break;
+#endif
+#ifdef SOCK_DCCP
+ case SOCK_DCCP:
+ return NST_DCCP;
+ break;
+#endif
+#ifdef SOCK_SEQPACKET
+ case SOCK_SEQPACKET:
+ return NST_SEQPACKET;
+#endif
+ default:
+ return NST_UNKN;
+ }
+}
+char *
+hst_to_str(int hst) {
+
+ switch(hst) {
+#ifdef SOCK_STREAM
+ case SOCK_STREAM:
+ return "Stream";
+ break;
+#endif
+#ifdef SOCK_DGRAM
+ case SOCK_DGRAM:
+ return "Datagram";
+ break;
+#endif
+#ifdef SOCK_DCCP
+ case SOCK_DCCP:
+ return "DCCP";
+ break;
+#endif
+#ifdef SOCK_SEQPACKET
+ case SOCK_SEQPACKET:
+ return "Seqpacket";
+#endif
+ default:
+ return "Unknown";
+ }
+}
+
+char *
+protocol_to_str(int protocol) {
+ switch(protocol) {
+ /* ass-u-me that everyone has IPPROTO_TCP and IPPROTO_UDP */
+ case IPPROTO_TCP:
+ return "TCP";
+ case IPPROTO_UDP:
+ return "UDP";
+ /* but do not assume that everyone has the others */
+#ifdef IPPROTO_UDPLITE
+ case IPPROTO_UDPLITE:
+ return "UDPLite";
+#endif
+#ifdef IPPROTO_SCTP
+ case IPPROTO_SCTP:
+ return "SCTP";
+#endif
+#ifdef IPPROTO_DCCP
+ case IPPROTO_DCCP:
+ return "DCCP";
+#endif
+#ifdef IPPROTO_SDP
+ case IPPROTO_SDP:
+ return "SDP";
+#endif
+#ifdef IPPROTO_IP
+ case IPPROTO_IP:
+ return "IP Default";
+#endif
+ default:
+ return "Unknown Protocol";
+ }
+}
+
+
+ /* This routine is intended to retrieve interesting aspects of tcp */
+ /* 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_tcp_info(SOCKET socket, int *mss)
+{
+
+#ifdef TCP_MAXSEG
+ netperf_socklen_t sock_opt_len;
+
+ sock_opt_len = sizeof(int);
+ if (getsockopt(socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_MAXSEG,
+ (char *)mss,
+ &sock_opt_len) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: get_tcp_info: getsockopt TCP_MAXSEG: errno %d\n",
+ errno);
+ fflush(where);
+ *mss = -1;
+ }
+#else
+ *mss = -1;
+#endif /* TCP_MAXSEG */
+}
+
+static
+void
+set_tcp_mss(SOCKET socket, int mss) {
+#ifdef TCP_MAXSEG
+ netperf_socklen_t sock_opt_len;
+
+ sock_opt_len = sizeof(int);
+ if ((setsockopt(socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_MAXSEG,
+ (const char *)&mss,
+ sock_opt_len) == SOCKET_ERROR) && (debug)) {
+ fprintf(where,
+ "netperf: %s: setsockopt TCP_MAXSEG: %s (errno %d)\n",
+ __FUNCTION__,
+ strerror(errno),
+ errno);
+ fflush(where);
+ }
+#else
+ if (debug) {
+ fprintf(where,
+ "netperf: %s platform does not know how to set TCP segment size\n",
+ __FUNCTION__);
+ fflush(where);
+ }
+
+#endif /* TCP_MAXSEG */
+}
+
+
+
+/* return a pointer to a completed addrinfo chain - prefer
+ data_address to controlhost and utilize the specified address
+ family */
+
+struct addrinfo *
+complete_addrinfo(char *controlhost, char *data_address, char *port, int family, int type, int protocol, int flags)
+{
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *temp_res;
+
+#define CHANGED_SOCK_TYPE 0x1
+#define CHANGED_PROTOCOL 0x2
+#define CHANGED_SCTP 0x4
+#define CHANGED_DCCP 0x8
+#define CHANGED_DCCP_SOCK 0x10
+
+ int change_info = 0;
+ static int change_warning_displayed = 0;
+
+ int count = 0;
+ int error = 0;
+
+ char *hostname;
+
+ /* take data-address over controlhost */
+ if (data_address)
+ hostname = data_address;
+ else
+ hostname = controlhost;
+
+ if (debug) {
+ fprintf(where,
+ "complete_addrinfo using hostname %s port %s family %s type %s prot %s flags 0x%x\n",
+ hostname,
+ port,
+ inet_ftos(family),
+ inet_ttos(type),
+ inet_ptos(protocol),
+ flags);
+ fflush(where);
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = type;
+ hints.ai_protocol = protocol;
+ hints.ai_flags = flags|AI_CANONNAME|AI_ADDRCONFIG;
+
+ count = 0;
+ do {
+ error = getaddrinfo((char *)hostname,
+ (char *)port,
+ &hints,
+ &res);
+ count += 1;
+ if (error == EAI_AGAIN) {
+ if (debug) {
+ fprintf(where,"Sleeping on getaddrinfo EAI_AGAIN\n");
+ fflush(where);
+ }
+ sleep(1);
+ }
+ /* while you see this kludge first, it is actually the second, the
+ first being the one for Solaris below. The need for this kludge
+ came after implementing the Solaris broken getaddrinfo kludge -
+ now we see a kludge in Linux getaddrinfo where if it is given
+ SOCK_STREAM and IPPROTO_SCTP it barfs with a -7
+ EAI_SOCKTYPE. so, we check if the error was EAI_SOCKTYPE and if
+ we were asking for IPPROTO_SCTP and if so, kludge, again... raj
+ 200?-10-13 and of course, requiring the kludge for SCTP, it is
+ no surprise that linux needs a kludge for DCCP...actually not
+ only does it need the ai_protocol kludge, it needs an
+ ai_socktype kludge too... sigh raj 2008-02-01 */
+#if defined(IPPROTO_SCTP) || defined (IPPROTO_DCCP)
+ if (EAI_SOCKTYPE == error
+#ifdef EAI_BADHINTS
+ || EAI_BADHINTS == error
+#endif
+ ) {
+ /* we ass-u-me this is the Linux getaddrinfo bug, clear the
+ hints.ai_protocol field, and set some state "remembering"
+ that we did this so the code for the Solaris kludge can do
+ the fix-up for us. also flip error over to EAI_AGAIN and
+ make sure we don't "count" this time around the loop. */
+#if defined(IPPROTO_DCCP) && defined(SOCK_DCCP)
+ /* only tweak on this one the second time around, after we've
+ kludged the ai_protocol field */
+ if ((hints.ai_socktype == SOCK_DCCP) &&
+ (hints.ai_protocol == 0)) {
+ change_info |= CHANGED_DCCP_SOCK;
+ hints.ai_socktype = 0;
+ /* we need to give it some sort of IPPROTO or it gets unhappy,
+ so for now, pick one from deep within the colon and use
+ IPPROTO_TCP */
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ if (hints.ai_protocol == IPPROTO_DCCP) {
+ change_info |= CHANGED_DCCP;
+ hints.ai_protocol = 0;
+ }
+
+#endif
+#if defined(IPPROTO_SCTP)
+ if (hints.ai_protocol == IPPROTO_SCTP) {
+ change_info |= CHANGED_SCTP;
+ hints.ai_protocol = 0;
+ }
+#endif
+
+ error = EAI_AGAIN;
+ count -= 1;
+ }
+#endif
+ } while ((error == EAI_AGAIN) && (count <= 5));
+
+ if (error) {
+ fprintf(where,
+ "complete_addrinfo: could not resolve '%s' port '%s' af %d"
+ "\n\tgetaddrinfo returned %d %s\n",
+ hostname,
+ port,
+ family,
+ error,
+ gai_strerror(error));
+ fflush(where);
+ exit(-1);
+ }
+
+ /* there exists at least one platform - Solaris 10 - that does not
+ seem to completely honor the ai_protocol and/or ai_socktype one
+ sets in the hints parm to the getaddrinfo call. so, we need to
+ walk the list of entries returned and if either of those do not
+ match what we asked for, we need to go ahead and set them
+ "correctly" this is based in part on some earlier SCTP-only code
+ from previous revisions. raj 2006-10-09 */
+
+ temp_res = res;
+
+ while (temp_res) {
+
+ if ((type) &&
+ (temp_res->ai_socktype != type)) {
+ change_info |= CHANGED_SOCK_TYPE;
+ if (debug) {
+ fprintf(where,
+ "WARNING! Changed bogus getaddrinfo socket type %d to %d\n",
+ temp_res->ai_socktype,
+ type);
+ fflush(where);
+ }
+ temp_res->ai_socktype = type;
+ }
+
+ if ((protocol) &&
+ (temp_res->ai_protocol != protocol)) {
+ change_info |= CHANGED_PROTOCOL;
+ if (debug) {
+ fprintf(where,
+ "WARNING! Changed bogus getaddrinfo protocol %d to %d\n",
+ temp_res->ai_protocol,
+ protocol);
+ fflush(where);
+ }
+ temp_res->ai_protocol = protocol;
+ }
+ temp_res = temp_res->ai_next;
+ }
+
+ if ((change_info & CHANGED_SOCK_TYPE) &&
+ !(change_warning_displayed & CHANGED_SOCK_TYPE)) {
+ change_warning_displayed |= CHANGED_SOCK_TYPE;
+ fprintf(where,
+ "WARNING! getaddrinfo returned a socket type which did not\n"
+ "match the requested type. Please contact your vendor for\n"
+ "a fix to this bug in getaddrinfo()\n");
+ fflush(where);
+ }
+
+ /* if we dropped the protocol hint, it would be for a protocol that
+ getaddrinfo() wasn't supporting yet, not for the bug that it took
+ our hint and still returned zero. raj 2006-10-16 */
+ /* as there is now an open bug against (Open)Solaris (id 6847733) on
+ this behaviour we will only emit this warning if debug is set
+ under Solaris and will continue to emit it under any circumstance
+ on other platforms should it arise. raj 2009-06-03 */
+ /* since it has now been two years since that bug was filed, it
+ should be resolved by now, so the "out" given to Sun should no
+ longer be necessary. either folks are running with the fix or
+ they need to get the fix. raj 2011-07-06 */
+ if ((change_info & CHANGED_PROTOCOL) &&
+ !(change_warning_displayed & CHANGED_PROTOCOL) &&
+ (hints.ai_protocol != 0)) {
+ change_warning_displayed |= CHANGED_PROTOCOL;
+ fprintf(where,
+ "WARNING! getaddrinfo returned a protocol other than the\n"
+ "requested protocol. Please contact your vendor for\n"
+ "a fix to this bug in getaddrinfo()\n");
+ fflush(where);
+ }
+
+ if ((change_info & CHANGED_SCTP) &&
+ !(change_warning_displayed & CHANGED_SCTP)) {
+ change_warning_displayed |= CHANGED_SCTP;
+ fprintf(where,
+ "WARNING! getaddrinfo on this platform does not accept IPPROTO_SCTP!\n"
+ "Please contact your vendor for a fix to this bug in getaddrinfo().\n");
+ fflush(where);
+ }
+
+ if ((change_info & CHANGED_DCCP) &&
+ !(change_warning_displayed & CHANGED_DCCP)) {
+ change_warning_displayed |= CHANGED_DCCP;
+ fprintf(where,
+ "WARNING! getaddrinfo on this platform does not accept IPPROTO_DCCP!\n"
+ "Please contact your vendor for a fix to this bug in getaddrinfo().\n");
+ fflush(where);
+ }
+
+
+ if (debug) {
+ dump_addrinfo(where, res, hostname, port, family);
+ }
+
+ return(res);
+}
+
+void
+complete_addrinfos(struct addrinfo **remote,struct addrinfo **local, char remote_host[], int type, int protocol, int flags) {
+
+ if (remote_data_family == AF_UNSPEC) {
+ remote_data_family = control_family;
+ }
+
+ *remote = complete_addrinfo(remote_host,
+ remote_data_address,
+ remote_data_port,
+ remote_data_family,
+ type,
+ protocol,
+ flags);
+
+ /* OK, if the user has not specified a local data endpoint address
+ (test-specific -L), pick the local data endpoint address based on
+ the remote data family info (test-specific -H or -4 or -6
+ option). if the user has not specified remote data addressing
+ info (test-specific -H, -4 -6) pick something based on the local
+ control connection address (ie the global -L option). */
+
+ if (NULL == local_data_address) {
+ local_data_address = malloc(HOSTNAMESIZE);
+ if (NULL == remote_data_address) {
+ if (debug) {
+ fprintf(where,
+ "local_data_address not set, using local_host_name of '%s'\n",
+ local_host_name);
+ fflush(where);
+ }
+ strcpy(local_data_address,local_host_name);
+ }
+ else {
+ if (debug) {
+ fprintf(where,
+ "local_data_address not set, using address family info\n");
+ fflush(where);
+ }
+ /* by default, use 0.0.0.0 - assume IPv4 */
+ strcpy(local_data_address,"0.0.0.0");
+#if defined(AF_INET6)
+ if ((AF_INET6 == local_data_family) ||
+ ((AF_UNSPEC == local_data_family) &&
+ (AF_INET6 == remote_data_family)) ||
+ ((AF_UNSPEC == local_data_family) &&
+ (AF_INET6 == (*remote)->ai_family))) {
+ strcpy(local_data_address,"::0");
+ }
+#endif
+ }
+ }
+
+ *local = complete_addrinfo("what to put here?",
+ local_data_address,
+ local_data_port,
+ local_data_family,
+ type,
+ protocol,
+ flags|AI_PASSIVE);
+
+ /* OK, at this point, if remote_data_address is NULL, we know that
+ we used the value of remote_host (the control connection) for the
+ remote, which means we can/should set remote_data_address to
+ remote_host so the "omni" output routines can use that global
+ variable. at least i think I can get away with that :) I'm sure
+ that at some point I'll find-out that I need to allocate
+ something for it rather than mess with the pointers, but that can
+ wait. famous last words of raj 2008-01-25 */
+ if (remote_data_address == NULL)
+ remote_data_address = remote_host;
+}
+
+void
+set_hostname_and_port(char *hostname, char *portstr, int family, int port)
+{
+ strcpy(hostname,"0.0.0.0");
+#if defined AF_INET6
+ if (AF_INET6 == family) {
+ strcpy(hostname,"::0");
+ }
+#endif
+
+ sprintf(portstr, "%u", port);
+
+}
+
+static unsigned short
+get_port_number(struct addrinfo *res)
+{
+ switch(res->ai_family) {
+ case AF_INET: {
+ struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr;
+ return(ntohs(foo->sin_port));
+ break;
+ }
+#if defined(AF_INET6)
+ case AF_INET6: {
+ struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr;
+ return(ntohs(foo->sin6_port));
+ break;
+ }
+#endif
+ default:
+ fprintf(where,
+ "Given Unexpected Address Family of %u\n",res->ai_family);
+ fflush(where);
+ exit(-1);
+ }
+}
+
+static void
+extract_inet_address_and_port(struct addrinfo *res, void *addr, int len, int *port)
+{
+ switch(res->ai_family) {
+ case AF_INET: {
+ struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr;
+ *port = foo->sin_port;
+ memcpy(addr,&(foo->sin_addr),min(len,sizeof(foo->sin_addr)));
+ break;
+ }
+#if defined(AF_INET6)
+ case AF_INET6: {
+ struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr;
+ *port = foo->sin6_port;
+ memcpy(addr,&(foo->sin6_addr),min(len,sizeof(foo->sin6_addr)));
+ break;
+ }
+#endif
+ default:
+ *port = 0xDEADBEEF;
+ strncpy(addr,"UNKN FAMILY",len);
+ }
+}
+
+/* this routine will set the port number of the sockaddr in the
+ addrinfo to the specified value, based on the address family */
+void
+set_port_number(struct addrinfo *res, unsigned short port)
+{
+ switch(res->ai_family) {
+ case AF_INET:
+#if defined(AF_RDS)
+ case AF_RDS:
+#endif
+ {
+ struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr;
+ foo->sin_port = htons(port);
+ break;
+ }
+#if defined(AF_INET6)
+ case AF_INET6: {
+ struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr;
+ foo->sin6_port = htons(port);
+ break;
+ }
+#endif
+ default:
+ fprintf(where,
+ "set_port_number Unexpected Address Family of %u\n",res->ai_family);
+ fflush(where);
+ exit(-1);
+ }
+}
+
+/* stuff the address family, port number and address into a
+ sockaddr. for now, we will go ahead and zero-out the sockaddr
+ first */
+void
+set_sockaddr_family_addr_port(struct sockaddr_storage *sockaddr, int family, void *addr, int port) {
+
+ memset(sockaddr,0,sizeof(struct sockaddr_storage));
+
+ switch (family) {
+#if defined(AF_RDS)
+ case AF_RDS:
+#endif
+ case AF_INET: {
+ struct sockaddr_in *foo = (struct sockaddr_in *)sockaddr;
+ foo->sin_port = htons((unsigned short) port);
+ foo->sin_family = (unsigned short) family;
+ memcpy(&(foo->sin_addr),addr,sizeof(foo->sin_addr));
+ *(int *)addr = htonl(*(int *)addr);
+ break;
+ }
+#if defined(AF_INET6)
+ case AF_INET6: {
+ struct sockaddr_in6 *foo = (struct sockaddr_in6 *)sockaddr;
+ foo->sin6_port = htons((unsigned short) port);
+ foo->sin6_family = (unsigned short) family;
+ memcpy(&(foo->sin6_addr),addr,sizeof(foo->sin6_addr));
+ break;
+ }
+#endif
+ default:
+ fprintf(where,
+ "set_sockaddr_family_addr_port Unexpected Address Family of %u\n",family);
+ fflush(where);
+ exit(-1);
+ }
+}
+
+/* pull the port and address out of the sockaddr in host format */
+int
+get_sockaddr_family_addr_port(struct sockaddr_storage *sockaddr, int family, void *addr, int *port)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sockaddr;
+
+ int ret = 0;
+ if (sin->sin_family != family) {
+ fprintf(where,
+ "get_sockaddr_family_addr_port family mismatch %d vs %d\n",
+ sin->sin_family,
+ family);
+ fflush(where);
+ return -1;
+ }
+
+ switch(family) {
+#if defined(AF_RDS)
+ case AF_RDS:
+#endif
+ case AF_INET: {
+ *port = ntohs(sin->sin_port);
+ memcpy(addr,&(sin->sin_addr),sizeof(sin->sin_addr));
+ if (*(int *)addr == INADDR_ANY) ret = 1;
+ *(int *)addr = ntohl(*(int *)addr);
+ break;
+ }
+#ifdef AF_INET6
+ case AF_INET6: {
+ int i;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sockaddr;
+ *port = ntohs(sin6->sin6_port);
+ ret = 1;
+ for (i=0; i < sizeof(struct in6_addr); i++)
+ if (sin6->sin6_addr.s6_addr[i] != 0) ret=0;
+ memcpy(addr,&(sin6->sin6_addr), sizeof(sin6->sin6_addr));
+ break;
+ }
+#endif
+ default:
+ fprintf(where,
+ "get_sockaddr_family_addr_port: Unexpected Address Family of %u\n",family);
+ fflush(where);
+ exit(-1);
+ }
+ return ret;
+}
+
+
+int
+set_socket_tos(SOCKET sock, int family, int socket_tos) {
+
+ int my_tos = -3;
+ netperf_socklen_t sock_opt_len;
+
+ switch (family) {
+#if defined(IP_TOS)
+ case AF_INET:
+ /* should I mask-away anything above the byte? */
+ my_tos = socket_tos;
+ if (setsockopt(sock,
+ IPPROTO_IP,
+ IP_TOS,
+ (const char *)&my_tos,sizeof(my_tos)) == SOCKET_ERROR) {
+ fprintf(where,
+ "%s ip_tos failed with %s (errno %d)\n",
+ __FUNCTION__,
+ strerror(errno),
+ errno);
+ fflush(where);
+ my_tos = -2;
+ }
+ else {
+ sock_opt_len = sizeof(my_tos);
+ getsockopt(sock,
+ IPPROTO_IP,
+ IP_TOS,
+ (char *)&my_tos,
+ &sock_opt_len);
+ }
+ break;
+#endif
+#if defined(IPV6_TCLASS)
+ case AF_INET6:
+ /* should I mask-away anything above the byte? */
+ my_tos = socket_tos;
+ if (setsockopt(sock,
+ IPPROTO_IPV6,
+ IPV6_TCLASS,
+ (const char *)&my_tos,sizeof(my_tos)) == SOCKET_ERROR) {
+ fprintf(where,
+ "%s ip_tos failed with %s (errno %d)\n",
+ __FUNCTION__,
+ strerror(errno),
+ errno);
+ fflush(where);
+ my_tos = -2;
+ }
+ else {
+ sock_opt_len = sizeof(my_tos);
+ getsockopt(sock,
+ IPPROTO_IPV6,
+ IPV6_TCLASS,
+ (char *)&my_tos,
+ &sock_opt_len);
+ }
+ break;
+#endif
+ }
+ return my_tos;
+}
+
+
+ /* This routine will create a data (listen) socket with the
+ apropriate options set and return it to the caller. this replaces
+ all the duplicate code in each of the test routines and should help
+ make things a little easier to understand. since this routine can be
+ called by either the netperf or netserver programs, all output
+ should be directed towards "where." family is generally AF_INET and
+ type will be either SOCK_STREAM or SOCK_DGRAM. This routine will
+ also be used by the "SCTP" tests, hence the slightly strange-looking
+ SCTP stuff in the classic bsd sockets test file... vlad/raj
+ 2005-03-15 */
+
+SOCKET
+create_data_socket(struct addrinfo *res)
+{
+
+ SOCKET temp_socket;
+ int one = 1;
+ int on = 1;
+ netperf_socklen_t sock_opt_len;
+
+ /*set up the data socket */
+ temp_socket = socket(res->ai_family,
+ res->ai_socktype,
+ res->ai_protocol);
+
+ if (temp_socket == INVALID_SOCKET){
+ fprintf(where,
+ "netperf: create_data_socket: socket: errno %d fam %s type %s prot %s errmsg %s\n",
+ errno,
+ inet_ftos(res->ai_family),
+ inet_ttos(res->ai_socktype),
+ inet_ptos(res->ai_protocol),
+ strerror(errno));
+ fflush(where);
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"create_data_socket: socket %d obtained...\n",temp_socket);
+ fflush(where);
+ }
+
+ /* Modify the local socket size. The reason we alter the send buffer
+ size here rather than when the connection is made is to take care
+ of decreases in buffer size. Decreasing the window size after
+ connection establishment is a TCP no-no. Also, by setting the
+ buffer (window) size before the connection is established, we can
+ control the TCP MSS (segment size). The MSS is never (well, should
+ never be) more that 1/2 the minimum receive buffer size at each
+ half of the connection. This is why we are altering the receive
+ buffer size on the sending size of a unidirectional transfer. If
+ the user has not requested that the socket buffers be altered, we
+ will try to find-out what their values are. If we cannot touch the
+ socket buffer in any way, we will set the values to -1 to indicate
+ that. */
+
+ /* all the oogy nitty gritty stuff moved from here into the routine
+ being called below, per patches from davidm to workaround the bug
+ in Linux getsockopt(). raj 2004-06-15 */
+ set_sock_buffer (temp_socket, SEND_BUFFER, lss_size_req, &lss_size);
+ set_sock_buffer (temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size);
+
+ /* now, we may wish to enable the copy avoidance features on the */
+ /* local system. of course, this may not be possible... */
+
+#ifdef SO_RCV_COPYAVOID
+ /* this is ancient vestigial HP-UX code that should probably go away
+ one day */
+ if (loc_rcvavoid) {
+ if (setsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_RCV_COPYAVOID,
+ (const char *)&loc_rcvavoid,
+ sizeof(int)) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: create_data_socket: Could not enable receive copy avoidance");
+ fflush(where);
+ loc_rcvavoid = 0;
+ }
+ }
+#endif
+
+#ifdef SO_SND_COPYAVOID
+ if (loc_sndavoid) {
+ if (setsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_SND_COPYAVOID,
+ (const char *)&loc_sndavoid,
+ sizeof(int)) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: create_data_socket: Could not enable send copy avoidance");
+ fflush(where);
+ loc_sndavoid = 0;
+ }
+ }
+#endif
+
+ /* Now, we will see about setting the TCP_NODELAY flag on the local */
+ /* socket. We will only do this for those systems that actually */
+ /* support the option. If it fails, note the fact, but keep going. */
+ /* If the user tries to enable TCP_NODELAY on a UDP socket, this */
+ /* will cause an error to be displayed */
+
+ /* well..... long ago and far away that would have happened, in
+ particular because we would always use IPPROTO_TCP here.
+ however, now we are using res->ai_protocol, which will be
+ IPPROT_UDP, and while HP-UX, and I suspect no-one else on the
+ planet has a UDP_mumble option that overlaps with TCP_NODELAY,
+ sure as knuth made little green programs, linux has a UDP_CORK
+ option that is defined as a value of 1, which is the same a
+ TCP_NODELAY under Linux. So, when asking for -D and
+ "TCP_NODELAY" under Linux, we are actually setting UDP_CORK
+ instead of getting an error like every other OS on the
+ planet. joy and rupture. this stops a UDP_RR test cold sooo we
+ have to make sure that res->ai_protocol actually makes sense for
+ a _NODELAY setsockopt() or a UDP_RR test on Linux where someone
+ mistakenly sets -D will hang. raj 2005-04-21 */
+
+#if defined(TCP_NODELAY) || defined(SCTP_NODELAY)
+ if ((loc_nodelay) && (res->ai_protocol != IPPROTO_UDP)) {
+
+ /* strictly speaking, since the if defined above is an OR, we
+ should probably check against TCP_NODELAY being defined here.
+ however, the likelihood of SCTP_NODELAY being defined and
+ TCP_NODELAY _NOT_ being defined is, probably :), epsilon. raj
+ 2005-03-15 */
+
+ int option = TCP_NODELAY;
+
+ /* I suspect that WANT_SCTP would suffice here since that is the
+ only time we would have called getaddrinfo with a hints asking
+ for SCTP, but just in case there is an SCTP implementation out
+ there _without_ SCTP_NODELAY... raj 2005-03-15 */
+ /* change this to IPPROTO_SCTP rather than WANT_SCTP to better fit
+ with the modus operandi of the new "omni" tests. raj
+ 2008-02-04 */
+#if defined(IPPROTO_SCTP) && defined(SCTP_NODELAY)
+ if (IPPROTO_SCTP == res->ai_protocol) {
+ option = SCTP_NODELAY;
+ }
+#endif
+
+ one = 1;
+ if(setsockopt(temp_socket,
+ res->ai_protocol,
+ option,
+ (char *)&one,
+ sizeof(one)) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: create_data_socket: nodelay: errno %d\n",
+ errno);
+ fflush(where);
+ }
+
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: create_data_socket: [TCP|SCTP]_NODELAY requested...\n");
+ fflush(where);
+ }
+ }
+#else /* TCP_NODELAY */
+
+ loc_nodelay = 0;
+
+#endif /* TCP_NODELAY */
+
+ if ((transport_mss_req != -1) && (IPPROTO_TCP == res->ai_protocol)) {
+ set_tcp_mss(temp_socket,transport_mss_req);
+ }
+
+#if defined(TCP_CORK)
+
+ if (loc_tcpcork > 0) {
+ /* the user wishes for us to set TCP_CORK on the socket */
+ if (setsockopt(temp_socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_CORK,
+ (char *)&one,
+ sizeof(one)) == SOCKET_ERROR) {
+ perror("netperf: create_data_socket: tcp_cork");
+ exit(1);
+ }
+ if (debug) {
+ fprintf(where,"create_data_socket: tcp_cork...\n");
+ }
+ }
+
+#endif /* TCP_CORK */
+
+ /* well, after Knuth only knows how many years, I have finally
+ decided to enable setting SO_KEEPALIVE on the data socket. 99
+ times out of 10 this should not be necessary, but that 100th time,
+ perhaps when netperf is being (ab)used by functional testers, may
+ benefit from it. And it may help clean-up some lingering
+ netservers from time to time. raj 2011-06-29 */
+
+#if defined(SO_KEEPALIVE)
+
+ if (want_keepalive) {
+ if (setsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_KEEPALIVE,
+ (const char *)&on,
+ sizeof(on)) < 0) {
+ if (debug) {
+ fprintf(where,
+ "%s: unable to set SO_KEEPALIVE on data socket: %s (errno %d)\n",
+ __FUNCTION__,
+ strerror(errno),
+ errno);
+ fflush(where);
+ }
+ }
+ }
+
+#endif /* SO_KEEPALIVE */
+
+ /* since some of the UDP tests do not do anything to cause an
+ implicit bind() call, we need to be rather explicit about our
+ bind() call here. even if the address and/or the port are zero
+ (INADDR_ANY etc). raj 2004-07-20 */
+
+ if (setsockopt(temp_socket,
+#ifdef IPPROTO_DCCP
+ /* it is REALLY SILLY THAT THIS SHOULD BE NEEDED!! I
+ should be able to use SOL_SOCKET for this just
+ like TCP and SCTP */
+ /* IT IS EVEN SILLIER THAT THERE COULD BE SYSTEMS
+ WITH IPPROTO_DCCP and no SOL_DCCP */
+#ifndef SOL_DCCP
+#define SOL_DCCP SOL_SOCKET
+#define NETPERF_NEED_CLEANUP 1
+#endif
+ (res->ai_protocol == IPPROTO_DCCP) ? SOL_DCCP : SOL_SOCKET,
+#ifdef NETPERF_NEED_CLEANUP
+#undef SOL_DCCP
+#undef NETPERF_NEED_CLEANUP
+#endif
+
+#else
+ SOL_SOCKET,
+#endif
+ SO_REUSEADDR,
+ (const char *)&on,
+ sizeof(on)) < 0) {
+ fprintf(where,
+ "netperf: create_data_socket: SO_REUSEADDR failed %d\n",
+ errno);
+ fflush(where);
+ }
+
+ if (bind(temp_socket,
+ res->ai_addr,
+ res->ai_addrlen) < 0) {
+ if (debug) {
+ fprintf(where,
+ "netperf: create_data_socket: data socket bind failed: %s (errno %d)\n",
+ strerror(errno),
+ errno);
+ fprintf(where," port: %d\n",get_port_number(res));
+ fflush(where);
+ }
+ }
+
+ /* this one is a slightly grudgingly added backside covering for
+ those folks who (ab)use netperf as a functional testing tool, and
+ further compound that error by running tests on systems also
+ connected to their site networks, and then compound it even
+ further compound it by running UDP_STREAM tests over links that
+ generate link-down events and so cause the traffic to be sent out
+ the default route into their corporate network... frankly such
+ people should not be allowed to run netperf in the first place
+ but there we are... raj 20091026 */
+
+#if defined (SO_DONTROUTE)
+ if (!routing_allowed) {
+ if (setsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_DONTROUTE,
+ (char *)&one,
+ sizeof(one)) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: create_data_socket: so_dontroute: errno %d\n",
+ errno);
+ fflush(where);
+ }
+ }
+#endif
+
+#if defined(SO_PRIORITY)
+ if (local_socket_prio >= 0) {
+ if (setsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_PRIORITY,
+ &local_socket_prio,
+ sizeof(int)) == SOCKET_ERROR) {
+ fprintf(where,
+ "netperf: create_data_socket: so_priority: errno %d\n",
+ errno);
+ fflush(where);
+ local_socket_prio = -2;
+ }
+ else {
+ sock_opt_len = 4;
+ getsockopt(temp_socket,
+ SOL_SOCKET,
+ SO_PRIORITY,
+ &local_socket_prio,
+ &sock_opt_len);
+ }
+ }
+#else
+ local_socket_prio = -3;
+#endif
+
+#if defined (IP_TOS) || defined(IPV6_TCLASS)
+ if (local_socket_tos > 0)
+ local_socket_tos = set_socket_tos(temp_socket,res->ai_family, local_socket_tos);
+#endif
+
+ return temp_socket;
+}
+
+#ifdef KLUDGE_SOCKET_OPTIONS
+
+
+ /* This routine is for those BROKEN systems which do not correctly */
+ /* pass socket attributes through calls such as accept(). It should */
+ /* only be called for those broken systems. I *really* don't want to */
+ /* have this, but even broken systems must be measured. raj 11/95 */
+void
+kludge_socket_options(int temp_socket)
+{
+
+ set_sock_buffer(temp_socket, SEND_BUFFER, lss_size_req, &lss_size);
+ set_sock_buffer(temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size);
+
+ /* now, we may wish to enable the copy avoidance features on the */
+ /* local system. of course, this may not be possible... */
+ /* those calls were only valid for HP-UX, and I know that HP-UX is */
+ /* written correctly, and so we do not need to include those calls */
+ /* in this kludgy routine. raj 11/95 */
+
+
+ /* Now, we will see about setting the TCP_NODELAY flag on the local */
+ /* socket. We will only do this for those systems that actually */
+ /* support the option. If it fails, note the fact, but keep going. */
+ /* If the user tries to enable TCP_NODELAY on a UDP socket, this */
+ /* will cause an error to be displayed */
+
+#ifdef TCP_NODELAY
+ if (loc_nodelay) {
+ one = 1;
+ if(setsockopt(temp_socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_NODELAY,
+ (char *)&one,
+ sizeof(one)) == SOCKET_ERROR) {
+ fprintf(where,"netperf: kludge_socket_options: nodelay: errno %d\n",
+ errno);
+ fflush(where);
+ }
+
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: kludge_socket_options: TCP_NODELAY requested...\n");
+ fflush(where);
+ }
+ }
+#else /* TCP_NODELAY */
+
+ loc_nodelay = 0;
+
+#endif /* TCP_NODELAY */
+
+ }
+
+#endif /* KLUDGE_SOCKET_OPTIONS */
+
+
+static void *
+get_address_address(struct addrinfo *info)
+{
+ struct sockaddr_in *sin;
+#if defined(AF_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch(info->ai_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)info->ai_addr;
+ return(&(sin->sin_addr));
+ break;
+#if defined(AF_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)info->ai_addr;
+ return(&(sin6->sin6_addr));
+ break;
+#endif
+ default:
+ fprintf(stderr,"we never expected to get here in get_address_address\n");
+ fflush(stderr);
+ exit(-1);
+ }
+}
+
+#if defined(WIN32)
+#if !defined(InetNtop)
+/* +*+ Why isn't this in the winsock headers yet? */
+const char *
+inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+#endif
+
+/* This routine is a generic test header printer for the topmost header */
+void
+print_top_test_header(char test_name[], struct addrinfo *source, struct addrinfo *destination)
+{
+
+ char *address_buf;
+
+#ifdef AF_INET6
+ address_buf = malloc(INET6_ADDRSTRLEN);
+#else
+ address_buf = malloc(16); /* magic constant */
+#endif
+
+ if (address_buf == NULL) {
+ fprintf(where,"Unable to allocate address_buf\n");
+ fflush(where);
+ exit(1);
+ }
+
+ /* we want to have some additional, interesting information in the
+ headers. we know some of it here, but not all, so we will only
+ print the test title here and will print the results titles after
+ the test is finished */
+ fprintf(where,"%s",test_name);
+
+ address_buf[0] = '\0';
+ inet_ntop(source->ai_family,get_address_address(source),address_buf,sizeof(address_buf));
+ fprintf(where,
+ " from %s (%s) port %u %s",
+ source->ai_canonname,
+ address_buf,
+ get_port_number(source),
+ inet_ftos(source->ai_family));
+
+ address_buf[0] = '\0';
+ inet_ntop(destination->ai_family,get_address_address(destination),address_buf,sizeof(address_buf));
+ fprintf(where,
+ " to %s (%s) port %u %s",
+ destination->ai_canonname,
+ address_buf,
+ get_port_number(destination),
+ inet_ftos(destination->ai_family));
+
+ if (iteration_max > 1) {
+ fprintf(where,
+ " : +/-%.3f%% @ %2d%% conf. %s",
+ interval/0.02,
+ confidence_level,
+ result_confidence_only ? " on result only" : "");
+ }
+ if ((loc_nodelay > 0) || (rem_nodelay > 0)) {
+ fprintf(where," : nodelay");
+ }
+ if ((loc_sndavoid > 0) ||
+ (loc_rcvavoid > 0) ||
+ (rem_sndavoid > 0) ||
+ (rem_rcvavoid > 0)) {
+ fprintf(where," : copy avoidance");
+ }
+
+ if (no_control) {
+ fprintf(where," : no control");
+ }
+
+#ifdef WANT_HISTOGRAM
+ fprintf(where," : histogram");
+#endif /* WANT_HISTOGRAM */
+
+#ifdef WANT_INTERVALS
+#ifndef WANT_SPIN
+ fprintf(where," : interval");
+#else
+ fprintf(where," : spin interval");
+#endif
+#endif /* WANT_INTERVALS */
+
+#ifdef DIRTY
+ fprintf(where," : dirty data");
+#endif /* DIRTY */
+#ifdef WANT_DEMO
+ fprintf(where," : demo");
+#endif
+#ifdef WANT_FIRST_BURST
+ /* a little hokey perhaps, but we really only want this to be
+ emitted for tests where it actually is used, which means a
+ "REQUEST/RESPONSE" test. raj 2005-11-10 */
+ if (strstr(test_name,"REQUEST/RESPONSE")) {
+ fprintf(where," : first burst %d",first_burst_size);
+ }
+#endif
+ if (cpu_binding_requested) {
+ fprintf(where," : cpu bind");
+ }
+ fprintf(where,"\n");
+
+ free(address_buf);
+}
+
+/* if WANT_MIGRATION is defined, we will use the send_tcp_stream()
+ call in src/nettest_omni.c */
+#ifndef WANT_MIGRATION
+
+/* This routine implements the TCP unidirectional data transfer test */
+/* (a.k.a. stream) for the sockets interface. It receives its */
+/* parameters via global variables from the shell and writes its */
+/* output to the standard output. */
+
+void
+send_tcp_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 tcp_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... time passes, and 64 bit types do indeed become common. */
+#if defined(WIN32) && _MSC_VER <= 1200
+ __int64 local_bytes_sent = 0;
+#else
+ unsigned long long local_bytes_sent = 0;
+#endif
+
+ 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 tcp_stream_request_struct *tcp_stream_request;
+ struct tcp_stream_response_struct *tcp_stream_response;
+ struct tcp_stream_results_struct *tcp_stream_result;
+
+ tcp_stream_request =
+ (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data;
+ tcp_stream_response =
+ (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data;
+ tcp_stream_result =
+ (struct tcp_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("TCP 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_stream: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_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_TCP_STREAM;
+ tcp_stream_request->send_buf_size = rss_size_req;
+ tcp_stream_request->recv_buf_size = rsr_size_req;
+ tcp_stream_request->receive_size = recv_size;
+ tcp_stream_request->no_delay = rem_nodelay;
+ tcp_stream_request->recv_alignment = remote_recv_align;
+ tcp_stream_request->recv_offset = remote_recv_offset;
+ tcp_stream_request->measure_cpu = remote_cpu_usage;
+ tcp_stream_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ tcp_stream_request->test_length = test_time;
+ }
+ else {
+ tcp_stream_request->test_length = test_bytes;
+ }
+ tcp_stream_request->so_rcvavoid = rem_rcvavoid;
+ tcp_stream_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ tcp_stream_request->dirty_count = rem_dirty_count;
+ tcp_stream_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ tcp_stream_request->port = atoi(remote_data_port);
+ tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_tcp_stream: requesting TCP stream test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant
+ socket parameters for this test type. We will put them back
+ into the variables here so they can be displayed if desired.
+ The remote will have calibrated CPU if necessary, and will
+ have done all the needed set-up we will have calibrated the
+ cpu locally before sending the request, and will grab the
+ counter value right after the connect returns. The remote
+ will grab the counter right after the accept call. This saves
+ the hassle of extra messages being sent for the TCP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_stream_response->recv_buf_size;
+ rss_size = tcp_stream_response->send_buf_size;
+ rem_nodelay = tcp_stream_response->no_delay;
+ remote_cpu_usage= tcp_stream_response->measure_cpu;
+ remote_cpu_rate = tcp_stream_response->cpu_rate;
+
+ /* we have to make sure that the server port number is in
+ network order */
+ set_port_number(remote_res,
+ (short)tcp_stream_response->data_port_number);
+
+ rem_rcvavoid = tcp_stream_response->so_rcvavoid;
+ rem_sndavoid = tcp_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_tcp_stream: data socket connect failed");
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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) {
+ demo_first_timestamp();
+ }
+#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 TCP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ tcp_mss = -1;
+ get_tcp_info(send_socket,&tcp_mss);
+ }
+
+ if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR && !times_up) {
+ perror("netperf: cannot shutdown tcp 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 defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ 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 reporting results for %.2f seconds\n",
+ tcp_stream_result->elapsed_time);
+ }
+ 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
+ TCP stream test, that the two numbers should be *very*
+ close... We calculate bytes_sent regardless of the way the
+ test length was controlled. If it was time, we needed to,
+ and if it was by bytes, the user may have specified a number
+ of bytes that wasn't a multiple of the send_size, so we
+ really didn't send what he asked for ;-) */
+
+ bytes_sent = ntohd(tcp_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 = tcp_stream_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ tcp_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(tcp_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 */
+ /* TCP 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)tcp_stream_result->recv_calls,
+ tcp_stream_result->recv_calls);
+ fprintf(where,
+ ksink_fmt2,
+ tcp_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 TCP 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_tcp_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 %s\n";
+
+ char *cpu_title = "\
+Recv Send Send Utilization Service Demand\n\
+Socket Socket Message Elapsed Recv Send Recv Send\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 tcp_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. of
+ course, time passes and they do become common.
+ */
+ double bytes_sent = 0.0;
+
+#if defined(WIN32) && (_MSC_VER < 1200)
+ __int64 local_bytes_recvd = 0;
+#else
+ unsigned long long local_bytes_recvd = 0;
+#endif
+
+ 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 tcp_maerts_request_struct *tcp_maerts_request;
+ struct tcp_maerts_response_struct *tcp_maerts_response;
+ struct tcp_maerts_results_struct *tcp_maerts_result;
+
+ tcp_maerts_request =
+ (struct tcp_maerts_request_struct *)netperf_request.content.test_specific_data;
+ tcp_maerts_response =
+ (struct tcp_maerts_response_struct *)netperf_response.content.test_specific_data;
+ tcp_maerts_result =
+ (struct tcp_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("TCP 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 */
+ recv_socket = create_data_socket(local_res);
+
+ if (recv_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_maerts: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_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_TCP_MAERTS;
+ tcp_maerts_request->send_buf_size = rss_size_req;
+ tcp_maerts_request->recv_buf_size = rsr_size_req;
+ tcp_maerts_request->send_size = send_size;
+ tcp_maerts_request->no_delay = rem_nodelay;
+ tcp_maerts_request->send_alignment = remote_send_align;
+ tcp_maerts_request->send_offset = remote_send_offset;
+ tcp_maerts_request->measure_cpu = remote_cpu_usage;
+ tcp_maerts_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ tcp_maerts_request->test_length = test_time;
+ }
+ else {
+ tcp_maerts_request->test_length = test_bytes;
+ }
+ tcp_maerts_request->so_rcvavoid = rem_rcvavoid;
+ tcp_maerts_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ tcp_maerts_request->dirty_count = rem_dirty_count;
+ tcp_maerts_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ tcp_maerts_request->port = atoi(remote_data_port);
+ tcp_maerts_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_tcp_maerts: requesting TCP 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 TCP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_maerts_response->recv_buf_size;
+ rss_size = tcp_maerts_response->send_buf_size;
+ rem_nodelay = tcp_maerts_response->no_delay;
+ remote_cpu_usage= tcp_maerts_response->measure_cpu;
+ remote_cpu_rate = tcp_maerts_response->cpu_rate;
+ send_size = tcp_maerts_response->send_size;
+
+ /* we have to make sure that the server port number is in
+ network order */
+ set_port_number(remote_res,
+ (short)tcp_maerts_response->data_port_number);
+ rem_rcvavoid = tcp_maerts_response->so_rcvavoid;
+ rem_sndavoid = tcp_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_tcp_maerts: data socket connect failed");
+ exit(1);
+ }
+
+#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 = recv_socket;
+#endif /* WIN32 */
+
+ /* 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 TCP_MAERTS test. sorry. raj 2002-06-21 */
+ printf("netperf: send_tcp_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) {
+ demo_first_timestamp();
+ }
+#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_tcp_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 TCP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ tcp_mss = -1;
+ get_tcp_info(recv_socket,&tcp_mss);
+ }
+
+ if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) {
+ perror("netperf: cannot shutdown tcp maerts socket");
+ exit(1);
+ }
+
+ stop_timer();
+
+#if defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ /* 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
+ TCP 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(tcp_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 = tcp_maerts_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ tcp_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(tcp_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 */
+ /* TCP 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)tcp_maerts_result->send_calls,
+ tcp_maerts_result->send_calls);
+ fprintf(where,
+ ksink_fmt2,
+ tcp_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 */
+ }
+
+}
+#endif /* WANT_MIGRATION */
+
+
+/* this routine implements the TCP_MSS test. All it does is pretend
+ to be a TCP_STREAM test and report the TCP_MSS for the data
+ connection. No actual data is transferred. raj 2007-11-07
+*/
+void
+send_tcp_mss(char remote_host[])
+{
+
+ char *mss_title = "\
+Maximum\n\
+Segment\n\
+Size (bytes)\n\n";
+
+ char *mss_fmt_0 =
+ "%d %s\n";
+
+ SOCKET send_socket;
+ int tcp_mss = -1; /* possibly uninitialized on printf far below */
+
+ struct addrinfo *remote_res;
+ struct addrinfo *local_res;
+
+ struct tcp_stream_request_struct *tcp_stream_request;
+ struct tcp_stream_response_struct *tcp_stream_response;
+ struct tcp_stream_results_struct *tcp_stream_result;
+
+ tcp_stream_request =
+ (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data;
+ tcp_stream_response =
+ (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data;
+ tcp_stream_result =
+ (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+ /* since we are now disconnected from the code that established the */
+ /* control socket, and since we want to be able to use different */
+ /* protocols and such, we are passed the name of the remote host and */
+ /* must turn that into the test specific addressing information. */
+
+ /* 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("TCP MSS TEST",local_res,remote_res);
+ }
+
+ /*set up the data socket */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_stream: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_stream: send_socket obtained...\n");
+ }
+
+
+ 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_TCP_STREAM;
+ tcp_stream_request->send_buf_size = rss_size_req;
+ tcp_stream_request->recv_buf_size = rsr_size_req;
+ tcp_stream_request->receive_size = recv_size;
+ tcp_stream_request->no_delay = rem_nodelay;
+ tcp_stream_request->recv_alignment = remote_recv_align;
+ tcp_stream_request->recv_offset = remote_recv_offset;
+ tcp_stream_request->measure_cpu = remote_cpu_usage;
+ tcp_stream_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ tcp_stream_request->test_length = test_time;
+ }
+ else {
+ tcp_stream_request->test_length = test_bytes;
+ }
+ tcp_stream_request->so_rcvavoid = rem_rcvavoid;
+ tcp_stream_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ tcp_stream_request->dirty_count = rem_dirty_count;
+ tcp_stream_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ tcp_stream_request->port = atoi(remote_data_port);
+ tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_tcp_mss: requesting TCP stream test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant
+ socket parameters for this test type. We will put them back
+ into the variables here so they can be displayed if desired.
+ The remote will have calibrated CPU if necessary, and will
+ have done all the needed set-up we will have calibrated the
+ cpu locally before sending the request, and will grab the
+ counter value right after the connect returns. The remote
+ will grab the counter right after the accept call. This saves
+ the hassle of extra messages being sent for the TCP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_stream_response->recv_buf_size;
+ rss_size = tcp_stream_response->send_buf_size;
+ rem_nodelay = tcp_stream_response->no_delay;
+ remote_cpu_usage= tcp_stream_response->measure_cpu;
+ remote_cpu_rate = tcp_stream_response->cpu_rate;
+
+ /* we have to make sure that the server port number is in
+ network order */
+ set_port_number(remote_res,
+ (short)tcp_stream_response->data_port_number);
+
+ rem_rcvavoid = tcp_stream_response->so_rcvavoid;
+ rem_sndavoid = tcp_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);
+ }
+ }
+
+ /*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_tcp_mss: data socket connect failed");
+ exit(1);
+ }
+
+
+ /* find-out what the TCP maximum segment_size was (if possible) */
+ tcp_mss = -1;
+ get_tcp_info(send_socket,&tcp_mss);
+
+ /* just go ahead and close the socket, the remote should figure it
+ out */
+ close(send_socket);
+
+ /* statistics? we don't need no stinking statistics */
+
+
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ mss_fmt_0,
+ tcp_mss,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,"%s",mss_title);
+ }
+ fprintf(where,
+ mss_fmt_0, /* the format string */
+ tcp_mss,
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ }
+
+
+}
+
+
+
+#ifdef HAVE_ICSC_EXS
+
+#include <sys/exs.h>
+
+
+/* This routine implements the TCP unidirectional data transfer test */
+/* (a.k.a. stream) for the sockets interface. It receives its */
+/* parameters via global variables from the shell and writes its */
+/* output to the standard output. */
+
+void
+send_exs_tcp_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\n";
+
+ char *tput_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f \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\n";
+
+ char *cpu_fmt_1 =
+ "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n";
+
+ char *ksink_fmt = "\n\
+Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\
+Local Remote Local Remote Xfered Per Per\n\
+Send Recv Send Recv Send (avg) Recv (avg)\n\
+%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n";
+
+ 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 tcp_mss = -1; /* possibly uninitialized on printf far below */
+
+ exs_mhandle_t exs_mhandle;
+ exs_qhandle_t exs_qhandle;
+#define NETPERF_EXS_PENDING 16
+ int exs_aio_pending;
+ int exs_aio_eagain;
+ int exs_aio_dequeued;
+ int exs_aio_dequeuecnt;
+ int exs_evtcnt;
+#define NETPERF_EXS_QSIZE 128
+ exs_event_t exs_evtvec[NETPERF_EXS_QSIZE];
+
+ /* 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 */
+
+ 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 tcp_stream_request_struct *tcp_stream_request;
+ struct tcp_stream_response_struct *tcp_stream_response;
+ struct tcp_stream_results_struct *tcp_stream_result;
+
+ tcp_stream_request =
+ (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data;
+ tcp_stream_response =
+ (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data;
+ tcp_stream_result =
+ (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+#if 0 /* def WANT_HISTOGRAM */
+ 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("EXS TCP STREAM TEST",local_res,remote_res);
+ }
+
+ send_ring = NULL;
+ confidence_iteration = 1;
+ init_stat();
+
+ /* initialize EXS API and create event queue */
+ if (exs_init (EXS_VERSION) == -1) {
+ perror ("netperf: send_exs_tcp_stream: exs_init failed");
+ exit (1);
+ }
+
+ if ((exs_qhandle = exs_qcreate (NETPERF_EXS_QSIZE)) == EXS_QHANDLE_INVALID) {
+ perror ("netperf: send_exs_tcp_stream: exs_qcreate failed");
+ exit (1);
+ }
+ if (debug) {
+ fprintf (where, "send_exs_tcp_stream: qhandle=%d\n", exs_qhandle);
+ }
+
+ /* 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_stream: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_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_exs_buffer_ring(send_width,
+ send_size,
+ local_send_align,
+ local_send_offset,
+ &exs_mhandle);
+ }
+
+ /* If the user has requested cpu utilization measurements, we must */
+ /* calibrate the cpu(s). We will perform this task within the tests */
+ /* themselves. If the user has specified the cpu rate, then */
+ /* calibrate_local_cpu will return rather quickly as it will have */
+ /* nothing to do. If local_cpu_rate is zero, then we will go through */
+ /* all the "normal" calibration stuff and return the rate back. */
+
+ if (local_cpu_usage) {
+ local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
+ }
+
+ /* Tell the remote end to do a listen. The server alters the socket */
+ /* paramters on the other side at this point, hence the reason for */
+ /* all the values being passed in the setup message. If the user did */
+ /* not specify any of the parameters, they will be passed as 0, which */
+ /* will indicate to the remote that no changes beyond the system's */
+ /* default should be used. Alignment is the exception, it will */
+ /* default to 1, which will be no alignment alterations. */
+
+ netperf_request.content.request_type = DO_TCP_STREAM;
+ tcp_stream_request->send_buf_size = rss_size_req;
+ tcp_stream_request->recv_buf_size = rsr_size_req;
+ tcp_stream_request->receive_size = recv_size;
+ tcp_stream_request->no_delay = rem_nodelay;
+ tcp_stream_request->recv_alignment = remote_recv_align;
+ tcp_stream_request->recv_offset = remote_recv_offset;
+ tcp_stream_request->measure_cpu = remote_cpu_usage;
+ tcp_stream_request->cpu_rate = remote_cpu_rate;
+ if (test_time) {
+ tcp_stream_request->test_length = test_time;
+ }
+ else {
+ tcp_stream_request->test_length = test_bytes;
+ }
+ tcp_stream_request->so_rcvavoid = rem_rcvavoid;
+ tcp_stream_request->so_sndavoid = rem_sndavoid;
+#ifdef DIRTY
+ tcp_stream_request->dirty_count = rem_dirty_count;
+ tcp_stream_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ tcp_stream_request->port = atoi(remote_data_port);
+ tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_tcp_stream: requesting TCP stream test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant */
+ /* socket parameters for this test type. We will put them back into */
+ /* the variables here so they can be displayed if desired. The */
+ /* remote will have calibrated CPU if necessary, and will have done */
+ /* all the needed set-up we will have calibrated the cpu locally */
+ /* before sending the request, and will grab the counter value right*/
+ /* after the connect returns. The remote will grab the counter right*/
+ /* after the accept call. This saves the hassle of extra messages */
+ /* being sent for the TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_stream_response->recv_buf_size;
+ rss_size = tcp_stream_response->send_buf_size;
+ rem_nodelay = tcp_stream_response->no_delay;
+ remote_cpu_usage= tcp_stream_response->measure_cpu;
+ remote_cpu_rate = tcp_stream_response->cpu_rate;
+
+ /* we have to make sure that the server port number is in */
+ /* network order */
+ set_port_number(remote_res,(short)tcp_stream_response->data_port_number);
+
+ rem_rcvavoid = tcp_stream_response->so_rcvavoid;
+ rem_sndavoid = tcp_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);
+ }
+
+#if 0 /* def 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_tcp_stream: data socket connect failed");
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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);
+
+#if 0 /* def WANT_INTERVALS */
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+ /* before we start, initialize a few variables */
+
+#if 0 /* def WANT_DEMO */
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#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. */
+
+ exs_aio_pending = 0;
+ exs_aio_eagain = 0;
+ exs_aio_dequeuecnt = 0;
+
+ while ((!times_up) || (bytes_remaining > 0)) {
+
+#ifdef DIRTY
+ access_buffer(send_ring->buffer_ptr,
+ send_size,
+ loc_dirty_count,
+ loc_clean_count);
+#endif /* DIRTY */
+
+#if 0 /* def WANT_HISTOGRAM */
+ /* timestamp just before we go into send and then again just after */
+ /* we come out raj 8/94 */
+ HIST_timestamp(&time_one);
+#endif /* WANT_HISTOGRAM */
+
+
+ /* post up to NETPERF_EXS_PENDING I/Os */
+ while ((exs_aio_pending < NETPERF_EXS_PENDING) &&
+ (exs_send (send_socket, send_ring->buffer_ptr, send_size,
+ 0, exs_qhandle, (exs_ahandle_t)-1, exs_mhandle) == 0)) {
+ exs_aio_pending++;
+
+ /* 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;
+ }
+ }
+
+ /* check exs_send result */
+ if (exs_aio_pending < NETPERF_EXS_PENDING) {
+ /* standard flow control case */
+ if (errno == EAGAIN)
+ exs_aio_eagain++;
+ /* case of times_up */
+ else if (errno == EINTR)
+ break;
+ /* strange, let's stop */
+ else {
+ perror ("netperf: exs_send error");
+ exit (1);
+ }
+ }
+
+ /* dequeue events with "threshold" on 1/2 posted */
+ exs_aio_dequeued =
+ exs_qdequeue (exs_qhandle, exs_evtvec,
+ -(exs_aio_pending>>1), NULL);
+ exs_aio_dequeuecnt++;
+
+ /* check exs_dequeue result */
+ if (exs_aio_dequeued < 0) {
+ /* case of times_up */
+ if (errno == EINTR)
+ break;
+ /* strange, let's stop */
+ else {
+ perror ("netperf: exs_send error");
+ exit (1);
+ }
+ }
+ /* update number of pending I/Os */
+ else {
+ exs_aio_pending -= exs_aio_dequeued;
+ }
+
+
+#if 0 /* def WANT_HISTOGRAM */
+ /* 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 */
+
+#if 0 /* def WANT_DEMO */
+ demo_stream_interval(send_size);
+#endif
+
+#if 0 /* def WANT_INTERVALS */
+ INTERVALS_WAIT();
+#endif /* WANT_INTERVALS */
+
+ }
+
+ /* Collect the last completion events */
+ exs_aio_dequeued =
+ exs_qdequeue (exs_qhandle, exs_evtvec, -exs_aio_pending, NULL);
+ exs_aio_dequeuecnt++;
+ /* check exs_dequeue result and update number of pending I/Os */
+ if (exs_aio_dequeued < 0) {
+ perror ("netperf: exs_send error");
+ exit (1);
+ }
+ exs_aio_pending -= exs_aio_dequeued;
+
+ /* Display some async I/O debug info */
+ if (debug) {
+ fprintf (where, "send_exs_tcp_stream: "
+ "aio sent=%d eagain=%d dequeue=%d pending=%d\n",
+ nummessages, exs_aio_eagain, exs_aio_dequeuecnt, exs_aio_pending);
+ }
+
+ /* 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 TCP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ tcp_mss = -1;
+ get_tcp_info(send_socket,&tcp_mss);
+ }
+
+ if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) {
+ perror("netperf: cannot shutdown tcp 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);
+
+ /* 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 TCP stream test, */
+ /* that the two numbers should be *very* close... We calculate */
+ /* bytes_sent regardless of the way the test length was controlled. */
+ /* If it was time, we needed to, and if it was by bytes, the user may */
+ /* have specified a number of bytes that wasn't a multiple of the */
+ /* send_size, so we really didn't send what he asked for ;-) */
+
+ bytes_sent = ntohd(tcp_stream_result->bytes_received);
+
+ 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 = tcp_stream_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ tcp_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(tcp_stream_result->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ remote_cpu_method);
+ }
+ 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 */
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ thruput);
+ break;
+ case 1:
+ case 2:
+ 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 */
+ break;
+ }
+ }
+
+ /* it would be a good thing to include information about some of the */
+ /* other parameters that may have been set for this test, but at the */
+ /* moment, I do not wish to figure-out all the formatting, so I will */
+ /* just put this comment here to help remind me that it is something */
+ /* that should be done at a later time. */
+
+ if (verbosity > 1) {
+ /* The user wanted to know it all, so we will give it to him. */
+ /* This information will include as much as we can find about */
+ /* TCP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ /* 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)tcp_stream_result->recv_calls,
+ tcp_stream_result->recv_calls);
+ fprintf(where,
+ ksink_fmt2,
+ tcp_mss);
+ fflush(where);
+#if 0 /* def WANT_HISTOGRAM */
+ fprintf(where,"\n\nHistogram of time spent in send() call.\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+ }
+
+}
+
+#endif /* HAVE_ICSC_EXS */
+
+
+
+#if defined(HAVE_SENDFILE)
+
+
+/* This routine implements the TCP unidirectional data transfer test
+ (a.k.a. stream) for the sockets interface using the sendfile()
+ system call - TCP_SENDFILE. It receives its parameters via global
+ variables from the shell and writes its output to the standard
+ output. Basically, this is the same test as the send_tcp_stream()
+ logic and we even tell the remote to do a TCP_STREAM test since for
+ all it knows, nothig is different. */
+
+void
+sendfile_tcp_stream(remote_host)
+ 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 tcp_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 */
+ 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 sockaddr_in server;
+
+#if defined(__linux) || defined(__sun)
+ off_t scratch_offset; /* the linux sendfile() call will update
+ the offset variable, which is
+ something we do _not_ want to happen
+ to the value in the send_ring! so, we
+ have to use a scratch variable. */
+#endif /* __linux || defined(__sun) */
+#if defined (USE_OSX)
+ off_t scratch_len; /* Darwin 9.x need a value-result parameter */
+#endif
+#if defined (__sun)
+ size_t scratch_len; /* the sun sendfilev() needs a place to
+ tell us how many bytes were written,
+ even though it also returns the value */
+ sendfilevec_t sv;
+#endif /* __sun */
+
+ struct tcp_stream_request_struct *tcp_stream_request;
+ struct tcp_stream_response_struct *tcp_stream_response;
+ struct tcp_stream_results_struct *tcp_stream_result;
+
+ tcp_stream_request =
+ (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data;
+ tcp_stream_response =
+ (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data;
+ tcp_stream_result =
+ (struct tcp_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. */
+
+ bzero((char *)&server,
+ sizeof(server));
+
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ /* we want to have some additional, interesting information in */
+ /* the headers. we know some of it here, but not all, so we will */
+ /* only print the test title here and will print the results */
+ /* titles after the test is finished */
+ print_top_test_header("TCP SENDFILE 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: sendfile_tcp_stream: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"sendfile_tcp_stream: send_socket obtained...\n");
+ }
+
+#if defined(TCP_CORK)
+ /* should this even be here?!? */
+ if (loc_tcpcork > 0) {
+ /* the user wishes for us to set TCP_CORK on the socket */
+ int one = 1;
+ if (setsockopt(send_socket,
+ getprotobyname("tcp")->p_proto,
+ TCP_CORK,
+ (char *)&one,
+ sizeof(one)) == SOCKET_ERROR) {
+ perror("netperf: sendfile_tcp_stream: tcp_cork");
+ exit(1);
+ }
+ if (debug) {
+ fprintf(where,"sendfile_tcp_stream: tcp_cork...\n");
+ }
+ }
+
+#endif /* TCP_CORK */
+
+ /* 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... */
+
+ /*check for file size/ min file size here? create file here/ back out???*/
+
+ 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... */
+
+ /*sendring -> an offset index that will shift the starting point of the*/
+ /*section of the file sent throughout the file*/
+
+ 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 = alloc_sendfile_buf_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);
+ }
+
+ /* 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_TCP_STREAM;
+ tcp_stream_request->send_buf_size = rss_size_req;
+ tcp_stream_request->recv_buf_size = rsr_size_req;
+ tcp_stream_request->receive_size = recv_size;
+ tcp_stream_request->no_delay = rem_nodelay;
+ tcp_stream_request->recv_alignment = remote_recv_align;
+ tcp_stream_request->recv_offset = remote_recv_offset;
+ tcp_stream_request->measure_cpu = remote_cpu_usage;
+ tcp_stream_request->cpu_rate = remote_cpu_rate;
+
+ if (test_time) {
+ tcp_stream_request->test_length = test_time;
+ }
+ else {
+ tcp_stream_request->test_length = test_bytes;
+ }
+
+ tcp_stream_request->so_rcvavoid = rem_rcvavoid;
+ tcp_stream_request->so_sndavoid = rem_sndavoid;
+
+#ifdef DIRTY
+ tcp_stream_request->dirty_count = rem_dirty_count;
+ tcp_stream_request->clean_count = rem_clean_count;
+#endif /* DIRTY */
+ tcp_stream_request->port = atoi(remote_data_port);
+ tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,
+ "netperf: send_tcp_stream: requesting TCP stream test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant
+ socket parameters for this test type. We will put them back
+ into the variables here so they can be displayed if desired.
+ The remote will have calibrated CPU if necessary, and will have
+ done all the needed set-up we will have calibrated the cpu
+ locally before sending the request, and will grab the counter
+ value right after the connect returns. The remote will grab the
+ counter right after the accept call. This saves the hassle of
+ extra messages being sent for the TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_stream_response->recv_buf_size;
+ rss_size = tcp_stream_response->send_buf_size;
+ rem_nodelay = tcp_stream_response->no_delay;
+ remote_cpu_usage= tcp_stream_response->measure_cpu;
+ remote_cpu_rate = tcp_stream_response->cpu_rate;
+
+ /* we have to make sure that the server port number is in */
+ /* network order */
+ set_port_number(remote_res,(short)tcp_stream_response->data_port_number);
+ rem_rcvavoid = tcp_stream_response->so_rcvavoid;
+ rem_sndavoid = tcp_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_tcp_stream: data socket connect failed");
+ printf(" port: %d\n",ntohs(server.sin_port));
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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);
+
+#ifdef WANT_INTERVALS
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+
+ /* before we start, initialize a few variables */
+
+#ifdef WANT_DEMO
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#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)) {
+
+ /* the sendfile_tcp_stream test does not support making the buffers
+ dirty. 08/2000 */
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before we go into sendfile() and then again
+ just after we come out raj 08/2000 */
+ /* but only if we are actually going to display a histogram */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ /* you can look at netlib.h for a description of the fields we
+ are passing to sendfile(). 08/2000 */
+ if (netperf_sendfile(send_socket, send_ring) != send_size) {
+ /* the test was interrupted, must be the end of test. the
+ send_tcp_stream code has some WIN32 ifdefs that we do not
+ need here. */
+ if ((len >=0) || SOCKET_EINTR(len)) {
+ break;
+ }
+ perror("netperf: data send error: sendfile");
+ fprintf(stderr,
+ "len was %d send_size was %d\n",
+ len,
+ send_size);
+ fflush(stderr);
+ exit(1);
+ }
+
+#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
+
+#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 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 TCP maximum segment_size was (if possible) */
+ if (verbosity > 1) {
+ tcp_mss = -1;
+ get_tcp_info(send_socket,&tcp_mss);
+ }
+
+ if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) {
+ perror("netperf: cannot shutdown tcp 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 */
+
+ /* since we are using sendfile() instead of send, we have no
+ scratch buffer from the send_ring to use for the
+ receive. however, since we "know" that the recv should be
+ returning zero bytes (not that we are making the checks we
+ should) we can pass the address of the flags field. raj 08/2000
+ */
+
+ recv(send_socket,
+ &(send_ring->flags),
+ sizeof(send_ring->flags),
+ 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 defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ /* 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 TCP stream test, */
+ /* that the two numbers should be *very* close... We calculate */
+ /* bytes_sent regardless of the way the test length was controlled. */
+ /* If it was time, we needed to, and if it was by bytes, the user may */
+ /* have specified a number of bytes that wasn't a multiple of the */
+ /* send_size, so we really didn't send what he asked for ;-) */
+
+ bytes_sent = ntohd(tcp_stream_result->bytes_received);
+
+ 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 = tcp_stream_result->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_sent,
+ 0.0,
+ remote_cpu_utilization,
+ tcp_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(tcp_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 */
+ /* TCP 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)tcp_stream_result->recv_calls,
+ tcp_stream_result->recv_calls);
+
+ fprintf(where,
+ ksink_fmt2,
+ tcp_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 */
+ }
+}
+
+#endif /* HAVE_SENDFILE */
+
+/* This is the server-side routine for the tcp stream test. It is */
+/* implemented as one routine. I could break things-out somewhat, but */
+/* didn't feel it was necessary. */
+
+void
+recv_tcp_stream()
+{
+
+ struct sockaddr_storage 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 tcp_stream_request_struct *tcp_stream_request;
+ struct tcp_stream_response_struct *tcp_stream_response;
+ struct tcp_stream_results_struct *tcp_stream_results;
+
+#ifdef DO_SELECT
+ FD_ZERO(&readfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+#endif /* DO_SELECT */
+
+ tcp_stream_request =
+ (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data;
+ tcp_stream_response =
+ (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data;
+ tcp_stream_results =
+ (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_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_tcp_stream: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_STREAM_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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_tcp_stream: requested alignment of %d\n",
+ tcp_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 = tcp_stream_request->send_buf_size;
+ lsr_size_req = tcp_stream_request->recv_buf_size;
+ loc_nodelay = tcp_stream_request->no_delay;
+ loc_rcvavoid = tcp_stream_request->so_rcvavoid;
+ loc_sndavoid = tcp_stream_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_stream_request->ipfamily),
+ tcp_stream_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_stream_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 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 (tcp_stream_request->receive_size == 0) {
+ if (lsr_size > 0) {
+ recv_size = lsr_size;
+ }
+ else {
+ recv_size = 4096;
+ }
+ }
+ else {
+ recv_size = tcp_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,
+ tcp_stream_request->recv_alignment,
+ tcp_stream_request->recv_offset);
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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. */
+
+ tcp_stream_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&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. */
+
+ tcp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (tcp_stream_request->measure_cpu) {
+ tcp_stream_response->measure_cpu = 1;
+ tcp_stream_response->cpu_rate =
+ calibrate_local_cpu(tcp_stream_request->cpu_rate);
+ }
+ else {
+ tcp_stream_response->measure_cpu = 0;
+ }
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_stream_response->send_buf_size = lss_size;
+ tcp_stream_response->recv_buf_size = lsr_size;
+ tcp_stream_response->no_delay = loc_nodelay;
+ tcp_stream_response->so_rcvavoid = loc_rcvavoid;
+ tcp_stream_response->so_sndavoid = loc_sndavoid;
+ tcp_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 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;
+ win_kludge_socket2 = INVALID_SOCKET;
+#endif /* WIN32 */
+
+ times_up = 0;
+
+ start_timer(tcp_stream_request->test_length + PAD_TIME);
+
+#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(tcp_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 (!times_up && ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0)) {
+ if (len == SOCKET_ERROR ) {
+ if (times_up) {
+ break;
+ }
+ 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,
+ tcp_stream_request->dirty_count,
+ tcp_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 && !times_up) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+ cpu_stop(tcp_stream_request->measure_cpu,&elapsed_time);
+
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_stream: got %g bytes\n",
+ bytes_received);
+ fprintf(where,
+ "recv_tcp_stream: got %d recvs\n",
+ receive_calls);
+ fflush(where);
+ }
+
+ tcp_stream_results->bytes_received = htond(bytes_received);
+ tcp_stream_results->elapsed_time = elapsed_time;
+ tcp_stream_results->recv_calls = receive_calls;
+
+ tcp_stream_results->cpu_method = cpu_method;
+ tcp_stream_results->num_cpus = lib_num_loc_cpus;
+
+ if (tcp_stream_request->measure_cpu) {
+ tcp_stream_results->cpu_util = calc_cpu_util(0.0);
+ };
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_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 tcp maerts test. It is
+ implemented as one routine. I could break things-out somewhat, but
+ didn't feel it was necessary. */
+
+void
+recv_tcp_maerts()
+{
+
+ struct sockaddr_storage 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 tcp_maerts_request_struct *tcp_maerts_request;
+ struct tcp_maerts_response_struct *tcp_maerts_response;
+ struct tcp_maerts_results_struct *tcp_maerts_results;
+
+ tcp_maerts_request =
+ (struct tcp_maerts_request_struct *)netperf_request.content.test_specific_data;
+ tcp_maerts_response =
+ (struct tcp_maerts_response_struct *)netperf_response.content.test_specific_data;
+ tcp_maerts_results =
+ (struct tcp_maerts_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_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_tcp_maerts: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_MAERTS_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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_tcp_maerts: requested alignment of %d\n",
+ tcp_maerts_request->send_alignment);
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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 = tcp_maerts_request->send_buf_size;
+ lsr_size_req = tcp_maerts_request->recv_buf_size;
+ loc_nodelay = tcp_maerts_request->no_delay;
+ loc_rcvavoid = tcp_maerts_request->so_rcvavoid;
+ loc_sndavoid = tcp_maerts_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_maerts_request->ipfamily),
+ tcp_maerts_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_maerts_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 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 (tcp_maerts_request->send_size == 0) {
+ if (lss_size > 0) {
+ send_size = lss_size;
+ }
+ else {
+ send_size = 4096;
+ }
+ }
+ else {
+ send_size = tcp_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,
+ tcp_maerts_request->send_alignment,
+ tcp_maerts_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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. */
+
+ tcp_maerts_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&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. */
+
+ tcp_maerts_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (tcp_maerts_request->measure_cpu) {
+ tcp_maerts_response->measure_cpu = 1;
+ tcp_maerts_response->cpu_rate =
+ calibrate_local_cpu(tcp_maerts_request->cpu_rate);
+ }
+ else {
+ tcp_maerts_response->measure_cpu = 0;
+ }
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_maerts_response->send_buf_size = lss_size;
+ tcp_maerts_response->recv_buf_size = lsr_size;
+ tcp_maerts_response->no_delay = loc_nodelay;
+ tcp_maerts_response->so_rcvavoid = loc_rcvavoid;
+ tcp_maerts_response->so_sndavoid = loc_sndavoid;
+ tcp_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 TCP_STREAM test. raj 2002-06-21 */
+
+ start_timer(tcp_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(tcp_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 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;
+ win_kludge_socket2 = INVALID_SOCKET;
+#endif /* WIN32 */
+
+#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,
+ tcp_maerts_request->dirty_count,
+ tcp_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(tcp_maerts_request->measure_cpu,&elapsed_time);
+
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_maerts: got %g bytes\n",
+ bytes_sent);
+ fprintf(where,
+ "recv_tcp_maerts: got %d sends\n",
+ send_calls);
+ fflush(where);
+ }
+
+ tcp_maerts_results->bytes_sent = htond(bytes_sent);
+ tcp_maerts_results->elapsed_time = elapsed_time;
+ tcp_maerts_results->send_calls = send_calls;
+
+ if (tcp_maerts_request->measure_cpu) {
+ tcp_maerts_results->cpu_util = calc_cpu_util(0.0);
+ };
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_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);
+ }
+
+ tcp_maerts_results->cpu_method = cpu_method;
+ tcp_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 TCP_RR */
+ /* test. */
+#ifndef WANT_MIGRATION
+void
+send_tcp_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_title_band = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed \n\
+Send Recv Size Size Time Throughput \n\
+bytes Bytes bytes bytes secs. %s/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_title_tput = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed Tput CPU CPU S.dem S.dem\n\
+Send Recv Size Size Time %-8.8s local remote local remote\n\
+bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n";
+
+ char *cpu_title_latency = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed Latency CPU CPU S.dem S.dem\n\
+Send Recv Size Size Time usecs local remote local remote\n\
+bytes bytes bytes bytes secs. per tran %% %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 RoundTrip Trans Throughput\n\
+Local Remote Local Remote Latency Rate %-8.8s/s\n\
+Send Recv Send Recv usec/Tran per sec Outbound Inbound\n\
+%5d %5d %5d %5d %-6.3f %-6.3f %-6.3f %-6.3f\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 tcp_rr_request_struct *tcp_rr_request;
+ struct tcp_rr_response_struct *tcp_rr_response;
+ struct tcp_rr_results_struct *tcp_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 TCP. 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 TCP stacks
+ of interest. the first
+ two will come from our
+ first_burst loop, and
+ the third from our
+ regularly scheduled
+ send */
+#endif
+
+ tcp_rr_request =
+ (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_rr_response=
+ (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_rr_result =
+ (struct tcp_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("TCP 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_rr: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_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_TCP_RR;
+ tcp_rr_request->recv_buf_size = rsr_size_req;
+ tcp_rr_request->send_buf_size = rss_size_req;
+ tcp_rr_request->recv_alignment = remote_recv_align;
+ tcp_rr_request->recv_offset = remote_recv_offset;
+ tcp_rr_request->send_alignment = remote_send_align;
+ tcp_rr_request->send_offset = remote_send_offset;
+ tcp_rr_request->request_size = req_size;
+ tcp_rr_request->response_size = rsp_size;
+ tcp_rr_request->no_delay = rem_nodelay;
+ tcp_rr_request->measure_cpu = remote_cpu_usage;
+ tcp_rr_request->cpu_rate = remote_cpu_rate;
+ tcp_rr_request->so_rcvavoid = rem_rcvavoid;
+ tcp_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ tcp_rr_request->test_length = test_time;
+ }
+ else {
+ tcp_rr_request->test_length = test_trans * -1;
+ }
+ tcp_rr_request->port = atoi(remote_data_port);
+ tcp_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_tcp_rr: requesting TCP 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 TCP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_rr_response->recv_buf_size;
+ rss_size = tcp_rr_response->send_buf_size;
+ rem_nodelay = tcp_rr_response->no_delay;
+ remote_cpu_usage = tcp_rr_response->measure_cpu;
+ remote_cpu_rate = tcp_rr_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ set_port_number(remote_res,(short)tcp_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);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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) {
+ demo_first_timestamp();
+ }
+#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_tcp_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_tcp_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 || rsp_bytes_recvd == 0) {
+ if ( SOCKET_EINTR(rsp_bytes_recvd) ) {
+ /* We hit the end of a timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_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 defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ 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 = tcp_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,
+ tcp_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(tcp_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) {
+ if ('x' == libfmt) {
+ fprintf(where,
+ cpu_title,
+ local_cpu_method,
+ remote_cpu_method);
+ }
+ else {
+ fprintf(where,
+ cpu_title_tput,
+ format_units(),
+ 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 */
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ 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,
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,
+ ('x' == libfmt) ? tput_title : tput_title_band,
+ 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 */
+ /* are we trans or do we need to convert to bytes then
+ bits? at this point, thruput is in our "confident"
+ transactions per second. we can convert to a
+ bidirectional bitrate by multiplying that by the sum
+ of the req_size and rsp_size. we pass that to
+ calc_thruput_interval_omni with an elapsed time of
+ 1.0 s to get it converted to [kmg]bits/s or
+ [KMG]Bytes/s */
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ ((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 */
+ /* TCP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ /* normally, you might think that if we were messing about with
+ the value of libfmt we would need to put it back again, but
+ since this is basically the last thing we are going to do with
+ it, it does not matter. so there :) raj 2007-06-08 */
+ /* if the user was asking for transactions, then we report
+ megabits per second for the unidirectional throughput,
+ otherwise we use the desired units. */
+ if ('x' == libfmt) {
+ libfmt = 'm';
+ }
+
+ fprintf(where,
+ ksink_fmt,
+ format_units(),
+ local_send_align,
+ remote_recv_offset,
+ local_send_offset,
+ remote_recv_offset,
+ /* if the user has enable burst mode, we have to remember
+ to account for that in the number of transactions
+ outstanding at any one time. otherwise we will
+ underreport the latency of individual
+ transactions. learned from saf by raj 2007-06-08 */
+ (((double)1.0/thruput)*(double)1000000.0) *
+ (double) (1 + ((first_burst_size > 0) ? first_burst_size : 0)),
+ thruput,
+ calc_thruput_interval_omni(thruput * (double)req_size,1.0),
+ calc_thruput_interval_omni(thruput * (double)rsp_size,1.0));
+
+#ifdef WANT_HISTOGRAM
+ fprintf(where,"\nHistogram of request/response times\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+
+ }
+
+}
+#endif /* WANT_MIGRATION */
+
+#if defined(__linux)
+/*
+ * Linux has this odd behavior where if the socket buffers are larger than
+ * a device's txqueuelen, the kernel will silently drop transmits which would
+ * not fit into the tx queue, and not pass an ENOBUFS error back to the
+ * application. As a result, a UDP stream test can report absurd transmit
+ * bandwidths (like 20Gb/s on a 1GbE NIC). This behavior can be avoided if
+ * you request extended error reporting on the socket. This is done by
+ * setting the IP_RECVERR socket option at the IP level.
+ */
+static void
+enable_enobufs(int s)
+{
+ struct protoent *pr;
+ int on = 1;
+
+ if ((pr = getprotobyname("ip")) == NULL) {
+ fprintf(where, "enable_enobufs failed: getprotobyname\n");
+ fflush(where);
+ return;
+ }
+ if (setsockopt(s, pr->p_proto, IP_RECVERR, (char *)&on, sizeof(on)) < 0) {
+ fprintf(where, "enable_enobufs failed: setsockopt\n");
+ fflush(where);
+ return;
+ }
+}
+#endif
+
+#ifndef WANT_MIGRATION
+void
+send_udp_stream(char remote_host[])
+{
+ /**********************************************************************/
+ /* */
+ /* UDP Unidirectional Send Test */
+ /* */
+ /**********************************************************************/
+
+#define UDP_LENGTH_MAX 0XFFFF - 28
+
+ char *tput_title = "\
+Socket Message Elapsed Messages \n\
+Size Size Time Okay Errors Throughput\n\
+bytes bytes secs # # %s/sec\n\n";
+
+ char *tput_fmt_0 =
+ "%7.2f\n";
+
+ char *tput_fmt_1 = "\
+%6d %6d %-7.2f %7d %6d %7.2f\n\
+%6d %-7.2f %7d %7.2f\n\n";
+
+
+ char *cpu_title = "\
+Socket Message Elapsed Messages CPU Service\n\
+Size Size Time Okay Errors Throughput Util Demand\n\
+bytes bytes secs # # %s/sec %% %c%c us/KB\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.2f %c\n";
+
+ char *cpu_fmt_1 = "\
+%6d %6d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\
+%6d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n";
+
+ unsigned int messages_recvd;
+ unsigned int messages_sent;
+ unsigned int failed_sends;
+
+ float elapsed_time,
+ local_cpu_utilization,
+ remote_cpu_utilization;
+
+ float local_service_demand, remote_service_demand;
+ double local_thruput, remote_thruput;
+ double bytes_sent;
+ double bytes_recvd;
+
+
+ int len;
+ struct ring_elt *send_ring;
+ SOCKET data_socket;
+
+ unsigned int sum_messages_sent;
+ unsigned int sum_messages_recvd;
+ unsigned int sum_failed_sends;
+ double sum_local_thruput;
+
+ struct addrinfo *local_res;
+ struct addrinfo *remote_res;
+
+ struct udp_stream_request_struct *udp_stream_request;
+ struct udp_stream_response_struct *udp_stream_response;
+ struct udp_stream_results_struct *udp_stream_results;
+
+ udp_stream_request =
+ (struct udp_stream_request_struct *)netperf_request.content.test_specific_data;
+ udp_stream_response =
+ (struct udp_stream_response_struct *)netperf_response.content.test_specific_data;
+ udp_stream_results =
+ (struct udp_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(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("UDP UNIDIRECTIONAL SEND TEST",local_res,remote_res);
+ }
+
+ send_ring = NULL;
+ confidence_iteration = 1;
+ init_stat();
+ sum_messages_sent = 0;
+ sum_messages_recvd = 0;
+ sum_failed_sends = 0;
+ sum_local_thruput = 0.0;
+
+ /* 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. */
+ messages_sent = 0;
+ messages_recvd = 0;
+ failed_sends = 0;
+ times_up = 0;
+
+ /*set up the data socket */
+ data_socket = create_data_socket(local_res);
+
+ if (data_socket == INVALID_SOCKET){
+ perror("udp_send: data socket");
+ exit(1);
+ }
+
+ /* now, we want to see if we need to set the send_size */
+ if (send_size == 0) {
+ if (lss_size > 0) {
+ send_size = (lss_size < UDP_LENGTH_MAX ? lss_size : UDP_LENGTH_MAX);
+ }
+ else {
+ send_size = 4096;
+ }
+ }
+
+
+ /* set-up the data buffer with the requested alignment and offset, */
+ /* most of the numbers here are just a hack to pick something nice */
+ /* and big in an attempt to never try to send a buffer a second time */
+ /* before it leaves the node...unless the user set the width */
+ /* explicitly. */
+ if (send_width == 0) send_width = 32;
+
+ if (send_ring == NULL ) {
+ send_ring = allocate_buffer_ring(send_width,
+ send_size,
+ local_send_align,
+ local_send_offset);
+ }
+
+
+ /* if the user supplied a cpu rate, this call will complete rather */
+ /* quickly, otherwise, the cpu rate will be retured to us for */
+ /* possible display. The Library will keep it's own copy of this data */
+ /* for use elsewhere. We will only display it. (Does that make it */
+ /* "opaque" to us?) */
+
+ if (local_cpu_usage)
+ local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
+
+ if (!no_control) {
+ /* Tell the remote end to set up the data connection. The server
+ sends back the port number and alters the socket parameters
+ there. Of course this is a datagram service so no connection
+ is actually set up, the server just sets up the socket and
+ binds it. */
+
+ netperf_request.content.request_type = DO_UDP_STREAM;
+ udp_stream_request->recv_buf_size = rsr_size_req;
+ udp_stream_request->message_size = send_size;
+ udp_stream_request->recv_connected = remote_connected;
+ udp_stream_request->recv_alignment = remote_recv_align;
+ udp_stream_request->recv_offset = remote_recv_offset;
+ udp_stream_request->measure_cpu = remote_cpu_usage;
+ udp_stream_request->cpu_rate = remote_cpu_rate;
+ udp_stream_request->test_length = test_time;
+ udp_stream_request->so_rcvavoid = rem_rcvavoid;
+ udp_stream_request->so_sndavoid = rem_sndavoid;
+ udp_stream_request->port = atoi(remote_data_port);
+ udp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ send_request();
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"send_udp_stream: remote data connection done.\n");
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ perror("send_udp_stream: error on remote");
+ exit(1);
+ }
+
+ /* Place the port number returned by the remote into the sockaddr */
+ /* structure so our sends can be sent to the correct place. Also get */
+ /* some of the returned socket buffer information for user display. */
+
+ /* make sure that port numbers are in the proper order */
+ set_port_number(remote_res,(short)udp_stream_response->data_port_number);
+
+ rsr_size = udp_stream_response->recv_buf_size;
+ rss_size = udp_stream_response->send_buf_size;
+ remote_cpu_rate = udp_stream_response->cpu_rate;
+ }
+
+#ifdef WANT_DEMO
+ demo_stream_setup(lss_size,rsr_size);
+#endif
+
+ /* We "connect" up to the remote post to allow is to use the send */
+ /* call instead of the sendto call. Presumeably, this is a little */
+ /* simpler, and a little more efficient. I think that it also means */
+ /* that we can be informed of certain things, but am not sure */
+ /* yet...also, this is the way I would expect a client to behave */
+ /* when talking to a server */
+ if (local_connected) {
+ if (connect(data_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen) == INVALID_SOCKET){
+ perror("send_udp_stream: data socket connect failed");
+ exit(1);
+ } else if (debug) {
+ fprintf(where,"send_udp_stream: connected data socket.\n");
+ fflush(where);
+ }
+ }
+
+#if defined (__linux)
+ enable_enobufs(data_socket);
+#endif
+
+#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 = data_socket;
+#endif /* WIN32 */
+
+ /* set up the timer to call us after test_time. one of these days, */
+ /* it might be nice to figure-out a nice reliable way to have the */
+ /* test controlled by a byte count as well, but since UDP is not */
+ /* reliable, that could prove difficult. so, in the meantime, we */
+ /* only allow a UDP_STREAM test to be a timed test. */
+
+ if (test_time) {
+ times_up = 0;
+ start_timer(test_time);
+ }
+ else {
+ fprintf(where,"Sorry, UDP_STREAM tests must be timed.\n");
+ fflush(where);
+ }
+
+ /* Get the start count for the idle counter and the start time */
+
+ cpu_start(local_cpu_usage);
+
+#ifdef WANT_INTERVALS
+ INTERVALS_INIT();
+#endif /* WANT_INTERVALS */
+
+#ifdef WANT_DEMO
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#endif
+
+ /* Send datagrams like there was no tomorrow. at somepoint it might */
+ /* be nice to set this up so that a quantity of bytes could be sent, */
+ /* but we still need some sort of end of test trigger on the receive */
+ /* side. that could be a select with a one second timeout, but then */
+ /* if there is a test where none of the data arrives for awile and */
+ /* then starts again, we would end the test too soon. something to */
+ /* think about... */
+ 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,
+ loc_dirty_count,
+ loc_clean_count);
+#endif /* DIRTY */
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ if (local_connected) {
+ len = send(data_socket,
+ send_ring->buffer_ptr,
+ send_size,
+ 0);
+ } else {
+ len = sendto(data_socket,
+ send_ring->buffer_ptr,
+ send_size,
+ 0,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen);
+ }
+
+ if (len != send_size) {
+ if ((len >= 0) ||
+ SOCKET_EINTR(len))
+ break;
+ if (errno == ENOBUFS) {
+ failed_sends++;
+ continue;
+ }
+ perror("udp_send: data send error");
+ exit(1);
+ }
+ messages_sent++;
+
+ /* now we want to move our pointer to the next position in the */
+ /* data buffer... */
+
+ send_ring = send_ring->next;
+
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* get the second timestamp */
+ 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
+
+#ifdef WANT_INTERVALS
+ INTERVALS_WAIT();
+#endif /* WANT_INTERVALS */
+
+ }
+
+ /* This is a timed test, so the remote will be returning to us after */
+ /* a time. We should not need to send any "strange" messages to tell */
+ /* the remote that the test is completed, unless we decide to add a */
+ /* number of messages to the test. */
+
+ /* the test is over, so get stats and stuff */
+ cpu_stop(local_cpu_usage,
+ &elapsed_time);
+
+#if defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ if (!no_control) {
+ /* Get the statistics from the remote end */
+ recv_response();
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"send_udp_stream: remote results obtained\n");
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ perror("send_udp_stream: error on remote");
+ exit(1);
+ }
+ messages_recvd = udp_stream_results->messages_recvd;
+ bytes_recvd = (double) send_size * (double) messages_recvd;
+ }
+ else {
+ /* since there was no control connection, we've no idea what was
+ actually received. raj 2007-02-08 */
+ messages_recvd = -1;
+ bytes_recvd = -1.0;
+ }
+
+ bytes_sent = (double) send_size * (double) messages_sent;
+ local_thruput = calc_thruput(bytes_sent);
+
+
+ /* we asume that the remote ran for as long as we did */
+
+ remote_thruput = calc_thruput(bytes_recvd);
+
+ /* print the results for this socket and message size */
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ /* We must now do a little math for service demand and cpu */
+ /* utilization for the system(s) We pass zeros for the local */
+ /* cpu utilization and elapsed time to tell the routine to use */
+ /* the libraries own values for those. */
+ if (local_cpu_usage) {
+ local_cpu_utilization = calc_cpu_util(0.0);
+ /* shouldn't this really be based on bytes_recvd, since that is */
+ /* the effective throughput of the test? I think that it should, */
+ /* so will make the change raj 11/94 */
+ local_service_demand = calc_service_demand(bytes_recvd,
+ 0.0,
+ 0.0,
+ 0);
+ }
+ else {
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ /* The local calculations could use variables being kept by */
+ /* the local netlib routines. The remote calcuations need to */
+ /* have a few things passed to them. */
+ if (remote_cpu_usage) {
+ remote_cpu_utilization = udp_stream_results->cpu_util;
+ remote_service_demand = calc_service_demand(bytes_recvd,
+ 0.0,
+ remote_cpu_utilization,
+ udp_stream_results->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,
+ remote_thruput,
+ local_cpu_utilization,
+ remote_cpu_utilization,
+ local_service_demand,
+ remote_service_demand);
+
+ /* since the routine calculate_confidence is rather generic, and */
+ /* we have a few other parms of interest, we will do a little work */
+ /* here to caclulate their average. */
+ sum_messages_sent += messages_sent;
+ sum_messages_recvd += messages_recvd;
+ sum_failed_sends += failed_sends;
+ sum_local_thruput += local_thruput;
+
+ confidence_iteration++;
+
+ /* this datapoint is done, so we don't need the socket any longer */
+ close(data_socket);
+
+ }
+
+ /* we should reach this point once the test is finished */
+
+ retrieve_confident_values(&elapsed_time,
+ &remote_thruput,
+ &local_cpu_utilization,
+ &remote_cpu_utilization,
+ &local_service_demand,
+ &remote_service_demand);
+
+ /* some of the interesting values aren't covered by the generic */
+ /* confidence routine */
+ messages_sent = sum_messages_sent / (confidence_iteration -1);
+ messages_recvd = sum_messages_recvd / (confidence_iteration -1);
+ failed_sends = sum_failed_sends / (confidence_iteration -1);
+ local_thruput = sum_local_thruput / (confidence_iteration -1);
+
+ /* 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(udp_stream_results->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ local_cpu_method);
+ }
+ 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 */
+ lss_size, /* local sendbuf size */
+ send_size, /* how large were the sends */
+ elapsed_time, /* how long was the test */
+ messages_sent,
+ failed_sends,
+ local_thruput, /* what was the xfer rate */
+ local_cpu_utilization, /* local cpu */
+ local_service_demand, /* local service demand */
+ rsr_size,
+ elapsed_time,
+ messages_recvd,
+ remote_thruput,
+ remote_cpu_utilization, /* remote cpu */
+ remote_service_demand); /* remote service demand */
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ local_thruput);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,tput_title,format_units());
+ }
+ fprintf(where,
+ tput_fmt_1, /* the format string */
+ lss_size, /* local sendbuf size */
+ send_size, /* how large were the sends */
+ elapsed_time, /* how long did it take */
+ messages_sent,
+ failed_sends,
+ local_thruput,
+ rsr_size, /* remote recvbuf size */
+ elapsed_time,
+ messages_recvd,
+ remote_thruput);
+ break;
+ }
+ }
+
+ fflush(where);
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ fprintf(where,"\nHistogram of time spent in send() call\n");
+ fflush(where);
+ HIST_report(time_hist);
+ }
+#endif /* WANT_HISTOGRAM */
+
+}
+#endif /* WANT_MIGRATION */
+
+
+ /* this routine implements the receive side (netserver) of the */
+ /* UDP_STREAM performance test. */
+
+void
+recv_udp_stream()
+{
+ struct ring_elt *recv_ring;
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct sockaddr_storage myaddr_in;
+ SOCKET s_data;
+ netperf_socklen_t addrlen;
+ struct sockaddr_storage remote_addr;
+ netperf_socklen_t remote_addrlen;
+
+ int len = 0;
+ unsigned int bytes_received = 0;
+ float elapsed_time;
+
+ int message_size;
+ unsigned int messages_recvd = 0;
+
+ struct udp_stream_request_struct *udp_stream_request;
+ struct udp_stream_response_struct *udp_stream_response;
+ struct udp_stream_results_struct *udp_stream_results;
+
+ udp_stream_request =
+ (struct udp_stream_request_struct *)netperf_request.content.test_specific_data;
+ udp_stream_response =
+ (struct udp_stream_response_struct *)netperf_response.content.test_specific_data;
+ udp_stream_results =
+ (struct udp_stream_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_udp_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 > 1) {
+ fprintf(where,"recv_udp_stream: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = UDP_STREAM_RESPONSE;
+
+ if (debug > 2) {
+ fprintf(where,"recv_udp_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 > 1) {
+ fprintf(where,"recv_udp_stream: requested alignment of %d\n",
+ udp_stream_request->recv_alignment);
+ fflush(where);
+ }
+
+ if (recv_width == 0) recv_width = 1;
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ udp_stream_request->message_size,
+ udp_stream_request->recv_alignment,
+ udp_stream_request->recv_offset);
+
+ if (debug > 1) {
+ fprintf(where,"recv_udp_stream: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug > 1) {
+ fprintf(where,"recv_udp_stream: 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 */
+ lsr_size_req = udp_stream_request->recv_buf_size;
+ loc_rcvavoid = udp_stream_request->so_rcvavoid;
+ loc_sndavoid = udp_stream_request->so_sndavoid;
+ local_connected = udp_stream_request->recv_connected;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(udp_stream_request->ipfamily),
+ udp_stream_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(udp_stream_request->ipfamily),
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ 0);
+
+ s_data = create_data_socket(local_res);
+
+ if (s_data == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+
+ udp_stream_response->test_length = udp_stream_request->test_length;
+
+ /* now get the port number assigned by the system */
+ addrlen = sizeof(myaddr_in);
+ if (getsockname(s_data,
+ (struct sockaddr *)&myaddr_in,
+ &addrlen) == SOCKET_ERROR){
+ netperf_response.content.serv_errno = errno;
+ close(s_data);
+ 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. */
+
+ udp_stream_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&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. */
+
+ udp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */
+ udp_stream_response->measure_cpu = 0;
+ if (udp_stream_request->measure_cpu) {
+ /* We will pass the rate into the calibration routine. If the */
+ /* user did not specify one, it will be 0.0, and we will do a */
+ /* "real" calibration. Otherwise, all it will really do is */
+ /* store it away... */
+ udp_stream_response->measure_cpu = 1;
+ udp_stream_response->cpu_rate =
+ calibrate_local_cpu(udp_stream_request->cpu_rate);
+ }
+
+ message_size = udp_stream_request->message_size;
+ test_time = udp_stream_request->test_length;
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ udp_stream_response->send_buf_size = lss_size;
+ udp_stream_response->recv_buf_size = lsr_size;
+ udp_stream_response->so_rcvavoid = loc_rcvavoid;
+ udp_stream_response->so_sndavoid = loc_sndavoid;
+
+ send_response();
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(udp_stream_request->measure_cpu);
+
+#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 */
+
+ /* The loop will exit when the timer pops, or if we happen to recv a */
+ /* message of less than send_size bytes... */
+
+ times_up = 0;
+
+ start_timer(test_time + PAD_TIME);
+
+ if (debug) {
+ fprintf(where,"recv_udp_stream: about to enter inner sanctum.\n");
+ fflush(where);
+ }
+
+ /* We "connect" up to the remote post to allow us to use the recv */
+ /* call instead of the recvfrom call. Presumeably, this is a little */
+ /* simpler, and a little more efficient. */
+
+ if (local_connected) {
+
+ /* Receive the first message using recvfrom to find the remote address */
+ remote_addrlen = sizeof(remote_addr);
+ len = recvfrom(s_data, recv_ring->buffer_ptr,
+ message_size, 0,
+ (struct sockaddr*)&remote_addr, &remote_addrlen);
+ if (len != message_size) {
+ if ((len == SOCKET_ERROR) && !SOCKET_EINTR(len)) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ }
+ messages_recvd++;
+ recv_ring = recv_ring->next;
+
+
+ /* Now connect with the remote socket address */
+ if (connect(s_data,
+ (struct sockaddr*)&remote_addr,
+ remote_addrlen )== INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ close(s_data);
+ send_response();
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"recv_udp_stream: connected data socket\n");
+ fflush(where);
+ }
+ }
+
+ while (!times_up) {
+ if(local_connected) {
+ len = recv(s_data,
+ recv_ring->buffer_ptr,
+ message_size,
+ 0);
+ } else {
+ len = recvfrom(s_data,
+ recv_ring->buffer_ptr,
+ message_size,
+ 0,0,0);
+ }
+
+ if (len != message_size) {
+ if ((len == SOCKET_ERROR) && !SOCKET_EINTR(len)) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ break;
+ }
+ messages_recvd++;
+ recv_ring = recv_ring->next;
+ }
+
+ if (debug) {
+ fprintf(where,"recv_udp_stream: got %d messages.\n",messages_recvd);
+ fflush(where);
+ }
+
+
+ /* The loop now exits due timer or < send_size bytes received. in */
+ /* reality, we only really support a timed UDP_STREAM test. raj */
+ /* 12/95 */
+
+ cpu_stop(udp_stream_request->measure_cpu,&elapsed_time);
+
+ if (times_up) {
+ /* we ended on a timer, subtract the PAD_TIME */
+ elapsed_time -= (float)PAD_TIME;
+ }
+ else {
+ stop_timer();
+ }
+
+ if (debug) {
+ fprintf(where,"recv_udp_stream: test ended in %f seconds.\n",elapsed_time);
+ fflush(where);
+ }
+
+
+ /* We will count the "off" message that got us out of the loop */
+ bytes_received = (messages_recvd * message_size) + len;
+
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_udp_stream: got %d bytes\n",
+ bytes_received);
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = UDP_STREAM_RESULTS;
+ udp_stream_results->bytes_received = bytes_received;
+ udp_stream_results->messages_recvd = messages_recvd;
+ udp_stream_results->elapsed_time = elapsed_time;
+ udp_stream_results->cpu_method = cpu_method;
+ udp_stream_results->num_cpus = lib_num_loc_cpus;
+ if (udp_stream_request->measure_cpu) {
+ udp_stream_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+ else {
+ udp_stream_results->cpu_util = (float) -1.0;
+ }
+
+ if (debug > 1) {
+ fprintf(where,
+ "recv_udp_stream: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ send_response();
+
+ close(s_data);
+
+}
+
+#ifndef WANT_MIGRATION
+void
+send_udp_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_title_band = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed \n\
+Send Recv Size Size Time Throughput \n\
+bytes Bytes bytes bytes secs. %s/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_title_tput = "\
+Local /Remote\n\
+Socket Size Request Resp. Elapsed Tput CPU CPU S.dem S.dem\n\
+Send Recv Size Size Time %-8.8s 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";
+
+ float elapsed_time;
+
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+
+ int len;
+ int nummessages;
+ SOCKET send_socket;
+ int trans_remaining;
+ int bytes_xferd;
+
+ 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 udp_rr_request_struct *udp_rr_request;
+ struct udp_rr_response_struct *udp_rr_response;
+ struct udp_rr_results_struct *udp_rr_result;
+
+ udp_rr_request =
+ (struct udp_rr_request_struct *)netperf_request.content.test_specific_data;
+ udp_rr_response =
+ (struct udp_rr_response_struct *)netperf_response.content.test_specific_data;
+ udp_rr_result =
+ (struct udp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ time_hist = HIST_new();
+ }
+#endif
+
+ /* 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_DGRAM,
+ IPPROTO_UDP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("UDP REQUEST/RESPONSE TEST",local_res,remote_res);
+ }
+
+ /* initialize a few counters */
+
+ send_ring = NULL;
+ recv_ring = NULL;
+ nummessages = 0;
+ bytes_xferd = 0;
+ times_up = 0;
+ 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)) {
+
+ nummessages = 0;
+ bytes_xferd = 0;
+ times_up = 0;
+ trans_remaining = 0;
+
+ /* set-up the data buffers with the requested alignment and offset */
+
+ 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_udp_rr: udp rr data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_udp_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 */
+ /* there is no idle counter in the kernel idle loop, the */
+ /* local_cpu_rate will be set to -1. */
+
+ if (local_cpu_usage) {
+ local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
+ }
+
+ 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_UDP_RR;
+ udp_rr_request->recv_buf_size = rsr_size_req;
+ udp_rr_request->send_buf_size = rss_size_req;
+ udp_rr_request->recv_alignment = remote_recv_align;
+ udp_rr_request->recv_offset = remote_recv_offset;
+ udp_rr_request->send_alignment = remote_send_align;
+ udp_rr_request->send_offset = remote_send_offset;
+ udp_rr_request->request_size = req_size;
+ udp_rr_request->response_size = rsp_size;
+ udp_rr_request->measure_cpu = remote_cpu_usage;
+ udp_rr_request->cpu_rate = remote_cpu_rate;
+ udp_rr_request->so_rcvavoid = rem_rcvavoid;
+ udp_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ udp_rr_request->test_length = test_time;
+ }
+ else {
+ udp_rr_request->test_length = test_trans * -1;
+ }
+ udp_rr_request->port = atoi(remote_data_port);
+ udp_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_udp_rr: requesting UDP r/r 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 UDP
+ tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = udp_rr_response->recv_buf_size;
+ rss_size = udp_rr_response->send_buf_size;
+ remote_cpu_usage = udp_rr_response->measure_cpu;
+ remote_cpu_rate = udp_rr_response->cpu_rate;
+ /* port numbers in proper order */
+ set_port_number(remote_res,(short)udp_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(100);
+#endif
+
+ /* Connect up to the remote port on the data socket. This will set */
+ /* the default destination address on this socket. With UDP, this */
+ /* does make a performance difference as we may not have to do as */
+ /* many routing lookups, however, I expect that a client would */
+ /* behave this way. raj 1/94 */
+
+ if ( connect(send_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen) == INVALID_SOCKET ) {
+ perror("netperf: data socket connect failed");
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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_DEMO
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#endif
+
+#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_FIRST_BURST
+ {
+ int i;
+ for (i = 0; i < first_burst_size; i++) {
+ 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_udp_rr: initial burst data send error");
+ exit(-1);
+ }
+ }
+ }
+#endif /* WANT_FIRST_BURST */
+
+ while ((!times_up) || (trans_remaining > 0)) {
+ /* send the request */
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ HIST_timestamp(&time_one);
+ }
+#endif
+ if((len=send(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ 0)) != req_size) {
+ if (SOCKET_EINTR(len)) {
+ /* We likely hit */
+ /* test-end time. */
+ break;
+ }
+ perror("send_udp_rr: data send error");
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+ /* receive the response. with UDP we will get it all, or nothing */
+
+ if((rsp_bytes_recvd=recv(send_socket,
+ recv_ring->buffer_ptr,
+ rsp_size,
+ 0)) != rsp_size) {
+ if (SOCKET_EINTR(rsp_bytes_recvd))
+ {
+ /* Again, we have likely hit test-end time */
+ break;
+ }
+ perror("send_udp_rr: data recv error");
+ exit(1);
+ }
+ recv_ring = recv_ring->next;
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ HIST_timestamp(&time_two);
+ HIST_add(time_hist,delta_micro(&time_one,&time_two));
+ }
+
+#endif
+
+ /* at this point, we may wish to sleep for some period of */
+ /* time, so we see how long that last transaction just took, */
+ /* and sleep for the difference of that and the interval. We */
+ /* will not sleep if the time would be less than a */
+ /* millisecond. */
+
+#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);
+ }
+ }
+
+ }
+
+ /* for some strange reason, I used to call shutdown on the UDP */
+ /* data socket here. I'm not sure why, because it would not have */
+ /* any effect... raj 11/94 */
+
+ /* 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 defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ 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 */
+ /* UDP rr test, that the two numbers should be *very* close... */
+ /* We calculate bytes_sent regardless of the way the test length */
+ /* was controlled. */
+
+ 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 by placing a code for the metod used in the */
+ /* test banner */
+
+ 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 = udp_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,
+ udp_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 done with the socket */
+ close(send_socket);
+ }
+
+ /* at this point, we have made all the iterations we are going to */
+ /* make. */
+ 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(udp_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) {
+ if ('x' == libfmt) {
+ fprintf(where,
+ cpu_title,
+ local_cpu_method,
+ remote_cpu_method);
+ }
+ else {
+ fprintf(where,
+ cpu_title_tput,
+ format_units(),
+ 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 */
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ 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,
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ break;
+ case 1:
+ case 2:
+ if (print_headers) {
+ fprintf(where,
+ ('x' == libfmt) ? tput_title : tput_title_band,
+ 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 */
+ ('x' == libfmt) ? thruput :
+ calc_thruput_interval_omni(thruput * (req_size+rsp_size),
+ 1.0),
+ ((print_headers) ||
+ (result_brand == NULL)) ? "" : result_brand);
+ fprintf(where,
+ tput_fmt_1_line_2,
+ rss_size, /* remote recvbuf size */
+ rsr_size);
+
+ break;
+ }
+ }
+ fflush(where);
+
+ /* 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 */
+ /* UDP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+#ifdef WANT_HISTOGRAM
+ fprintf(where,"\nHistogram of request/reponse times.\n");
+ fflush(where);
+ HIST_report(time_hist);
+#endif /* WANT_HISTOGRAM */
+ }
+}
+#endif /* WANT_MIGRATION */
+
+ /* this routine implements the receive side (netserver) of a UDP_RR */
+ /* test. */
+void
+recv_udp_rr()
+{
+
+ struct ring_elt *recv_ring;
+ struct ring_elt *send_ring;
+
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct sockaddr_storage myaddr_in;
+ struct sockaddr_storage peeraddr;
+ SOCKET s_data;
+ netperf_socklen_t addrlen;
+ int trans_received;
+ int trans_remaining;
+ int request_bytes_recvd;
+ int response_bytes_sent;
+ float elapsed_time;
+
+ struct udp_rr_request_struct *udp_rr_request;
+ struct udp_rr_response_struct *udp_rr_response;
+ struct udp_rr_results_struct *udp_rr_results;
+
+ udp_rr_request =
+ (struct udp_rr_request_struct *)netperf_request.content.test_specific_data;
+ udp_rr_response =
+ (struct udp_rr_response_struct *)netperf_response.content.test_specific_data;
+ udp_rr_results =
+ (struct udp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_udp_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_udp_rr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = UDP_RR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_udp_rr: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* We now alter the message_ptr variables to be at the desired */
+ /* alignments with the desired offsets. */
+
+ if (debug) {
+ fprintf(where,"recv_udp_rr: requested recv alignment of %d offset %d\n",
+ udp_rr_request->recv_alignment,
+ udp_rr_request->recv_offset);
+ fprintf(where,"recv_udp_rr: requested send alignment of %d offset %d\n",
+ udp_rr_request->send_alignment,
+ udp_rr_request->send_offset);
+ fflush(where);
+ }
+
+ if (send_width == 0) send_width = 1;
+ if (recv_width == 0) recv_width = 1;
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ udp_rr_request->request_size,
+ udp_rr_request->recv_alignment,
+ udp_rr_request->recv_offset);
+
+ send_ring = allocate_buffer_ring(send_width,
+ udp_rr_request->response_size,
+ udp_rr_request->send_alignment,
+ udp_rr_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_udp_rr: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_udp_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 = udp_rr_request->send_buf_size;
+ lsr_size_req = udp_rr_request->recv_buf_size;
+ loc_rcvavoid = udp_rr_request->so_rcvavoid;
+ loc_sndavoid = udp_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(udp_rr_request->ipfamily),
+ udp_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(udp_rr_request->ipfamily),
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ 0);
+
+ s_data = create_data_socket(local_res);
+
+ if (s_data == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+
+ exit(1);
+ }
+
+ /* now get the port number assigned by the system */
+ addrlen = sizeof(myaddr_in);
+ if (getsockname(s_data,
+ (struct sockaddr *)&myaddr_in,
+ &addrlen) == SOCKET_ERROR){
+ netperf_response.content.serv_errno = errno;
+ close(s_data);
+ 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. */
+
+ udp_rr_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port);
+ netperf_response.content.serv_errno = 0;
+
+ if (debug) {
+ fprintf(where,
+ "recv port number %d\n",
+ ((struct sockaddr_in *)&myaddr_in)->sin_port);
+ fflush(where);
+ }
+
+ /* 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. */
+
+ udp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */
+ udp_rr_response->measure_cpu = 0;
+ if (udp_rr_request->measure_cpu) {
+ udp_rr_response->measure_cpu = 1;
+ udp_rr_response->cpu_rate = calibrate_local_cpu(udp_rr_request->cpu_rate);
+ }
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ udp_rr_response->send_buf_size = lss_size;
+ udp_rr_response->recv_buf_size = lsr_size;
+ udp_rr_response->so_rcvavoid = loc_rcvavoid;
+ udp_rr_response->so_sndavoid = loc_sndavoid;
+
+ send_response();
+
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(udp_rr_request->measure_cpu);
+
+#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 (udp_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(udp_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = udp_rr_request->test_length * -1;
+ }
+
+ addrlen = sizeof(peeraddr);
+ bzero((char *)&peeraddr, addrlen);
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+ /* receive the request from the other side */
+ if ((request_bytes_recvd = recvfrom(s_data,
+ recv_ring->buffer_ptr,
+ udp_rr_request->request_size,
+ 0,
+ (struct sockaddr *)&peeraddr,
+ &addrlen)) != udp_rr_request->request_size) {
+ if ( SOCKET_EINTR(request_bytes_recvd) )
+ {
+ /* we must have hit the end of test time. */
+ break;
+ }
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ recv_ring = recv_ring->next;
+
+ /* Now, send the response to the remote */
+ if ((response_bytes_sent = sendto(s_data,
+ send_ring->buffer_ptr,
+ udp_rr_request->response_size,
+ 0,
+ (struct sockaddr *)&peeraddr,
+ addrlen)) !=
+ udp_rr_request->response_size) {
+ if ( SOCKET_EINTR(response_bytes_sent) )
+ {
+ /* we have hit end of test time. */
+ break;
+ }
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+ trans_received++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_udp_rr: Transaction %d complete.\n",
+ trans_received);
+ fflush(where);
+ }
+
+ }
+
+
+ /* The loop now exits due to timeout or transaction count being */
+ /* reached */
+
+ cpu_stop(udp_rr_request->measure_cpu,&elapsed_time);
+
+ if (times_up) {
+ /* we ended the test by time, which was at least 2 seconds */
+ /* longer than we wanted to run. so, we want to subtract */
+ /* PAD_TIME from the elapsed_time. */
+ elapsed_time -= PAD_TIME;
+ }
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_udp_rr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ udp_rr_results->bytes_received = (trans_received *
+ (udp_rr_request->request_size +
+ udp_rr_request->response_size));
+ udp_rr_results->trans_received = trans_received;
+ udp_rr_results->elapsed_time = elapsed_time;
+ udp_rr_results->cpu_method = cpu_method;
+ udp_rr_results->num_cpus = lib_num_loc_cpus;
+ if (udp_rr_request->measure_cpu) {
+ udp_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_udp_rr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ send_response();
+
+ /* we are done with the socket now */
+ close(s_data);
+
+ }
+
+
+ /* this routine implements the receive (netserver) side of a TCP_RR */
+ /* test */
+void
+recv_tcp_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_storage 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 tcp_rr_request_struct *tcp_rr_request;
+ struct tcp_rr_response_struct *tcp_rr_response;
+ struct tcp_rr_results_struct *tcp_rr_results;
+
+ tcp_rr_request =
+ (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_rr_response =
+ (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_rr_results =
+ (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_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_tcp_rr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_RR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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_tcp_rr: requested recv alignment of %d offset %d\n",
+ tcp_rr_request->recv_alignment,
+ tcp_rr_request->recv_offset);
+ fprintf(where,"recv_tcp_rr: requested send alignment of %d offset %d\n",
+ tcp_rr_request->send_alignment,
+ tcp_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,
+ tcp_rr_request->response_size,
+ tcp_rr_request->send_alignment,
+ tcp_rr_request->send_offset);
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ tcp_rr_request->request_size,
+ tcp_rr_request->recv_alignment,
+ tcp_rr_request->recv_offset);
+
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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 = tcp_rr_request->send_buf_size;
+ lsr_size_req = tcp_rr_request->recv_buf_size;
+ loc_nodelay = tcp_rr_request->no_delay;
+ loc_rcvavoid = tcp_rr_request->so_rcvavoid;
+ loc_sndavoid = tcp_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_rr_request->ipfamily),
+ tcp_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_rr_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 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. */
+
+ tcp_rr_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&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. */
+
+ tcp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */
+ tcp_rr_response->measure_cpu = 0;
+
+ if (tcp_rr_request->measure_cpu) {
+ tcp_rr_response->measure_cpu = 1;
+ tcp_rr_response->cpu_rate = calibrate_local_cpu(tcp_rr_request->cpu_rate);
+ }
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_rr_response->send_buf_size = lss_size;
+ tcp_rr_response->recv_buf_size = lsr_size;
+ tcp_rr_response->no_delay = loc_nodelay;
+ tcp_rr_response->so_rcvavoid = loc_rcvavoid;
+ tcp_rr_response->so_sndavoid = loc_sndavoid;
+ tcp_rr_response->test_length = tcp_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;
+ win_kludge_socket2 = INVALID_SOCKET;
+#endif /* WIN32 */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_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(tcp_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 (tcp_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(tcp_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = tcp_rr_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+ temp_message_ptr = recv_ring->buffer_ptr;
+ request_bytes_remaining = tcp_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,
+ tcp_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(tcp_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_tcp_rr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ tcp_rr_results->bytes_received = (trans_received *
+ (tcp_rr_request->request_size +
+ tcp_rr_request->response_size));
+ tcp_rr_results->trans_received = trans_received;
+ tcp_rr_results->elapsed_time = elapsed_time;
+ tcp_rr_results->cpu_method = cpu_method;
+ tcp_rr_results->num_cpus = lib_num_loc_cpus;
+ if (tcp_rr_request->measure_cpu) {
+ tcp_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_rr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ /* we are now done with the sockets */
+ close(s_data);
+ close(s_listen);
+
+ send_response();
+
+}
+
+
+void
+loc_cpu_rate()
+{
+#if defined(USE_LOOPER)
+ float dummy;
+#endif
+
+ /* a rather simple little test - it merely calibrates the local cpu */
+ /* and prints the results. There are no headers to allow someone to */
+ /* find a rate and use it in other tests automagically by setting a */
+ /* variable equal to the output of this test. We ignore any rates */
+ /* that may have been specified. In fact, we ignore all of the */
+ /* command line args! */
+
+ fprintf(where,
+ "%g",
+ calibrate_local_cpu(0.0));
+
+ if (verbosity > 1)
+ fprintf(where,
+ "\nThere %s %d local %s\n",
+ (lib_num_loc_cpus > 1) ? "are" : "is",
+ lib_num_loc_cpus,
+ (lib_num_loc_cpus > 1) ? "cpus" : "cpu");
+
+ /* we need the cpu_start, cpu_stop in the looper case to kill the */
+ /* child proceses raj 4/95 */
+
+#ifdef USE_LOOPER
+ cpu_start(1);
+ cpu_stop(1,&dummy);
+#endif /* USE_LOOPER */
+
+}
+
+void
+rem_cpu_rate()
+{
+ /* this test is much like the local variant, except that it works for */
+ /* the remote system, so in this case, we do pay attention to the */
+ /* value of the '-H' command line argument. */
+
+ fprintf(where,
+ "%g",
+ calibrate_remote_cpu());
+
+ if (verbosity > 1)
+ fprintf(where,
+ "\nThere %s %d remote %s\n",
+ (lib_num_rem_cpus > 1) ? "are" : "is",
+ lib_num_rem_cpus,
+ (lib_num_rem_cpus > 1) ? "cpus" : "cpu");
+
+}
+
+
+#ifndef WANT_MIGRATION
+ /* this test is intended to test the performance of establishing a
+ connection, exchanging a request/response pair, and repeating. it
+ is expected that this would be a good starting-point for
+ comparision of T/TCP with classic TCP for transactional workloads.
+ it will also look (can look) much like the communication pattern
+ of http for www access. */
+
+void
+send_tcp_conn_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\n";
+
+ char *tput_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %7.2f \n";
+ char *tput_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *cpu_title = "\
+Local /Remote\n\
+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 %% %% us/Tr us/Tr\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f\n";
+
+ char *cpu_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n";
+
+ char *cpu_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *ksink_fmt = "\n\
+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;
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+ char *temp_message_ptr;
+ int nummessages;
+ SOCKET send_socket;
+ int trans_remaining;
+ double bytes_xferd;
+ 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;
+
+ int myport;
+ int ret;
+
+ struct tcp_conn_rr_request_struct *tcp_conn_rr_request;
+ struct tcp_conn_rr_response_struct *tcp_conn_rr_response;
+ struct tcp_conn_rr_results_struct *tcp_conn_rr_result;
+
+ tcp_conn_rr_request =
+ (struct tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_conn_rr_response =
+ (struct tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_conn_rr_result =
+ (struct tcp_conn_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("TCP Connect/Request/Response TEST",local_res,remote_res);
+ }
+
+ /* initialize a few counters */
+
+ nummessages = 0;
+ bytes_xferd = 0.0;
+ times_up = 0;
+
+ /* set-up the data buffers with the requested alignment and offset */
+ if (send_width == 0) send_width = 1;
+ if (recv_width == 0) recv_width = 1;
+
+ send_ring = allocate_buffer_ring(send_width,
+ req_size,
+ local_send_align,
+ local_send_offset);
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ rsp_size,
+ local_recv_align,
+ local_recv_offset);
+
+
+ if (debug) {
+ fprintf(where,"send_tcp_conn_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_TCP_CRR;
+ tcp_conn_rr_request->recv_buf_size = rsr_size_req;
+ tcp_conn_rr_request->send_buf_size = rss_size_req;
+ tcp_conn_rr_request->recv_alignment = remote_recv_align;
+ tcp_conn_rr_request->recv_offset = remote_recv_offset;
+ tcp_conn_rr_request->send_alignment = remote_send_align;
+ tcp_conn_rr_request->send_offset = remote_send_offset;
+ tcp_conn_rr_request->request_size = req_size;
+ tcp_conn_rr_request->response_size = rsp_size;
+ tcp_conn_rr_request->no_delay = rem_nodelay;
+ tcp_conn_rr_request->measure_cpu = remote_cpu_usage;
+ tcp_conn_rr_request->cpu_rate = remote_cpu_rate;
+ tcp_conn_rr_request->so_rcvavoid = rem_rcvavoid;
+ tcp_conn_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ tcp_conn_rr_request->test_length = test_time;
+ }
+ else {
+ tcp_conn_rr_request->test_length = test_trans * -1;
+ }
+ tcp_conn_rr_request->port = atoi(remote_data_port);
+ tcp_conn_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_tcp_conn_rr: requesting TCP crr test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant
+ socket parameters for this test type. We will put them back
+ into the variables here so they can be displayed if desired.
+ The remote will have calibrated CPU if necessary, and will have
+ done all the needed set-up we will have calibrated the cpu
+ locally before sending the request, and will grab the counter
+ value right after the connect returns. The remote will grab the
+ counter right after the accept call. This saves the hassle of
+ extra messages being sent for the TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ rsr_size = tcp_conn_rr_response->recv_buf_size;
+ rss_size = tcp_conn_rr_response->send_buf_size;
+ rem_nodelay = tcp_conn_rr_response->no_delay;
+ remote_cpu_usage = tcp_conn_rr_response->measure_cpu;
+ remote_cpu_rate = tcp_conn_rr_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ set_port_number(remote_res,
+ (unsigned short)tcp_conn_rr_response->data_port_number);
+
+ if (debug) {
+ fprintf(where,"remote listen done.\n");
+ fprintf(where,"remote port is %u\n",get_port_number(remote_res));
+ fflush(where);
+ }
+ }
+ 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(100);
+#endif
+
+ /* pick a nice random spot between client_port_min and */
+ /* client_port_max for our initial port number */
+ srand(getpid());
+ if (client_port_max - client_port_min) {
+ myport = client_port_min +
+ (rand() % (client_port_max - client_port_min));
+ }
+ else {
+ myport = client_port_min;
+ }
+ /* there will be a ++ before the first call to bind, so subtract one */
+ myport--;
+ /* 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_DEMO
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#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. I think I */
+ /* just arbitrarily decrement trans_remaining for the timed test, but */
+ /* will not do that just yet... One other question is whether or not */
+ /* the send buffer and the receive buffer should be the same buffer. */
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before our call to create the socket, and then */
+ /* again just after the receive raj 3/95 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+newport:
+ /* pick a new port number */
+ myport++;
+
+ /* wrap the port number when we get to client_port_max. NOTE, some */
+ /* broken TCP's might treat the port number as a signed 16 bit */
+ /* quantity. we aren't interested in testing such broken */
+ /* implementations :) so we won't make sure that it is below 32767 */
+ /* raj 8/94 */
+ if (myport >= client_port_max) {
+ myport = client_port_min;
+ }
+
+ /* we do not want to use the port number that the server is */
+ /* sitting at - this would cause us to fail in a loopback test. we */
+ /* could just rely on the failure of the bind to get us past this, */
+ /* but I'm guessing that in this one case at least, it is much */
+ /* faster, given that we *know* that port number is already in use */
+ /* (or rather would be in a loopback test) */
+
+ if (myport == get_port_number(remote_res)) myport++;
+
+ if (debug) {
+ if ((nummessages % 100) == 0) {
+ printf("port %d\n",myport);
+ }
+ }
+
+ /* set up the data socket */
+ set_port_number(local_res, (unsigned short)myport);
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET) {
+ perror("netperf: send_tcp_conn_rr: tcp stream data socket");
+ exit(1);
+ }
+
+
+ /* we used to call bind here, but that is now taken-care-of by the
+ create_data_socket routine. */
+
+ /* Connect up to the remote port on the data socket */
+ if ((ret = connect(send_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen)) == INVALID_SOCKET){
+ if (SOCKET_EINTR(ret))
+ {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ if ((SOCKET_EADDRINUSE(ret)) || SOCKET_EADDRNOTAVAIL(ret)) {
+ /* likely something our explicit bind() would have caught in
+ the past, so go get another port, via create_data_socket.
+ yes, this is a bit more overhead than before, but the
+ condition should be rather rare. raj 2005-02-08 */
+ close(send_socket);
+ goto newport;
+ }
+ perror("netperf: data socket connect failed");
+ printf("\tattempted to connect on socket %d to port %d",
+ send_socket,
+ get_port_number(remote_res));
+ printf(" from port %d \n",get_port_number(local_res));
+ exit(1);
+ }
+
+
+ /* send the request */
+ if((len=send(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ 0)) != req_size) {
+ if (SOCKET_EINTR(len))
+ {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_conn_rr: data send error");
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+ /* receive the response */
+ rsp_bytes_left = rsp_size;
+ temp_message_ptr = recv_ring->buffer_ptr;
+
+
+ do {
+ rsp_bytes_recvd = recv(send_socket,
+ temp_message_ptr,
+ rsp_bytes_left,
+ 0);
+ if (rsp_bytes_recvd > 0) {
+ rsp_bytes_left -= rsp_bytes_recvd;
+ temp_message_ptr += rsp_bytes_recvd;
+ }
+ else {
+ break;
+ }
+ } while (rsp_bytes_left);
+
+
+ /* OK, we are out of the loop - now what? */
+ if (rsp_bytes_recvd < 0) {
+ /* did the timer hit, or was there an error? */
+ if (SOCKET_EINTR(rsp_bytes_recvd))
+ {
+ /* We hit the end of a timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_conn_rr: data recv error");
+ exit(1);
+ }
+
+ /* if this is a no_control test, we initiate connection close,
+ otherwise the remote netserver does it to remain just like
+ previous behaviour. raj 2007-27-08 */
+ if (!no_control) {
+ shutdown(send_socket,SHUT_WR);
+ }
+
+ /* we are expecting to get either a return of zero indicating
+ connection close, or an error. */
+ rsp_bytes_recvd = recv(send_socket,
+ temp_message_ptr,
+ 1,
+ 0);
+
+ /* our exit from the while loop should generally be when */
+ /* tmp_bytes_recvd is equal to zero, which implies the connection */
+ /* has been closed by the server side. By waiting until we get the */
+ /* zero return we can avoid race conditions that stick us with the */
+ /* TIME_WAIT connection and not the server. raj 8/96 */
+
+#ifdef VMWARE_UW
+ /* why this should be for VMware I'm not sure, but it was given as
+ part of the patches, so we include it here, but put it under an
+ ifdef VMWARE_UW. raj 2008-07-25 */
+ if (sp_bytes_recvd < 0 && errno == ECONNRESET) {
+ rsp_bytes_recvd = 0;
+ }
+#endif /* VMWARE_UW */
+
+ if (rsp_bytes_recvd == 0) {
+ /* connection close, call close. we assume that the requisite */
+ /* number of bytes have been received */
+ recv_ring = recv_ring->next;
+
+#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
+
+ nummessages++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug > 3) {
+ fprintf(where,
+ "Transaction %d completed on local port %d\n",
+ nummessages,
+ get_port_number(local_res));
+ fflush(where);
+ }
+
+ close(send_socket);
+
+ }
+ else {
+ /* it was less than zero - an error occured */
+ if (SOCKET_EINTR(rsp_bytes_recvd))
+ {
+ /* We hit the end of a timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_conn_rr: data recv error");
+ exit(1);
+ }
+
+ }
+
+
+ /* this call will always give us the elapsed time for the test, and */
+ /* will also store-away the necessaries for cpu utilization */
+
+ cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */
+ /* how long did we really run? */
+
+ 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 TCP stream test, */
+ /* that the two numbers should be *very* close... We calculate */
+ /* bytes_sent regardless of the way the test length was controlled. */
+ /* If it was time, we needed to, and if it was by bytes, the user may */
+ /* have specified a number of bytes that wasn't a multiple of the */
+ /* send_size, so we really didn't send what he asked for ;-) We use */
+ /* Kbytes/s as the units of thruput for a TCP stream test, where K = */
+ /* 1024. A future enhancement *might* be to choose from a couple of */
+ /* unit selections. */
+
+ bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages);
+ thruput = calc_thruput(bytes_xferd);
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ /* We must now do a little math for service demand and cpu */
+ /* utilization for the system(s) */
+ /* Of course, some of the information might be bogus because */
+ /* there was no idle counter in the kernel(s). We need to make */
+ /* a note of this for the user's benefit...*/
+ if (local_cpu_usage) {
+ if (local_cpu_rate == 0.0) {
+ fprintf(where,
+ "WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n");
+ fprintf(where,
+ "Local CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ local_cpu_utilization = calc_cpu_util(0.0);
+ /* since calc_service demand is doing ms/Kunit we will */
+ /* multiply the number of transaction by 1024 to get */
+ /* "good" numbers */
+ local_service_demand = calc_service_demand((double) nummessages*1024,
+ 0.0,
+ 0.0,
+ 0);
+ }
+ else {
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+ if (remote_cpu_rate == 0.0) {
+ fprintf(where,
+ "DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n");
+ fprintf(where,
+ "Remote CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ remote_cpu_utilization = tcp_conn_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,
+ tcp_conn_rr_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* We are now ready to print all the information. If the user */
+ /* has specified zero-level verbosity, we will just print the */
+ /* local service demand, or the remote service demand. If the */
+ /* user has requested verbosity level 1, he will get the basic */
+ /* "streamperf" numbers. If the user has specified a verbosity */
+ /* of greater than 1, we will display a veritable plethora of */
+ /* background information from outside of this block as it it */
+ /* not cpu_measurement specific... */
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand);
+ }
+ break;
+ case 1:
+ case 2:
+
+ 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 */
+ nummessages/elapsed_time,
+ local_cpu_utilization, /* local cpu */
+ remote_cpu_utilization, /* remote cpu */
+ local_service_demand, /* local service demand */
+ remote_service_demand); /* remote service demand */
+ fprintf(where,
+ cpu_fmt_1_line_2,
+ rss_size,
+ rsr_size);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ nummessages/elapsed_time);
+ break;
+ case 1:
+ case 2:
+ 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 */
+ nummessages/elapsed_time);
+ 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. */
+
+ if (verbosity > 1) {
+ /* The user wanted to know it all, so we will give it to him. */
+ /* This information will include as much as we can find about */
+ /* TCP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ fprintf(where,
+ ksink_fmt,
+ 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 */
+
+ }
+
+}
+#endif /* WANT_MIGRATION */
+
+void
+recv_tcp_conn_rr()
+{
+
+ char *message;
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct sockaddr_storage myaddr_in, peeraddr_in;
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ char *recv_message_ptr;
+ char *send_message_ptr;
+ 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;
+ float elapsed_time;
+
+ struct tcp_conn_rr_request_struct *tcp_conn_rr_request;
+ struct tcp_conn_rr_response_struct *tcp_conn_rr_response;
+ struct tcp_conn_rr_results_struct *tcp_conn_rr_results;
+
+ tcp_conn_rr_request =
+ (struct tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_conn_rr_response =
+ (struct tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_conn_rr_results =
+ (struct tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_conn_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_tcp_conn_rr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_CRR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_conn_rr: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* set-up the data buffer with the requested alignment and offset */
+ message = (char *)malloc(DATABUFFERLEN);
+ if (message == NULL) {
+ printf("malloc(%d) failed!\n", DATABUFFERLEN);
+ exit(1);
+ }
+
+ /* We now alter the message_ptr variables to be at the desired */
+ /* alignments with the desired offsets. */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_conn_rr: requested recv alignment of %d offset %d\n",
+ tcp_conn_rr_request->recv_alignment,
+ tcp_conn_rr_request->recv_offset);
+ fprintf(where,
+ "recv_tcp_conn_rr: requested send alignment of %d offset %d\n",
+ tcp_conn_rr_request->send_alignment,
+ tcp_conn_rr_request->send_offset);
+ fflush(where);
+ }
+
+ recv_message_ptr = ALIGN_BUFFER(message, tcp_conn_rr_request->recv_alignment, tcp_conn_rr_request->recv_offset);
+
+ send_message_ptr = ALIGN_BUFFER(message, tcp_conn_rr_request->send_alignment, tcp_conn_rr_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_tcp_conn_rr: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_conn_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 = tcp_conn_rr_request->send_buf_size;
+ lsr_size_req = tcp_conn_rr_request->recv_buf_size;
+ loc_nodelay = tcp_conn_rr_request->no_delay;
+ loc_rcvavoid = tcp_conn_rr_request->so_rcvavoid;
+ loc_sndavoid = tcp_conn_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_conn_rr_request->ipfamily),
+ tcp_conn_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_conn_rr_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ if (debug) {
+ fprintf(where,"could not create data socket\n");
+ fflush(where);
+ }
+ 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, 128) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+ if (debug) {
+ fprintf(where,"could not listen\n");
+ fflush(where);
+ }
+ 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();
+ if (debug) {
+ fprintf(where,"could not getsockname\n");
+ fflush(where);
+ }
+ 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. */
+
+ tcp_conn_rr_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port);
+ if (debug) {
+ fprintf(where,"telling the remote to call me at %d\n",
+ tcp_conn_rr_response->data_port_number);
+ fflush(where);
+ }
+ netperf_response.content.serv_errno = 0;
+
+ /* But wait, there's more. If the initiator wanted cpu measurements, */
+ /* then we must call the calibrate routine, which will return the max */
+ /* rate back to the initiator. If the CPU was not to be measured, or */
+ /* something went wrong with the calibration, we will return a 0.0 to */
+ /* the initiator. */
+
+ tcp_conn_rr_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (tcp_conn_rr_request->measure_cpu) {
+ tcp_conn_rr_response->measure_cpu = 1;
+ tcp_conn_rr_response->cpu_rate =
+ calibrate_local_cpu(tcp_conn_rr_request->cpu_rate);
+ }
+
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_conn_rr_response->send_buf_size = lss_size;
+ tcp_conn_rr_response->recv_buf_size = lsr_size;
+ tcp_conn_rr_response->no_delay = loc_nodelay;
+ tcp_conn_rr_response->so_rcvavoid = loc_rcvavoid;
+ tcp_conn_rr_response->so_sndavoid = loc_sndavoid;
+
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(tcp_conn_rr_request->measure_cpu);
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ if (tcp_conn_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(tcp_conn_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = tcp_conn_rr_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+ /* accept a connection from the remote */
+#ifdef WIN32
+ /* The test timer will probably fire during this accept,
+ so to make the start_timer above work we have to move
+ it to close s_listen while we are blocked on accept. */
+ win_kludge_socket = s_listen;
+#endif
+ if ((s_data=accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ if (errno == EINTR) {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+ fprintf(where,"recv_tcp_conn_rr: accept: errno = %d\n",errno);
+ fflush(where);
+ close(s_listen);
+
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"recv_tcp_conn_rr: accepted data connection.\n");
+ fflush(where);
+ }
+
+#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 */
+
+#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 */
+
+ temp_message_ptr = recv_message_ptr;
+ request_bytes_remaining = tcp_conn_rr_request->request_size;
+
+ /* receive the request from the other side */
+ while (!times_up && (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))
+ {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ else if (request_bytes_recvd > 0) {
+ request_bytes_remaining -= request_bytes_recvd;
+ temp_message_ptr += request_bytes_recvd;
+ }
+ else {
+ /* for some reason the remote closed the connection on
+ * us and that is unexpected so we should just close the
+ * socket and move-on. for that we will use an evil goto
+ * neener neener raj 20090622 */
+ goto bail;
+ }
+ }
+
+ if (timed_out) {
+ /* we hit the end of the test based on time - lets */
+ /* bail out of here now... */
+ fprintf(where,"yo5\n");
+ fflush(where);
+ break;
+ }
+
+ /* Now, send the response to the remote */
+ if((bytes_sent=send(s_data,
+ send_message_ptr,
+ tcp_conn_rr_request->response_size,
+ 0)) == SOCKET_ERROR) {
+ if (errno == EINTR) {
+ /* the test timer has popped */
+ timed_out = 1;
+ fprintf(where,"yo6\n");
+ fflush(where);
+ break;
+ }
+ netperf_response.content.serv_errno = 99;
+ send_response();
+ exit(1);
+ }
+
+ trans_received++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_conn_rr: Transaction %d complete\n",
+ trans_received);
+ fflush(where);
+ }
+
+ /* close the connection. the server will likely do a graceful */
+ /* close of the connection, insuring that all data has arrived at */
+ /* the client. for this it will call shutdown(), and then recv() and */
+ /* then close(). I'm reasonably confident that this is the */
+ /* appropriate sequence of calls - I would like to hear of */
+ /* examples in web servers to the contrary. raj 10/95*/
+#ifdef TCP_CRR_SHUTDOWN
+ shutdown(s_data,SHUT_WR);
+ recv(s_data,
+ recv_message_ptr,
+ 1,
+ 0);
+bail:
+ close(s_data);
+#else
+bail:
+ close(s_data);
+#endif /* TCP_CRR_SHUTDOWN */
+
+ }
+
+
+ /* The loop now exits due to timeout or transaction count being */
+ /* reached */
+
+ cpu_stop(tcp_conn_rr_request->measure_cpu,&elapsed_time);
+
+ if (timed_out) {
+ /* we ended the test by time, which was at least 2 seconds */
+ /* longer than we wanted to run. so, we want to subtract */
+ /* PAD_TIME from the elapsed_time. */
+ elapsed_time -= PAD_TIME;
+ }
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_conn_rr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ tcp_conn_rr_results->bytes_received = (trans_received *
+ (tcp_conn_rr_request->request_size +
+ tcp_conn_rr_request->response_size));
+ tcp_conn_rr_results->trans_received = trans_received;
+ tcp_conn_rr_results->elapsed_time = elapsed_time;
+ if (tcp_conn_rr_request->measure_cpu) {
+ tcp_conn_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_conn_rr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ send_response();
+
+}
+
+
+#ifdef DO_1644
+
+ /* this test is intended to test the performance of establishing a */
+ /* connection, exchanging a request/response pair, and repeating. it */
+ /* is expected that this would be a good starting-point for */
+ /* comparision of T/TCP with classic TCP for transactional workloads. */
+ /* it will also look (can look) much like the communication pattern */
+ /* of http for www access. */
+
+int
+send_tcp_tran_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\n";
+
+ char *tput_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %7.2f \n";
+ char *tput_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *cpu_title = "\
+Local /Remote\n\
+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 %% %% us/Tr us/Tr\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f\n";
+
+ char *cpu_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n";
+
+ char *cpu_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *ksink_fmt = "\n\
+Alignment Offset\n\
+Local Remote Local Remote\n\
+Send Recv Send Recv\n\
+%5d %5d %5d %5d\n";
+
+
+ int one = 1;
+ int timed_out = 0;
+ float elapsed_time;
+
+ int len;
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+ char *temp_message_ptr;
+ int nummessages;
+ SOCKET send_socket;
+ int trans_remaining;
+ double bytes_xferd;
+ int sock_opt_len = sizeof(int);
+ 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 hostent *hp;
+ struct sockaddr_in server;
+ struct sockaddr_in *myaddr;
+ unsigned int addr;
+ int myport;
+
+ struct tcp_tran_rr_request_struct *tcp_tran_rr_request;
+ struct tcp_tran_rr_response_struct *tcp_tran_rr_response;
+ struct tcp_tran_rr_results_struct *tcp_tran_rr_result;
+
+ tcp_tran_rr_request =
+ (struct tcp_tran_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_tran_rr_response =
+ (struct tcp_tran_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_tran_rr_result =
+ (struct tcp_tran_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. */
+
+ myaddr = (struct sockaddr_storage *)malloc(sizeof(struct sockaddr_storage));
+ if (myaddr == NULL) {
+ printf("malloc(%d) failed!\n", sizeof(struct sockaddr_storage));
+ exit(1);
+ }
+
+ bzero((char *)&server,
+ sizeof(server));
+ bzero((char *)myaddr,
+ sizeof(struct sockaddr_storage));
+ myaddr->sin_family = AF_INET;
+
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("TCP Transactional/Request/Response TEST",local_res,remote_res);
+ }
+
+ /* initialize a few counters */
+
+ nummessages = 0;
+ bytes_xferd = 0.0;
+ times_up = 0;
+
+ /* set-up the data buffers with the requested alignment and offset */
+ if (send_width == 0) send_width = 1;
+ if (recv_width == 0) recv_width = 1;
+
+ send_ring = allocate_buffer_ring(send_width,
+ req_size,
+ local_send_align,
+ local_send_offset);
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ rsp_size,
+ local_recv_align,
+ local_recv_offset);
+
+
+ if (debug) {
+ fprintf(where,"send_tcp_tran_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);
+ }
+
+ /* 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_TCP_TRR;
+ tcp_tran_rr_request->recv_buf_size = rsr_size_req;
+ tcp_tran_rr_request->send_buf_size = rss_size_req;
+ tcp_tran_rr_request->recv_alignment = remote_recv_align;
+ tcp_tran_rr_request->recv_offset = remote_recv_offset;
+ tcp_tran_rr_request->send_alignment = remote_send_align;
+ tcp_tran_rr_request->send_offset = remote_send_offset;
+ tcp_tran_rr_request->request_size = req_size;
+ tcp_tran_rr_request->response_size = rsp_size;
+ tcp_tran_rr_request->no_delay = rem_nodelay;
+ tcp_tran_rr_request->measure_cpu = remote_cpu_usage;
+ tcp_tran_rr_request->cpu_rate = remote_cpu_rate;
+ tcp_tran_rr_request->so_rcvavoid = rem_rcvavoid;
+ tcp_tran_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ tcp_tran_rr_request->test_length = test_time;
+ }
+ else {
+ tcp_tran_rr_request->test_length = test_trans * -1;
+ }
+ tcp_tran_rr_request->port = atoi(remote_data_port);
+ tcp_tran_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_tcp_tran_rr: requesting TCP_TRR test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant */
+ /* socket parameters for this test type. We will put them back into */
+ /* the variables here so they can be displayed if desired. The */
+ /* remote will have calibrated CPU if necessary, and will have done */
+ /* all the needed set-up we will have calibrated the cpu locally */
+ /* before sending the request, and will grab the counter value right */
+ /* after the connect returns. The remote will grab the counter right */
+ /* after the accept call. This saves the hassle of extra messages */
+ /* being sent for the TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ rsr_size = tcp_tran_rr_response->recv_buf_size;
+ rss_size = tcp_tran_rr_response->send_buf_size;
+ rem_nodelay = tcp_tran_rr_response->no_delay;
+ remote_cpu_usage= tcp_tran_rr_response->measure_cpu;
+ remote_cpu_rate = tcp_tran_rr_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ server.sin_port = tcp_tran_rr_response->data_port_number;
+ server.sin_port = htons(server.sin_port);
+ if (debug) {
+ fprintf(where,"remote listen done.\n");
+ fprintf(where,"remote port is %d\n",ntohs(server.sin_port));
+ fflush(where);
+ }
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+ exit(1);
+ }
+
+ /* pick a nice random spot between client_port_min and */
+ /* client_port_max for our initial port number. if they are the */
+ /* same, then just set to _min */
+ if (client_port_max - client_port_min) {
+ srand(getpid());
+ myport = client_port_min +
+ (rand() % (client_port_max - client_port_min));
+ }
+ else {
+ myport = client_port_min;
+ }
+
+ /* there will be a ++ before the first call to bind, so subtract one */
+ myport--;
+ myaddr->sin_port = htons((unsigned short)myport);
+
+ /* Set-up the test end conditions. For a request/response test, they */
+ /* can be either time or transaction based. */
+
+ if (test_time) {
+ /* The user wanted to end the test after a period of time. */
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(test_time);
+ }
+ else {
+ /* The tester wanted to send a number of bytes. */
+ trans_remaining = test_bytes;
+ times_up = 1;
+ }
+
+ /* The cpu_start routine will grab the current time and possibly */
+ /* value of the idle counter for later use in measuring cpu */
+ /* utilization and/or service demand and thruput. */
+
+ cpu_start(local_cpu_usage);
+
+ /* We use an "OR" to control test execution. When the test is */
+ /* controlled by time, the byte count check will always return false. */
+ /* When the test is controlled by byte count, the time test will */
+ /* always return false. When the test is finished, the whole */
+ /* expression will go false and we will stop sending data. I think I */
+ /* just arbitrarily decrement trans_remaining for the timed test, but */
+ /* will not do that just yet... One other question is whether or not */
+ /* the send buffer and the receive buffer should be the same buffer. */
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before our call to create the socket, and then */
+ /* again just after the receive raj 3/95 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ /* set up the data socket - is this really necessary or can I just */
+ /* re-use the same socket and move this cal out of the while loop. */
+ /* it does introcudea *boatload* of system calls. I guess that it */
+ /* all depends on "reality of programming." keeping it this way is */
+ /* a bit more conservative I imagine - raj 3/95 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET) {
+ perror("netperf: send_tcp_tran_rr: tcp stream data socket");
+ exit(1);
+ }
+
+ /* we set SO_REUSEADDR on the premis that no unreserved port */
+ /* number on the local system is going to be already connected to */
+ /* the remote netserver's port number. One thing that I might */
+ /* try later is to have the remote actually allocate a couple of */
+ /* port numbers and cycle through those as well. depends on if we */
+ /* can get through all the unreserved port numbers in less than */
+ /* the length of the TIME_WAIT state raj 8/94 */
+ one = 1;
+ if(setsockopt(send_socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sock_opt_len) == SOCKET_ERROR) {
+ perror("netperf: send_tcp_tran_rr: so_reuseaddr");
+ exit(1);
+ }
+
+newport:
+ /* pick a new port number */
+ myport = ntohs(myaddr->sin_port);
+ myport++;
+
+ /* we do not want to use the port number that the server is */
+ /* sitting at - this would cause us to fail in a loopback test. we */
+ /* could just rely on the failure of the bind to get us past this, */
+ /* but I'm guessing that in this one case at least, it is much */
+ /* faster, given that we *know* that port number is already in use */
+ /* (or rather would be in a loopback test) */
+
+ if (myport == ntohs(server.sin_port)) myport++;
+
+ /* wrap the port number when we get to 65535. NOTE, some broken */
+ /* TCP's might treat the port number as a signed 16 bit quantity. */
+ /* we aren't interested in testing such broken implementations :) */
+ /* raj 8/94 */
+ if (myport >= client_port_max) {
+ myport = client_port_min;
+ }
+ myaddr->sin_port = htons((unsigned short)myport);
+
+ if (debug) {
+ if ((nummessages % 100) == 0) {
+ printf("port %d\n",myport);
+ }
+ }
+
+ /* we want to bind our socket to a particular port number. */
+ if (bind(send_socket,
+ (struct sockaddr *)myaddr,
+ sizeof(struct sockaddr_storage)) == SOCKET_ERROR) {
+ /* if the bind failed, someone else must have that port number */
+ /* - perhaps in the listen state. since we can't use it, skip to */
+ /* the next port number. we may have to do this again later, but */
+ /* that's just too bad :) */
+ if (debug > 1) {
+ fprintf(where,
+ "send_tcp_tran_rr: tried to bind to port %d errno %d\n",
+ ntohs(myaddr->sin_port),
+ errno);
+ fflush(where);
+ }
+ /* yes, goto's are supposed to be evil, but they do have their */
+ /* uses from time to time. the real world doesn't always have */
+ /* to code to ge tthe A in CS 101 :) raj 3/95 */
+ goto newport;
+ }
+
+ /* Connect up to the remote port on the data socket. Since this is */
+ /* a test for RFC_1644-style transactional TCP, we can use the */
+ /* sendto() call instead of calling connect and then send() */
+
+ /* send the request */
+ if((len=sendto(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ MSG_EOF,
+ (struct sockaddr *)&server,
+ sizeof(server))) != req_size) {
+ if (SOCKET_EINTR(len))
+ {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_tran_rr: data send error");
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+ /* 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_tcp_tran_rr: data recv error");
+ exit(1);
+ }
+ rsp_bytes_left -= rsp_bytes_recvd;
+ temp_message_ptr += rsp_bytes_recvd;
+ }
+ recv_ring = recv_ring->next;
+
+ if (timed_out) {
+ /* we may have been in a nested while loop - we need */
+ /* another call to break. */
+ break;
+ }
+
+ close(send_socket);
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ HIST_timestamp(&time_two);
+ HIST_add(time_hist,delta_micro(&time_one,&time_two));
+ }
+#endif /* WANT_HISTOGRAM */
+
+ nummessages++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug > 3) {
+ fprintf(where,
+ "Transaction %d completed on local port %d\n",
+ nummessages,
+ ntohs(myaddr->sin_port));
+ fflush(where);
+ }
+
+
+ }
+
+ /* this call will always give us the elapsed time for the test, and */
+ /* will also store-away the necessaries for cpu utilization */
+
+ cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */
+ /* how long did we really run? */
+
+ /* Get the statistics from the remote end. The remote will have */
+ /* calculated service demand and all those interesting things. If it */
+ /* wasn't supposed to care, it will return obvious values. */
+
+ recv_response();
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote results obtained\n");
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ 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 TCP stream test, */
+ /* that the two numbers should be *very* close... We calculate */
+ /* bytes_sent regardless of the way the test length was controlled. */
+ /* If it was time, we needed to, and if it was by bytes, the user may */
+ /* have specified a number of bytes that wasn't a multiple of the */
+ /* send_size, so we really didn't send what he asked for ;-) We use */
+ /* Kbytes/s as the units of thruput for a TCP stream test, where K = */
+ /* 1024. A future enhancement *might* be to choose from a couple of */
+ /* unit selections. */
+
+ bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages);
+ thruput = calc_thruput(bytes_xferd);
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ /* We must now do a little math for service demand and cpu */
+ /* utilization for the system(s) */
+ /* Of course, some of the information might be bogus because */
+ /* there was no idle counter in the kernel(s). We need to make */
+ /* a note of this for the user's benefit...*/
+ if (local_cpu_usage) {
+ if (local_cpu_rate == 0.0) {
+ fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n");
+ fprintf(where,"Local CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ local_cpu_utilization = calc_cpu_util(0.0);
+ /* since calc_service demand is doing ms/Kunit we will */
+ /* multiply the number of transaction by 1024 to get */
+ /* "good" numbers */
+ local_service_demand = calc_service_demand((double) nummessages*1024,
+ 0.0,
+ 0.0,
+ 0);
+ }
+ else {
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+ if (remote_cpu_rate == 0.0) {
+ fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n");
+ fprintf(where,"Remote CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ remote_cpu_utilization = tcp_tran_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,
+ tcp_tran_rr_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* We are now ready to print all the information. If the user */
+ /* has specified zero-level verbosity, we will just print the */
+ /* local service demand, or the remote service demand. If the */
+ /* user has requested verbosity level 1, he will get the basic */
+ /* "streamperf" numbers. If the user has specified a verbosity */
+ /* of greater than 1, we will display a veritable plethora of */
+ /* background information from outside of this block as it it */
+ /* not cpu_measurement specific... */
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand);
+ }
+ break;
+ case 1:
+ case 2:
+
+ 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 */
+ nummessages/elapsed_time,
+ local_cpu_utilization, /* local cpu */
+ remote_cpu_utilization, /* remote cpu */
+ local_service_demand, /* local service demand */
+ remote_service_demand); /* remote service demand */
+ fprintf(where,
+ cpu_fmt_1_line_2,
+ rss_size,
+ rsr_size);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ nummessages/elapsed_time);
+ break;
+ case 1:
+ case 2:
+ 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 */
+ nummessages/elapsed_time);
+ 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. */
+
+ if (verbosity > 1) {
+ /* The user wanted to know it all, so we will give it to him. */
+ /* This information will include as much as we can find about */
+ /* TCP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ fprintf(where,
+ ksink_fmt,
+ 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 */
+
+ }
+
+}
+
+
+int
+recv_tcp_tran_rr()
+{
+
+ char *message;
+ struct sockaddr_in myaddr_in,
+ peeraddr_in;
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ int NoPush = 1;
+
+ char *recv_message_ptr;
+ char *send_message_ptr;
+ 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;
+ float elapsed_time;
+
+ struct tcp_tran_rr_request_struct *tcp_tran_rr_request;
+ struct tcp_tran_rr_response_struct *tcp_tran_rr_response;
+ struct tcp_tran_rr_results_struct *tcp_tran_rr_results;
+
+ tcp_tran_rr_request =
+ (struct tcp_tran_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_tran_rr_response =
+ (struct tcp_tran_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_tran_rr_results =
+ (struct tcp_tran_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_tran_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_tcp_tran_rr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_TRR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_tran_rr: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* set-up the data buffer with the requested alignment and offset */
+ message = (char *)malloc(DATABUFFERLEN);
+ if (message == NULL) {
+ printf("malloc(%d) failed!\n", DATABUFFERLEN);
+ exit(1);
+ }
+
+ /* We now alter the message_ptr variables to be at the desired */
+ /* alignments with the desired offsets. */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_tran_rr: requested recv alignment of %d offset %d\n",
+ tcp_tran_rr_request->recv_alignment,
+ tcp_tran_rr_request->recv_offset);
+ fprintf(where,
+ "recv_tcp_tran_rr: requested send alignment of %d offset %d\n",
+ tcp_tran_rr_request->send_alignment,
+ tcp_tran_rr_request->send_offset);
+ fflush(where);
+ }
+
+ recv_message_ptr = ALIGN_BUFFER(message, tcp_tran_rr_request->recv_alignment, tcp_tran_rr_request->recv_offset);
+
+ send_message_ptr = ALIGN_BUFFER(message, tcp_tran_rr_request->send_alignment, tcp_tran_rr_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_tcp_tran_rr: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
+ /* can put in OUR values !-) At some point, we may want to nail this */
+ /* socket to a particular network-level address, but for now, */
+ /* INADDR_ANY should be just fine. */
+
+ bzero((char *)&myaddr_in,
+ sizeof(myaddr_in));
+ myaddr_in.sin_family = AF_INET;
+ myaddr_in.sin_addr.s_addr = INADDR_ANY;
+ myaddr_in.sin_port = htons((unsigned short)tcp_tran_rr_request->port);
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_tran_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 = tcp_tran_rr_request->send_buf_size;
+ lsr_size_req = tcp_tran_rr_request->recv_buf_size;
+ loc_nodelay = tcp_tran_rr_request->no_delay;
+ loc_rcvavoid = tcp_tran_rr_request->so_rcvavoid;
+ loc_sndavoid = tcp_tran_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_tran_rr_request->ipfamily),
+ tcp_tran_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_tran_rr_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ if (debug) {
+ fprintf(where,"could not create data socket\n");
+ fflush(where);
+ }
+ 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
+
+
+ /* Let's get an address assigned to this socket so we can tell the */
+ /* initiator how to reach the data socket. There may be a desire to */
+ /* nail this socket to a specific IP address in a multi-homed, */
+ /* multi-connection situation, but for now, we'll ignore the issue */
+ /* and concentrate on single connection testing. */
+
+ if (bind(s_listen,
+ (struct sockaddr *)&myaddr_in,
+ sizeof(myaddr_in)) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+ if (debug) {
+ fprintf(where,"could not bind\n");
+ fflush(where);
+ }
+ exit(1);
+ }
+
+ /* we want to disable the implicit PUSH on all sends. at some point, */
+ /* this might want to be a parm to the test raj 3/95 */
+ if (setsockopt(s_listen,
+ IPPROTO_TCP,
+ TCP_NOPUSH,
+ (const char *)&NoPush,
+ sizeof(int)) == SOCKET_ERROR) {
+ fprintf(where,
+ "recv_tcp_tran_rr: could not set TCP_NOPUSH errno %d\n",
+ errno);
+ fflush(where);
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+ }
+
+ /* 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();
+ if (debug) {
+ fprintf(where,"could not listen\n");
+ fflush(where);
+ }
+ 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();
+ if (debug) {
+ fprintf(where,"could not geetsockname\n");
+ fflush(where);
+ }
+ 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. */
+
+ tcp_tran_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
+ if (debug) {
+ fprintf(where,"telling the remote to call me at %d\n",
+ tcp_tran_rr_response->data_port_number);
+ fflush(where);
+ }
+ netperf_response.content.serv_errno = 0;
+
+ /* But wait, there's more. If the initiator wanted cpu measurements, */
+ /* then we must call the calibrate routine, which will return the max */
+ /* rate back to the initiator. If the CPU was not to be measured, or */
+ /* something went wrong with the calibration, we will return a 0.0 to */
+ /* the initiator. */
+
+ tcp_tran_rr_response->cpu_rate = 0.0; /* assume no cpu */
+ if (tcp_tran_rr_request->measure_cpu) {
+ tcp_tran_rr_response->measure_cpu = 1;
+ tcp_tran_rr_response->cpu_rate =
+ calibrate_local_cpu(tcp_tran_rr_request->cpu_rate);
+ }
+
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_tran_rr_response->send_buf_size = lss_size;
+ tcp_tran_rr_response->recv_buf_size = lsr_size;
+ tcp_tran_rr_response->no_delay = loc_nodelay;
+ tcp_tran_rr_response->so_rcvavoid = loc_rcvavoid;
+ tcp_tran_rr_response->so_sndavoid = loc_sndavoid;
+
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(tcp_tran_rr_request->measure_cpu);
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ if (tcp_tran_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(tcp_tran_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = tcp_tran_rr_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+ /* accept a connection from the remote */
+ if ((s_data=accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ if (errno == EINTR) {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+ fprintf(where,"recv_tcp_tran_rr: accept: errno = %d\n",errno);
+ fflush(where);
+ close(s_listen);
+
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"recv_tcp_tran_rr: accepted data connection.\n");
+ fflush(where);
+ }
+
+#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 */
+
+#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 */
+
+ temp_message_ptr = recv_message_ptr;
+ request_bytes_remaining = tcp_tran_rr_request->request_size;
+
+ /* receive the request from the other side. we can just receive */
+ /* until we get zero bytes, but that would be a slight structure */
+ /* change in the code, with minimal perfomance effects. If */
+ /* however, I has variable-length messages, I would want to do */
+ /* this to avoid needing "double reads" - one for the message */
+ /* length, and one for the rest of the message raj 3/95 */
+ 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) )
+ {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ else {
+ request_bytes_remaining -= request_bytes_recvd;
+ temp_message_ptr += request_bytes_recvd;
+ }
+ }
+
+ if (timed_out) {
+ /* we hit the end of the test based on time - lets */
+ /* bail out of here now... */
+ fprintf(where,"yo5\n");
+ fflush(where);
+ break;
+ }
+
+ /* Now, send the response to the remote we can use sendto here to */
+ /* help remind people that this is an rfc 1644 style of test */
+ if((bytes_sent=sendto(s_data,
+ send_message_ptr,
+ tcp_tran_rr_request->response_size,
+ MSG_EOF,
+ (struct sockaddr *)&peeraddr_in,
+ sizeof(struct sockaddr_storage))) == 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 = 99;
+ send_response();
+ exit(1);
+ }
+
+ trans_received++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_tran_rr: Transaction %d complete\n",
+ trans_received);
+ fflush(where);
+ }
+
+ /* close the connection. since we have disable PUSH on sends, the */
+ /* FIN should be tacked-onto our last send instead of being */
+ /* standalone */
+ close(s_data);
+
+ }
+
+
+ /* The loop now exits due to timeout or transaction count being */
+ /* reached */
+
+ cpu_stop(tcp_tran_rr_request->measure_cpu,&elapsed_time);
+
+ if (timed_out) {
+ /* we ended the test by time, which was at least 2 seconds */
+ /* longer than we wanted to run. so, we want to subtract */
+ /* PAD_TIME from the elapsed_time. */
+ elapsed_time -= PAD_TIME;
+ }
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_tran_rr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ tcp_tran_rr_results->bytes_received = (trans_received *
+ (tcp_tran_rr_request->request_size +
+ tcp_tran_rr_request->response_size));
+ tcp_tran_rr_results->trans_received = trans_received;
+ tcp_tran_rr_results->elapsed_time = elapsed_time;
+ if (tcp_tran_rr_request->measure_cpu) {
+ tcp_tran_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_tran_rr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ send_response();
+
+}
+#endif /* DO_1644 */
+
+#ifdef DO_NBRR
+ /* this routine implements the sending (netperf) side of the TCP_RR */
+ /* test using POSIX-style non-blocking sockets. */
+
+void
+send_tcp_nbrr(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\n";
+
+ char *tput_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %7.2f \n";
+ char *tput_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *cpu_title = "\
+Local /Remote\n\
+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\n";
+
+ char *cpu_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n";
+
+ char *cpu_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *ksink_fmt = "\
+Alignment Offset\n\
+Local Remote Local Remote\n\
+Send Recv Send Recv\n\
+%5d %5d %5d %5d\n";
+
+
+ int timed_out = 0;
+ float elapsed_time;
+
+ int 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 hostent *hp;
+ struct sockaddr_storage server;
+ unsigned int addr;
+
+ struct tcp_rr_request_struct *tcp_rr_request;
+ struct tcp_rr_response_struct *tcp_rr_response;
+ struct tcp_rr_results_struct *tcp_rr_result;
+
+ struct addrinfo *remote_res;
+ struct addrinfo *local_res;
+
+ tcp_rr_request =
+ (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_rr_response=
+ (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_rr_result =
+ (struct tcp_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. */
+
+ bzero((char *)&server,
+ sizeof(server));
+
+ complete_addrinfos(&remote_res,
+ &local_res,
+ remote_host,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ if ( print_headers ) {
+ print_top_test_header("TCP Non-Blocking 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;
+
+ /* 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 */
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET){
+ perror("netperf: send_tcp_nbrr: tcp stream data socket");
+ exit(1);
+ }
+
+ if (debug) {
+ fprintf(where,"send_tcp_nbrr: 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);
+ }
+
+ /* 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_TCP_NBRR;
+ tcp_rr_request->recv_buf_size = rsr_size_req;
+ tcp_rr_request->send_buf_size = rss_size_req;
+ tcp_rr_request->recv_alignment = remote_recv_align;
+ tcp_rr_request->recv_offset = remote_recv_offset;
+ tcp_rr_request->send_alignment = remote_send_align;
+ tcp_rr_request->send_offset = remote_send_offset;
+ tcp_rr_request->request_size = req_size;
+ tcp_rr_request->response_size = rsp_size;
+ tcp_rr_request->no_delay = rem_nodelay;
+ tcp_rr_request->measure_cpu = remote_cpu_usage;
+ tcp_rr_request->cpu_rate = remote_cpu_rate;
+ tcp_rr_request->so_rcvavoid = rem_rcvavoid;
+ tcp_rr_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ tcp_rr_request->test_length = test_time;
+ }
+ else {
+ tcp_rr_request->test_length = test_trans * -1;
+ }
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_tcp_nbrr: requesting TCP 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 TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote listen done.\n");
+ rsr_size = tcp_rr_response->recv_buf_size;
+ rss_size = tcp_rr_response->send_buf_size;
+ rem_nodelay = tcp_rr_response->no_delay;
+ remote_cpu_usage = tcp_rr_response->measure_cpu;
+ remote_cpu_rate = tcp_rr_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ server.sin_port = (unsigned short)tcp_rr_response->data_port_number;
+ server.sin_port = htons(server.sin_port);
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ fprintf(where,
+ "netperf: remote error %d",
+ netperf_response.content.serv_errno);
+ perror("");
+ fflush(where);
+ exit(1);
+ }
+
+ /*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);
+ }
+
+ /* now that we are connected, mark the socket as non-blocking */
+ if (!set_nonblock(send_socket)) {
+ perror("netperf: set_nonblock");
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* 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. */
+
+ 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_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before our call to send, and then again just */
+ /* after the receive raj 8/94 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ /* even though this is a non-blocking socket, we will assume for */
+ /* the time being that we will be able to send an entire request */
+ /* without getting an EAGAIN */
+ if((len=send(send_socket,
+ send_ring->buffer_ptr,
+ req_size,
+ 0)) != req_size) {
+ if (SOCKET_EINTR(len)) {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_nbrr: data send error");
+ exit(1);
+ }
+ send_ring = send_ring->next;
+
+ /* receive the response. since we are using non-blocking I/O, we */
+ /* will "spin" on the recvs */
+ 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;
+ }
+#ifndef WIN32 // But what does WinNT indicate in this situation...
+ else if (errno == EAGAIN) {
+ Set_errno(0);
+ continue;
+ }
+#endif
+ else {
+ perror("send_tcp_nbrr: data recv error");
+ exit(1);
+ }
+ }
+ rsp_bytes_left -= rsp_bytes_recvd;
+ temp_message_ptr += rsp_bytes_recvd;
+ }
+ recv_ring = recv_ring->next;
+
+ 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_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? */
+
+ /* 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. */
+
+#if defined(WANT_INTERVALS)
+#ifdef WIN32
+ stop_itimer();
+#endif
+#endif /* WANT_INTERVALS */
+
+ 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. */
+
+ 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 = tcp_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,
+ tcp_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(tcp_rr_result->cpu_method);
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand,
+ local_cpu_method);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand,
+ remote_cpu_method);
+ }
+ 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 */
+ 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);
+ 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);
+ 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 */
+ /* TCP 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 TCP_RR */
+ /* test */
+void
+recv_tcp_nbrr()
+{
+
+ struct ring_elt *send_ring;
+ struct ring_elt *recv_ring;
+
+ 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;
+ float elapsed_time;
+
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct tcp_rr_request_struct *tcp_rr_request;
+ struct tcp_rr_response_struct *tcp_rr_response;
+ struct tcp_rr_results_struct *tcp_rr_results;
+
+ tcp_rr_request =
+ (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data;
+ tcp_rr_response =
+ (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data;
+ tcp_rr_results =
+ (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_nbrr: 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_tcp_nbrr: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_RR_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_nbrr: 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_tcp_nbrr: requested recv alignment of %d offset %d\n",
+ tcp_rr_request->recv_alignment,
+ tcp_rr_request->recv_offset);
+ fprintf(where,"recv_tcp_nbrr: requested send alignment of %d offset %d\n",
+ tcp_rr_request->send_alignment,
+ tcp_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,
+ tcp_rr_request->response_size,
+ tcp_rr_request->send_alignment,
+ tcp_rr_request->send_offset);
+
+ recv_ring = allocate_buffer_ring(recv_width,
+ tcp_rr_request->request_size,
+ tcp_rr_request->recv_alignment,
+ tcp_rr_request->recv_offset);
+
+
+ /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
+ /* can put in OUR values !-) At some point, we may want to nail this */
+ /* socket to a particular network-level address, but for now, */
+ /* INADDR_ANY should be just fine. */
+
+ bzero((char *)&myaddr_in,
+ sizeof(myaddr_in));
+ myaddr_in.sin_family = AF_INET;
+ myaddr_in.sin_addr.s_addr = INADDR_ANY;
+ myaddr_in.sin_port = htons((unsigned short)tcp_rr_request->port);
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_nbrr: 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 = tcp_rr_request->send_buf_size;
+ lsr_size_req = tcp_rr_request->recv_buf_size;
+ loc_nodelay = tcp_rr_request->no_delay;
+ loc_rcvavoid = tcp_rr_request->so_rcvavoid;
+ loc_sndavoid = tcp_rr_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_rr_request->ipfamily),
+ tcp_rr_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_rr_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+
+ exit(1);
+ }
+
+ /* Let's get an address assigned to this socket so we can tell the */
+ /* initiator how to reach the data socket. There may be a desire to */
+ /* nail this socket to a specific IP address in a multi-homed, */
+ /* multi-connection situation, but for now, we'll ignore the issue */
+ /* and concentrate on single connection testing. */
+
+ if (bind(s_listen,
+ (struct sockaddr *)&myaddr_in,
+ sizeof(myaddr_in)) == SOCKET_ERROR) {
+ netperf_response.content.serv_errno = errno;
+ close(s_listen);
+ send_response();
+
+ exit(1);
+ }
+
+ /* 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. */
+
+ tcp_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. */
+
+ tcp_rr_response->cpu_rate = 0.0; /* assume no cpu */
+ tcp_rr_response->measure_cpu = 0;
+
+ if (tcp_rr_request->measure_cpu) {
+ tcp_rr_response->measure_cpu = 1;
+ tcp_rr_response->cpu_rate = calibrate_local_cpu(tcp_rr_request->cpu_rate);
+ }
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_rr_response->send_buf_size = lss_size;
+ tcp_rr_response->recv_buf_size = lsr_size;
+ tcp_rr_response->no_delay = loc_nodelay;
+ tcp_rr_response->so_rcvavoid = loc_rcvavoid;
+ tcp_rr_response->so_sndavoid = loc_sndavoid;
+ tcp_rr_response->test_length = tcp_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);
+ }
+
+ if (debug) {
+ fprintf(where,"recv_tcp_nbrr: accept completes on the data connection.\n");
+ fflush(where);
+ }
+
+#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 that we are connected, mark the socket as non-blocking */
+ if (!set_nonblock(s_data)) {
+ close(s_data);
+ exit(1);
+ }
+
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(tcp_rr_request->measure_cpu);
+
+#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 */
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ if (tcp_rr_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(tcp_rr_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = tcp_rr_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+ temp_message_ptr = recv_ring->buffer_ptr;
+ request_bytes_remaining = tcp_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))
+ {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+#ifndef WIN32 // But what does WinNT indicate in this situation...
+ else if (errno == EAGAIN) {
+ Set_errno(0);
+ if (times_up) {
+ timed_out = 1;
+ break;
+ }
+ continue;
+ }
+#endif
+ else {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ exit(1);
+ }
+ }
+ else {
+ request_bytes_remaining -= request_bytes_recvd;
+ temp_message_ptr += request_bytes_recvd;
+ }
+ }
+
+ recv_ring = recv_ring->next;
+
+ if (timed_out) {
+ /* we hit the end of the test based on time - lets */
+ /* bail out of here now... */
+ fprintf(where,"yo5\n");
+ fflush(where);
+ break;
+ }
+
+ /* Now, send the response to the remote */
+ if((bytes_sent=send(s_data,
+ send_ring->buffer_ptr,
+ tcp_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(tcp_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_tcp_nbrr: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ tcp_rr_results->bytes_received = (trans_received *
+ (tcp_rr_request->request_size +
+ tcp_rr_request->response_size));
+ tcp_rr_results->trans_received = trans_received;
+ tcp_rr_results->elapsed_time = elapsed_time;
+ tcp_rr_results->cpu_method = cpu_method;
+ tcp_rr_results->num_cpus = lib_num_loc_cpus;
+ if (tcp_rr_request->measure_cpu) {
+ tcp_rr_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_nbrr: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ /* we are done with the socket, free it */
+ close(s_data);
+
+ send_response();
+
+}
+
+#endif /* DO_NBRR */
+
+
+ /* this test is intended to test the performance of establishing a */
+ /* connection, and then closing it again. this test is of somewhat */
+ /* arcane interest since no packets are exchanged between the */
+ /* user-space processes, but it will show the raw overhead of */
+ /* establishing a TCP connection. that service demand could then be */
+ /* compared with the sum of the service demands of a TCP_CRR and */
+ /* TCP_RR test - presumeably, they would all relate */
+
+void
+send_tcp_cc(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\n";
+
+ char *tput_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %7.2f \n";
+ char *tput_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *cpu_title = "\
+Local /Remote\n\
+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 %% %% us/Tr us/Tr\n\n";
+
+ char *cpu_fmt_0 =
+ "%6.3f\n";
+
+ char *cpu_fmt_1_line_1 = "\
+%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n";
+
+ char *cpu_fmt_1_line_2 = "\
+%-6d %-6d\n";
+
+ char *ksink_fmt = "\n\
+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;
+
+ char temp_message_ptr[1];
+ int nummessages;
+ SOCKET send_socket;
+ int trans_remaining;
+ double bytes_xferd;
+ int rsp_bytes_left = 1;
+ 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;
+
+ int myport;
+ int ret;
+
+ struct tcp_cc_request_struct *tcp_cc_request;
+ struct tcp_cc_response_struct *tcp_cc_response;
+ struct tcp_cc_results_struct *tcp_cc_result;
+
+ tcp_cc_request =
+ (struct tcp_cc_request_struct *)netperf_request.content.test_specific_data;
+ tcp_cc_response =
+ (struct tcp_cc_response_struct *)netperf_response.content.test_specific_data;
+ tcp_cc_result =
+ (struct tcp_cc_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("TCP Connect/Close TEST",local_res,remote_res);
+ }
+
+ /* initialize a few counters */
+
+ nummessages = 0;
+ bytes_xferd = 0.0;
+ times_up = 0;
+
+ /* since there are no data buffers in this test, we need no send or */
+ /* recv rings */
+
+ if (debug) {
+ fprintf(where,"send_tcp_cc: 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);
+ }
+
+ /* 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_TCP_CC;
+ tcp_cc_request->recv_buf_size = rsr_size_req;
+ tcp_cc_request->send_buf_size = rss_size_req;
+ tcp_cc_request->recv_alignment = remote_recv_align;
+ tcp_cc_request->recv_offset = remote_recv_offset;
+ tcp_cc_request->send_alignment = remote_send_align;
+ tcp_cc_request->send_offset = remote_send_offset;
+ tcp_cc_request->request_size = req_size;
+ tcp_cc_request->response_size = rsp_size;
+ tcp_cc_request->no_delay = rem_nodelay;
+ tcp_cc_request->measure_cpu = remote_cpu_usage;
+ tcp_cc_request->cpu_rate = remote_cpu_rate;
+ tcp_cc_request->so_rcvavoid = rem_rcvavoid;
+ tcp_cc_request->so_sndavoid = rem_sndavoid;
+ if (test_time) {
+ tcp_cc_request->test_length = test_time;
+ }
+ else {
+ tcp_cc_request->test_length = test_trans * -1;
+ }
+ tcp_cc_request->port = atoi(remote_data_port);
+ tcp_cc_request->ipfamily = af_to_nf(remote_res->ai_family);
+
+ if (debug > 1) {
+ fprintf(where,"netperf: send_tcp_cc: requesting TCP crr test\n");
+ }
+
+ send_request();
+
+ /* The response from the remote will contain all of the relevant */
+ /* socket parameters for this test type. We will put them back into */
+ /* the variables here so they can be displayed if desired. The */
+ /* remote will have calibrated CPU if necessary, and will have done */
+ /* all the needed set-up we will have calibrated the cpu locally */
+ /* before sending the request, and will grab the counter value right */
+ /* after the connect returns. The remote will grab the counter right */
+ /* after the accept call. This saves the hassle of extra messages */
+ /* being sent for the TCP tests. */
+
+ recv_response();
+
+ if (!netperf_response.content.serv_errno) {
+ rsr_size = tcp_cc_response->recv_buf_size;
+ rss_size = tcp_cc_response->send_buf_size;
+ rem_nodelay = tcp_cc_response->no_delay;
+ remote_cpu_usage= tcp_cc_response->measure_cpu;
+ remote_cpu_rate = tcp_cc_response->cpu_rate;
+ /* make sure that port numbers are in network order */
+ set_port_number(remote_res,(unsigned short)tcp_cc_response->data_port_number);
+
+ if (debug) {
+ fprintf(where,"remote listen done.\n");
+ fprintf(where,"remote port is %d\n",get_port_number(remote_res));
+ fflush(where);
+ }
+ }
+ 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(100);
+#endif
+
+ /* pick a nice random spot between client_port_min and */
+ /* client_port_max for our initial port number */
+ srand(getpid());
+ if (client_port_max - client_port_min) {
+ myport = client_port_min +
+ (rand() % (client_port_max - client_port_min));
+ }
+ else {
+ myport = client_port_min;
+ }
+ /* there will be a ++ before the first call to bind, so subtract one */
+ myport--;
+
+ /* 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_DEMO
+ if (demo_mode) {
+ demo_first_timestamp();
+ }
+#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. I think I */
+ /* just arbitrarily decrement trans_remaining for the timed test, but */
+ /* will not do that just yet... One other question is whether or not */
+ /* the send buffer and the receive buffer should be the same buffer. */
+
+ while ((!times_up) || (trans_remaining > 0)) {
+
+#ifdef WANT_HISTOGRAM
+ if (verbosity > 1) {
+ /* timestamp just before our call to create the socket, and then */
+ /* again just after the receive raj 3/95 */
+ HIST_timestamp(&time_one);
+ }
+#endif /* WANT_HISTOGRAM */
+
+ /* set up the data socket */
+ /* newport: is this label really required any longer? */
+ /* pick a new port number */
+ myport++;
+
+ /* wrap the port number when we get to client_port_max. NOTE, some */
+ /* broken TCP's might treat the port number as a signed 16 bit */
+ /* quantity. we aren't interested in testing such broken */
+ /* implementations :) so we won't make sure that it is below 32767 */
+ /* raj 8/94 */
+ if (myport >= client_port_max) {
+ myport = client_port_min;
+ }
+
+ /* we do not want to use the port number that the server is */
+ /* sitting at - this would cause us to fail in a loopback test. we */
+ /* could just rely on the failure of the bind to get us past this, */
+ /* but I'm guessing that in this one case at least, it is much */
+ /* faster, given that we *know* that port number is already in use */
+ /* (or rather would be in a loopback test) */
+
+ if (myport == get_port_number(remote_res)) myport++;
+
+ if (debug) {
+ if ((nummessages % 100) == 0) {
+ printf("port %d\n",myport);
+ }
+ }
+ set_port_number(local_res, (unsigned short)myport);
+ send_socket = create_data_socket(local_res);
+
+ if (send_socket == INVALID_SOCKET) {
+ perror("netperf: send_tcp_cc: tcp stream data socket");
+ exit(1);
+ }
+
+#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 = send_socket;
+#endif /* WIN32 */
+
+ /* we used to have a call to bind() here, but that is being
+ taken care of by create_data_socket(). raj 2005-02-08 */
+
+ /* Connect up to the remote port on the data socket */
+ if ((ret = connect(send_socket,
+ remote_res->ai_addr,
+ remote_res->ai_addrlen)) == INVALID_SOCKET){
+ if (SOCKET_EINTR(ret))
+ {
+ /* we hit the end of a */
+ /* timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("netperf: data socket connect failed");
+ printf("\tattempted to connect on socket %d to port %d",
+ send_socket,
+ get_port_number(remote_res));
+ printf(" from port %u \n",get_port_number(local_res));
+ exit(1);
+ }
+
+ /* we hang in a recv() to get the remote's close indication */
+
+ rsp_bytes_recvd=recv(send_socket,
+ temp_message_ptr,
+ rsp_bytes_left,
+ 0);
+
+
+ if (rsp_bytes_recvd == 0) {
+ /* connection close, call close. we assume that the requisite */
+ /* number of bytes have been received */
+
+#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
+
+ nummessages++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug > 3) {
+ fprintf(where,
+ "Transaction %d completed on local port %u\n",
+ nummessages,
+ get_port_number(local_res));
+ fflush(where);
+ }
+
+ close(send_socket);
+
+ }
+ else {
+ /* it was less than zero - an error occured */
+ if (SOCKET_EINTR(rsp_bytes_recvd))
+ {
+ /* We hit the end of a timed test. */
+ timed_out = 1;
+ break;
+ }
+ perror("send_tcp_cc: data recv error");
+ exit(1);
+ }
+
+ }
+
+
+ /* this call will always give us the elapsed time for the test, and */
+ /* will also store-away the necessaries for cpu utilization */
+
+ cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */
+ /* how long did we really run? */
+
+ /* Get the statistics from the remote end. The remote will have */
+ /* calculated service demand and all those interesting things. If it */
+ /* wasn't supposed to care, it will return obvious values. */
+
+ recv_response();
+ if (!netperf_response.content.serv_errno) {
+ if (debug)
+ fprintf(where,"remote results obtained\n");
+ }
+ else {
+ Set_errno(netperf_response.content.serv_errno);
+ 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 TCP stream test, */
+ /* that the two numbers should be *very* close... We calculate */
+ /* bytes_sent regardless of the way the test length was controlled. */
+ /* If it was time, we needed to, and if it was by bytes, the user may */
+ /* have specified a number of bytes that wasn't a multiple of the */
+ /* send_size, so we really didn't send what he asked for ;-) We use */
+ /* Kbytes/s as the units of thruput for a TCP stream test, where K = */
+ /* 1024. A future enhancement *might* be to choose from a couple of */
+ /* unit selections. */
+
+ bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages);
+ thruput = calc_thruput(bytes_xferd);
+
+ if (local_cpu_usage || remote_cpu_usage) {
+ /* We must now do a little math for service demand and cpu */
+ /* utilization for the system(s) */
+ /* Of course, some of the information might be bogus because */
+ /* there was no idle counter in the kernel(s). We need to make */
+ /* a note of this for the user's benefit...*/
+ if (local_cpu_usage) {
+ if (local_cpu_rate == 0.0) {
+ fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n");
+ fprintf(where,"Local CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ local_cpu_utilization = calc_cpu_util(0.0);
+ /* since calc_service demand is doing ms/Kunit we will */
+ /* multiply the number of transaction by 1024 to get */
+ /* "good" numbers */
+ local_service_demand = calc_service_demand((double) nummessages*1024,
+ 0.0,
+ 0.0,
+ 0);
+ }
+ else {
+ local_cpu_utilization = (float) -1.0;
+ local_service_demand = (float) -1.0;
+ }
+
+ if (remote_cpu_usage) {
+ if (remote_cpu_rate == 0.0) {
+ fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n");
+ fprintf(where,"Remote CPU usage numbers based on process information only!\n");
+ fflush(where);
+ }
+ remote_cpu_utilization = tcp_cc_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,
+ tcp_cc_result->num_cpus);
+ }
+ else {
+ remote_cpu_utilization = (float) -1.0;
+ remote_service_demand = (float) -1.0;
+ }
+
+ /* We are now ready to print all the information. If the user */
+ /* has specified zero-level verbosity, we will just print the */
+ /* local service demand, or the remote service demand. If the */
+ /* user has requested verbosity level 1, he will get the basic */
+ /* "streamperf" numbers. If the user has specified a verbosity */
+ /* of greater than 1, we will display a veritable plethora of */
+ /* background information from outside of this block as it it */
+ /* not cpu_measurement specific... */
+
+ switch (verbosity) {
+ case 0:
+ if (local_cpu_usage) {
+ fprintf(where,
+ cpu_fmt_0,
+ local_service_demand);
+ }
+ else {
+ fprintf(where,
+ cpu_fmt_0,
+ remote_service_demand);
+ }
+ break;
+ case 1:
+ case 2:
+
+ 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 */
+ nummessages/elapsed_time,
+ local_cpu_utilization, /* local cpu */
+ remote_cpu_utilization, /* remote cpu */
+ local_service_demand, /* local service demand */
+ remote_service_demand); /* remote service demand */
+ fprintf(where,
+ cpu_fmt_1_line_2,
+ rss_size,
+ rsr_size);
+ break;
+ }
+ }
+ else {
+ /* The tester did not wish to measure service demand. */
+ switch (verbosity) {
+ case 0:
+ fprintf(where,
+ tput_fmt_0,
+ nummessages/elapsed_time);
+ break;
+ case 1:
+ case 2:
+ 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 */
+ nummessages/elapsed_time);
+ 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. */
+
+ if (verbosity > 1) {
+ /* The user wanted to know it all, so we will give it to him. */
+ /* This information will include as much as we can find about */
+ /* TCP statistics, the alignments of the sends and receives */
+ /* and all that sort of rot... */
+
+ fprintf(where,
+ ksink_fmt,
+ 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 */
+
+ }
+
+}
+
+
+void
+recv_tcp_cc()
+{
+
+ char *message;
+
+ struct addrinfo *local_res;
+ char local_name[BUFSIZ];
+ char port_buffer[PORTBUFSIZE];
+
+ struct sockaddr_storage myaddr_in, peeraddr_in;
+ SOCKET s_listen,s_data;
+ netperf_socklen_t addrlen;
+ char *recv_message_ptr;
+ char *send_message_ptr;
+ int trans_received;
+ int trans_remaining;
+ int timed_out = 0;
+ float elapsed_time;
+
+ struct tcp_cc_request_struct *tcp_cc_request;
+ struct tcp_cc_response_struct *tcp_cc_response;
+ struct tcp_cc_results_struct *tcp_cc_results;
+
+ tcp_cc_request =
+ (struct tcp_cc_request_struct *)netperf_request.content.test_specific_data;
+ tcp_cc_response =
+ (struct tcp_cc_response_struct *)netperf_response.content.test_specific_data;
+ tcp_cc_results =
+ (struct tcp_cc_results_struct *)netperf_response.content.test_specific_data;
+
+ if (debug) {
+ fprintf(where,"netserver: recv_tcp_cc: 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_tcp_cc: setting the response type...\n");
+ fflush(where);
+ }
+
+ netperf_response.content.response_type = TCP_CC_RESPONSE;
+
+ if (debug) {
+ fprintf(where,"recv_tcp_cc: the response type is set...\n");
+ fflush(where);
+ }
+
+ /* set-up the data buffer with the requested alignment and offset */
+ message = (char *)malloc(DATABUFFERLEN);
+ if (message == NULL) {
+ printf("malloc(%d) failed!\n", DATABUFFERLEN);
+ exit(1);
+ }
+
+ /* We now alter the message_ptr variables to be at the desired */
+ /* alignments with the desired offsets. */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_cc: requested recv alignment of %d offset %d\n",
+ tcp_cc_request->recv_alignment,
+ tcp_cc_request->recv_offset);
+ fprintf(where,
+ "recv_tcp_cc: requested send alignment of %d offset %d\n",
+ tcp_cc_request->send_alignment,
+ tcp_cc_request->send_offset);
+ fflush(where);
+ }
+
+ recv_message_ptr = ALIGN_BUFFER(message, tcp_cc_request->recv_alignment, tcp_cc_request->recv_offset);
+
+ send_message_ptr = ALIGN_BUFFER(message, tcp_cc_request->send_alignment, tcp_cc_request->send_offset);
+
+ if (debug) {
+ fprintf(where,"recv_tcp_cc: receive alignment and offset set...\n");
+ fflush(where);
+ }
+
+ /* Grab a socket to listen on, and then listen on it. */
+
+ if (debug) {
+ fprintf(where,"recv_tcp_cc: 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 = tcp_cc_request->send_buf_size;
+ lsr_size_req = tcp_cc_request->recv_buf_size;
+ loc_nodelay = tcp_cc_request->no_delay;
+ loc_rcvavoid = tcp_cc_request->so_rcvavoid;
+ loc_sndavoid = tcp_cc_request->so_sndavoid;
+
+ set_hostname_and_port(local_name,
+ port_buffer,
+ nf_to_af(tcp_cc_request->ipfamily),
+ tcp_cc_request->port);
+
+ local_res = complete_addrinfo(local_name,
+ local_name,
+ port_buffer,
+ nf_to_af(tcp_cc_request->ipfamily),
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ 0);
+
+ s_listen = create_data_socket(local_res);
+
+ if (s_listen == INVALID_SOCKET) {
+ netperf_response.content.serv_errno = errno;
+ send_response();
+ if (debug) {
+ fprintf(where,"could not create data socket\n");
+ fflush(where);
+ }
+ 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();
+ if (debug) {
+ fprintf(where,"could not listen\n");
+ fflush(where);
+ }
+ 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();
+ if (debug) {
+ fprintf(where,"could not geetsockname\n");
+ fflush(where);
+ }
+ 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. */
+
+ tcp_cc_response->data_port_number =
+ (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port);
+ if (debug) {
+ fprintf(where,"telling the remote to call me at %d\n",
+ tcp_cc_response->data_port_number);
+ fflush(where);
+ }
+ netperf_response.content.serv_errno = 0;
+
+ /* But wait, there's more. If the initiator wanted cpu measurements, */
+ /* then we must call the calibrate routine, which will return the max */
+ /* rate back to the initiator. If the CPU was not to be measured, or */
+ /* something went wrong with the calibration, we will return a 0.0 to */
+ /* the initiator. */
+
+ tcp_cc_response->cpu_rate = (float)0.0; /* assume no cpu */
+ if (tcp_cc_request->measure_cpu) {
+ tcp_cc_response->measure_cpu = 1;
+ tcp_cc_response->cpu_rate =
+ calibrate_local_cpu(tcp_cc_request->cpu_rate);
+ }
+
+
+
+ /* before we send the response back to the initiator, pull some of */
+ /* the socket parms from the globals */
+ tcp_cc_response->send_buf_size = lss_size;
+ tcp_cc_response->recv_buf_size = lsr_size;
+ tcp_cc_response->no_delay = loc_nodelay;
+ tcp_cc_response->so_rcvavoid = loc_rcvavoid;
+ tcp_cc_response->so_sndavoid = loc_sndavoid;
+
+ send_response();
+
+ addrlen = sizeof(peeraddr_in);
+
+ /* Now it's time to start receiving data on the connection. We will */
+ /* first grab the apropriate counters and then start grabbing. */
+
+ cpu_start(tcp_cc_request->measure_cpu);
+
+ /* The loop will exit when the sender does a shutdown, which will */
+ /* return a length of zero */
+
+ if (tcp_cc_request->test_length > 0) {
+ times_up = 0;
+ trans_remaining = 0;
+ start_timer(tcp_cc_request->test_length + PAD_TIME);
+ }
+ else {
+ times_up = 1;
+ trans_remaining = tcp_cc_request->test_length * -1;
+ }
+
+ trans_received = 0;
+
+ while ((!times_up) || (trans_remaining > 0)) {
+#ifdef WIN32
+ /* The test timer will probably fire during this accept,
+ so to make the start_timer above work we have to move
+ it to close s_listen while we are blocked on accept. */
+ win_kludge_socket = s_listen;
+#endif
+ /* accept a connection from the remote */
+ if ((s_data=accept(s_listen,
+ (struct sockaddr *)&peeraddr_in,
+ &addrlen)) == INVALID_SOCKET) {
+ if (errno == EINTR) {
+ /* the timer popped */
+ timed_out = 1;
+ break;
+ }
+ fprintf(where,"recv_tcp_cc: accept: errno = %d\n",errno);
+ fflush(where);
+ 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_tcp_cc: accepted data connection.\n");
+ fflush(where);
+ }
+
+
+ /* close the connection. the server will likely do a graceful */
+ /* close of the connection, insuring that all data has arrived at */
+ /* the client. for this it will call shutdown(), and then recv() and */
+ /* then close(). I'm reasonably confident that this is the */
+ /* appropriate sequence of calls - I would like to hear of */
+ /* examples in web servers to the contrary. raj 10/95*/
+ close(s_data);
+
+ trans_received++;
+ if (trans_remaining) {
+ trans_remaining--;
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_cc: Transaction %d complete\n",
+ trans_received);
+ fflush(where);
+ }
+
+ }
+
+
+ /* The loop now exits due to timeout or transaction count being */
+ /* reached */
+
+ cpu_stop(tcp_cc_request->measure_cpu,&elapsed_time);
+
+ if (timed_out) {
+ /* we ended the test by time, which was at least 2 seconds */
+ /* longer than we wanted to run. so, we want to subtract */
+ /* PAD_TIME from the elapsed_time. */
+ elapsed_time -= PAD_TIME;
+ }
+ /* send the results to the sender */
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_cc: got %d transactions\n",
+ trans_received);
+ fflush(where);
+ }
+
+ tcp_cc_results->bytes_received = (trans_received *
+ (tcp_cc_request->request_size +
+ tcp_cc_request->response_size));
+ tcp_cc_results->trans_received = trans_received;
+ tcp_cc_results->elapsed_time = elapsed_time;
+ tcp_cc_results->num_cpus = lib_num_loc_cpus;
+ if (tcp_cc_request->measure_cpu) {
+ tcp_cc_results->cpu_util = calc_cpu_util(elapsed_time);
+ }
+
+ if (debug) {
+ fprintf(where,
+ "recv_tcp_cc: test complete, sending results.\n");
+ fflush(where);
+ }
+
+ send_response();
+
+}
+
+void
+print_sockets_usage()
+{
+
+ fwrite(sockets_usage, sizeof(char), strlen(sockets_usage), stdout);
+ exit(1);
+
+}
+
+void
+scan_sockets_args(int argc, char *argv[])
+
+{
+
+#define SOCKETS_ARGS "b:CDnNhH:L:m:M:p:P:r:R:s:S:T:Vw:W:z46"
+
+ extern char *optarg; /* pointer to option string */
+
+ int c;
+
+ char
+ arg1[BUFSIZ], /* argument holders */
+ arg2[BUFSIZ];
+
+ if (debug) {
+ int i;
+ printf("%s called with the following argument vector\n",
+#if _MSC_VER <= 1200
+ "scan_sockets_args");
+#else
+ __func__);
+#endif
+ for (i = 0; i< argc; i++) {
+ printf("%s ",argv[i]);
+ }
+ printf("\n");
+ }
+
+ strncpy(local_data_port,"0",sizeof(local_data_port));
+ strncpy(remote_data_port,"0",sizeof(remote_data_port));
+
+ /* by default, only a UDP_STREAM test disallows routing, to cover
+ the backsides of incompetent testers who have bogus setups */
+ if (strcasecmp(test_name,"UDP_STREAM") == 0) {
+ routing_allowed = 0;
+ }
+
+ /* 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_sockets_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 'C':
+#ifdef TCP_CORK
+ /* set TCP_CORK */
+ loc_tcpcork = 1;
+ rem_tcpcork = 1; /* however, at first, we ony have cork affect loc */
+#else
+ printf("WARNING: TCP_CORK not available on this platform!\n");
+#endif /* TCP_CORK */
+ break;
+ case 'D':
+ /* set the TCP 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 '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 'R':
+ /* enable/disable routing on the data connection*/
+ routing_allowed = atoi(optarg);
+ break;
+ case 'm':
+ /* set the send size */
+ send_size = convert(optarg);
+ break;
+ case 'M':
+ /* set the recv size */
+ recv_size = convert(optarg);
+ break;
+ case 'n':
+ /* set the local socket type*/
+ local_connected = 1;
+ break;
+ case 'N':
+ /* set the remote socket type*/
+ remote_connected = 1;
+ break;
+ case 'p':
+ /* set the min and max port numbers for the TCP_CRR and TCP_TRR */
+ /* tests. */
+ break_args(optarg,arg1,arg2);
+ if (arg1[0])
+ client_port_min = atoi(arg1);
+ if (arg2[0])
+ client_port_max = atoi(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 '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;
+ };
+ }
+
+#if defined(WANT_FIRST_BURST)
+#if defined(WANT_HISTOGRAM)
+ /* if WANT_FIRST_BURST and WANT_HISTOGRAM are defined and the user
+ indeed wants a non-zero first burst size, and we would emit a
+ histogram, then we should emit a warning that the two are not
+ compatible. raj 2006-01-31 */
+ if ((first_burst_size > 0) && (verbosity >= 2)) {
+ fprintf(stderr,
+ "WARNING! Histograms and first bursts are incompatible!\n");
+ fflush(stderr);
+ }
+#endif
+#endif
+
+ /* we do not want to make remote_data_address non-NULL because if
+ the user has not specified a remote adata address, we want to
+ take it from the hostname in the -H global option. raj
+ 2005-02-08 */
+
+ /* so, if there is to be no control connection, we want to have some
+ different settings for a few things */
+
+ if (no_control) {
+
+ if (strcmp(remote_data_port,"0") == 0) {
+ /* we need to select either the discard port, echo port or
+ chargen port dedepending on the test name. raj 2007-02-08 */
+ if (strstr(test_name,"STREAM") ||
+ strstr(test_name,"SENDFILE")) {
+ strncpy(remote_data_port,"discard",sizeof(remote_data_port));
+ }
+ else if (strstr(test_name,"RR")) {
+ strncpy(remote_data_port,"echo",sizeof(remote_data_port));
+ }
+ else if (strstr(test_name,"MAERTS")) {
+ strncpy(remote_data_port,"chargen",sizeof(remote_data_port));
+ }
+ else {
+ printf("No default port known for the %s test, please set one yourself\n",test_name);
+ exit(-1);
+ }
+ }
+ remote_data_port[sizeof(remote_data_port) - 1] = '\0';
+
+ /* I go back and forth on whether these should become -1 or if
+ they should become 0 for a no_control test. what do you think?
+ raj 2006-02-08 */
+
+ rem_rcvavoid = -1;
+ rem_sndavoid = -1;
+ rss_size_req = -1;
+ rsr_size_req = -1;
+ rem_nodelay = -1;
+
+ if (strstr(test_name,"STREAM") ||
+ strstr(test_name,"SENDFILE")) {
+ recv_size = -1;
+ }
+ else if (strstr(test_name,"MAERTS")) {
+ send_size = -1;
+ }
+ }
+}