diff options
Diffstat (limited to 'src/nettest_bsd.c')
-rw-r--r-- | src/nettest_bsd.c | 13202 |
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; + } + } +} |