blob: 8fdb5f08a615de84dbf85ef0fdc6d20f8046e311 [file] [log] [blame]
#ifndef lint
char nettest_id[]="\
@(#)nettest_bsd.c (c) Copyright 1993-2008 Hewlett-Packard Co. Version 2.4.5";
#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 */
#ifdef WANT_SCTP
#include <netinet/sctp.h>
#endif
#ifndef WIN32
#if !defined(__VMS) && !defined(MSDOS)
#include <sys/ipc.h>
#endif /* !__VMS && !MSDOS */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#else /* WIN32 */
#include <process.h>
#define netperf_socklen_t socklen_t
#include <winsock2.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 */
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 */
#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
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); \
}
#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; \
}
#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
#ifdef WANT_DEMO
#ifdef HAVE_GETHRTIME
static hrtime_t demo_one;
static hrtime_t demo_two;
static hrtime_t *demo_one_ptr = &demo_one;
static hrtime_t *demo_two_ptr = &demo_two;
static hrtime_t *temp_demo_ptr = &demo_one;
#elif defined(WIN32)
static LARGE_INTEGER demo_one;
static LARGE_INTEGER demo_two;
static LARGE_INTEGER *demo_one_ptr = &demo_one;
static LARGE_INTEGER *demo_two_ptr = &demo_two;
static LARGE_INTEGER *temp_demo_ptr = &demo_one;
#else
static struct timeval demo_one;
static struct timeval demo_two;
static struct timeval *demo_one_ptr = &demo_one;
static struct timeval *demo_two_ptr = &demo_two;
static struct timeval *temp_demo_ptr = &demo_one;
#endif
/* for a _STREAM test, "a" should be lss_size and "b" should be
rsr_size. for a _MAERTS test, "a" should be lsr_size and "b" should
be rss_size. raj 2005-04-06 */
#define DEMO_STREAM_SETUP(a,b) \
if ((demo_mode) && (demo_units == 0)) { \
/* take our default value of demo_units to be the larger of \
twice the remote's SO_RCVBUF or twice our SO_SNDBUF */ \
if (a > b) { \
demo_units = 2*a; \
} \
else { \
demo_units = 2*b; \
} \
}
/* now that calc_thruput_interval knows about transactions as a format
we can merge DEMO_STREAM_INTERVAL and DEMO_RR_INTERVAL since the
are the same */
#define DEMO_INTERVAL(units) \
if (demo_mode) { \
double actual_interval; \
units_this_tick += units; \
if (units_this_tick >= demo_units) { \
/* time to possibly update demo_units and maybe output an \
interim result */ \
HIST_timestamp(demo_two_ptr); \
actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); \
/* we always want to fine-tune demo_units here whether we \
emit an interim result or not. if we are short, this \
will lengthen demo_units. if we are long, this will \
shorten it */ \
demo_units = demo_units * (demo_interval / actual_interval); \
if (actual_interval >= demo_interval) { \
/* time to emit an interim result */ \
fprintf(where, \
"Interim result: %.2f %s/s over %.2f seconds\n", \
calc_thruput_interval(units_this_tick, \
actual_interval/1000000.0), \
format_units(), \
actual_interval/1000000.0); \
units_this_tick = 0.0; \
/* now get a new starting timestamp. we could be clever \
and swap pointers - the math we do probably does not \
take all that long, but for now this will suffice */ \
temp_demo_ptr = demo_one_ptr; \
demo_one_ptr = demo_two_ptr; \
demo_two_ptr = temp_demo_ptr; \
} \
} \
}
#define DEMO_STREAM_INTERVAL(units) DEMO_INTERVAL(units)
#define DEMO_RR_SETUP(a) \
if ((demo_mode) && (demo_units == 0)) { \
/* take whatever we are given */ \
demo_units = a; \
}
#define DEMO_RR_INTERVAL(units) DEMO_INTERVAL(units)
#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;
break;
case NF_UNSPEC:
return AF_UNSPEC;
break;
case NF_INET6:
#if defined(AF_INET6)
return AF_INET6;
#else
return AF_UNSPEC;
#endif
break;
default:
return AF_UNSPEC;
break;
}
}
int
af_to_nf(int af) {
switch(af) {
case AF_INET:
return NF_INET;
break;
case AF_UNSPEC:
return NF_UNSPEC;
break;
#if defined(AF_INET6)
case AF_INET6:
return NF_INET6;
break;
#endif
default:
return NF_UNSPEC;
break;
}
}
/* 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
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
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
default:
return "Unknown";
}
}
char *
protocol_to_str(int protocol) {
switch(protocol) {
#ifdef IPPROTO_TCP
case IPPROTO_TCP:
return "TCP";
#endif
#ifdef IPPROTO_UDP
case IPPROTO_UDP:
return "UDP";
#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
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(netperf_socklen_t);
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 */
}
/* 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;
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)
/* 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",
hostname,
port,
family);
fprintf(where,
"\n\tgetaddrinfo returned %d %s\n",
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");
fprintf(where,
"match the requested type. Please contact your vendor for\n");
fprintf(where,
"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 */
if ((change_info & CHANGED_PROTOCOL) &&
!(change_warning_displayed & CHANGED_PROTOCOL) &&
#ifdef __sun
(debug) &&
#endif
(hints.ai_protocol != 0)) {
change_warning_displayed |= CHANGED_PROTOCOL;
fprintf(where,
"WARNING! getaddrinfo returned a protocol other than the\n");
fprintf(where,
"requested protocol. Please contact your vendor for\n");
fprintf(where,
"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");
fprintf(where,
"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");
fprintf(where,
"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) {
*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: {
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) {
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;
int *bar;
int i;
foo->sin6_port = htons((unsigned short) port);
foo->sin6_family = (unsigned short) family;
memcpy(&(foo->sin6_addr),addr,sizeof(foo->sin6_addr));
/* how to put this into "host" order? */
for (i = sizeof(foo->sin6_addr)/sizeof(int), bar=addr; i > 0; i--) {
bar[i] = htonl(bar[i]);
}
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) {
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 *foo;
int i;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sockaddr;
ret = 0;
*port = ntohs(sin6->sin6_port);
memcpy(addr,&(sin6->sin6_addr), sizeof(sin6->sin6_addr));
/* how to put this into "host" order? */
for (i = sizeof(sin6->sin6_addr)/sizeof(int), foo=addr; i > 0; i--) {
if (foo[i] != 0) ret = 1;
foo[i] = ntohl(foo[i]);
}
break;
}
#endif
default:
fprintf(where,
"get_sockaddr_family_addr_port: Unexpected Address Family of %u\n",family);
fflush(where);
exit(-1);
}
return ret;
}
/* 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;
int on = 1;
/*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
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;
}
}
#else
/* it wasn't compiled in... */
loc_rcvavoid = 0;
#endif /* SO_RCV_COPYAVOID */
#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;
}
}
#else
/* it was not compiled in... */
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 operendi (sp) 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 defined(TCP_CORK)
if (loc_tcpcork > 0) {
/* the user wishes for us to set TCP_CORK on the socket */
int one = 1;
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 */
/* 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 errno %d\n",
errno);
fprintf(where," port: %d\n",get_port_number(res));
fflush(where);
}
}
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)
{
#if defined(AF_INET6)
char address_buf[INET6_ADDRSTRLEN];
#else
char address_buf[16]; /* magic constant */
#endif
/* 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,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");
}
/* 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 */
unsigned long long local_bytes_sent = 0;
double bytes_sent = 0.0;
float local_cpu_utilization;
float local_service_demand;
float remote_cpu_utilization;
float remote_service_demand;
double thruput;
struct addrinfo *remote_res;
struct addrinfo *local_res;
struct 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) {
HIST_timestamp(demo_one_ptr);
}
#endif
/* We use an "OR" to control test execution. When the test is */
/* controlled by time, the byte count check will always return false. */
/* When the test is controlled by byte count, the time test will */
/* always return false. When the test is finished, the whole */
/* expression will go false and we will stop sending data. */
while ((!times_up) || (bytes_remaining > 0)) {
#ifdef DIRTY
access_buffer(send_ring->buffer_ptr,
send_size,
loc_dirty_count,
loc_clean_count);
#endif /* DIRTY */
#ifdef WANT_HISTOGRAM
if (verbosity > 1) {
/* timestamp just before we go into send and then again just
after we come out raj 8/94 */
/* but lets only do this if there is going to be a histogram
displayed */
HIST_timestamp(&time_one);
}
#endif /* WANT_HISTOGRAM */
if((len=send(send_socket,
send_ring->buffer_ptr,
send_size,
0)) != send_size) {
if ((len >=0) || SOCKET_EINTR(len)) {
/* the test was interrupted, must be the end of test */
break;
}
perror("netperf: data send error");
printf("len was %d\n",len);
exit(1);
}
local_bytes_sent += send_size;
#ifdef WANT_HISTOGRAM
if (verbosity > 1) {
/* timestamp the exit from the send call and update the histogram */
HIST_timestamp(&time_two);
HIST_add(time_hist,delta_micro(&time_one,&time_two));
}
#endif /* WANT_HISTOGRAM */
#ifdef WANT_DEMO
DEMO_STREAM_INTERVAL(send_size)
#endif
#if defined(WANT_INTERVALS)
INTERVALS_WAIT();
#endif /* WANT_INTERVALS */
/* now we want to move our pointer to the next position in the */
/* data buffer...we may also want to wrap back to the "beginning" */
/* of the bufferspace, so we will mod the number of messages sent */
/* by the send width, and use that to calculate the offset to add */
/* to the base pointer. */
nummessages++;
send_ring = send_ring->next;
if (bytes_remaining) {
bytes_remaining -= send_size;
}
}
/* The test is over. Flush the buffers to the remote end. We do a */
/* graceful release to insure that all data has been taken by the */
/* remote. */
/* but first, if the verbosity is greater than 1, find-out what */
/* the 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);
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 */
double bytes_sent = 0.0;
unsigned long long local_bytes_recvd = 0;
float local_cpu_utilization;
float local_service_demand;
float remote_cpu_utilization;
float remote_service_demand;
double thruput;
struct addrinfo *remote_res;
struct addrinfo *local_res;
struct 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) {
HIST_timestamp(demo_one_ptr);
}
#endif
/* the test will continue until we either get a zero-byte recv()
on the socket or our failsafe timer expires. most of the time
we trust that we get a zero-byte recieve from the socket. raj
2002-06-21 */
#ifdef WANT_HISTOGRAM
if (verbosity > 1) {
/* timestamp just before we go into recv and then again just
after we come out raj 8/94 */
/* but only if we are actually going to display a histogram. raj
2006-02-07 */
HIST_timestamp(&time_one);
}
#endif /* WANT_HISTOGRAM */
while ((!times_up) && (len=recv(recv_socket,
recv_ring->buffer_ptr,
recv_size,
0)) > 0 ) {
#ifdef WANT_HISTOGRAM
if (verbosity > 1) {
/* timestamp the exit from the recv call and update the histogram */
HIST_timestamp(&time_two);
HIST_add(time_hist,delta_micro(&time_one,&time_two));
}
#endif /* WANT_HISTOGRAM */
#ifdef DIRTY
access_buffer(recv_ring->buffer_ptr,
recv_size,
loc_dirty_count,
loc_clean_count);
#endif /* DIRTY */
#ifdef WANT_DEMO
DEMO_STREAM_INTERVAL(len);
#endif
#ifdef WANT_INTERVALS
INTERVALS_WAIT();
#endif /* WANT_INTERVALS */
/* now we want to move our pointer to the next position in the */
/* data buffer...we may also want to wrap back to the "beginning" */
/* of the bufferspace, so we will mod the number of messages sent */
/* by the recv width, and use that to calculate the offset to add */
/* to the base pointer. */
nummessages++;
recv_ring = recv_ring->next;
if (bytes_remaining) {
bytes_remaining -= len;
}
local_bytes_recvd += len;
#ifdef WANT_HISTOGRAM
if (verbosity > 1) {
/* make sure we timestamp just before we go into recv */
/* raj 2004-06-15 */
HIST_timestamp(&time_one);
}
#endif /* WANT_HISTOGRAM */
}
/* an EINTR is to be expected when this is a no_control test */
if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) {
perror("send_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();
/* 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 */
}
}
/* 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,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 */