diff options
Diffstat (limited to 'netlib.c')
-rw-r--r-- | netlib.c | 4161 |
1 files changed, 4161 insertions, 0 deletions
diff --git a/netlib.c b/netlib.c new file mode 100644 index 0000000..bee93c8 --- /dev/null +++ b/netlib.c @@ -0,0 +1,4161 @@ +char netlib_id[]="\ +@(#)netlib.c (c) Copyright 1993-2007 Hewlett-Packard Company. Version 2.4.3"; + + +/****************************************************************/ +/* */ +/* netlib.c */ +/* */ +/* the common utility routines available to all... */ +/* */ +/* establish_control() establish the control socket */ +/* calibrate_local_cpu() do local cpu calibration */ +/* calibrate_remote_cpu() do remote cpu calibration */ +/* send_request() send a request to the remote */ +/* recv_response() receive a response from remote */ +/* send_response() send a response to the remote */ +/* recv_request() recv a request from the remote */ +/* dump_request() dump request contents */ +/* dump_response() dump response contents */ +/* cpu_start() start measuring cpu */ +/* cpu_stop() stop measuring cpu */ +/* calc_cpu_util() calculate the cpu utilization */ +/* calc_service_demand() calculate the service demand */ +/* calc_thruput() calulate the tput in units */ +/* calibrate() really calibrate local cpu */ +/* identify_local() print local host information */ +/* identify_remote() print remote host information */ +/* format_number() format the number (KB, MB,etc) */ +/* format_units() return the format in english */ +/* msec_sleep() sleep for some msecs */ +/* start_timer() start a timer */ +/* */ +/* the routines you get when WANT_DLPI is defined... */ +/* */ +/* dl_open() open a file descriptor and */ +/* attach to the card */ +/* dl_mtu() find the MTU of the card */ +/* dl_bind() bind the sap do the card */ +/* dl_connect() sender's have of connect */ +/* dl_accpet() receiver's half of connect */ +/* dl_set_window() set the window size */ +/* dl_stats() retrieve statistics */ +/* dl_send_disc() initiate disconnect (sender) */ +/* dl_recv_disc() accept disconnect (receiver) */ +/****************************************************************/ + +/****************************************************************/ +/* */ +/* Global include files */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + /* It would seem that most of the includes being done here from */ + /* "sys/" actually have higher-level wrappers at just /usr/include. */ + /* This is based on a spot-check of a couple systems at my disposal. */ + /* If you have trouble compiling you may want to add "sys/" raj 10/95 */ +#include <limits.h> +#include <signal.h> +#ifdef MPE +# define NSIG _NSIG +#endif /* MPE */ +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <assert.h> +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#endif + + +#ifndef WIN32 + /* at some point, I would like to get rid of all these "sys/" */ + /* includes where appropriate. if you have a system that requires */ + /* them, speak now, or your system may not comile later revisions of */ + /* netperf. raj 1/96 */ +#include <unistd.h> +#include <sys/stat.h> +#include <sys/times.h> +#ifndef MPE +#include <time.h> +#include <sys/time.h> +#endif /* MPE */ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <sys/utsname.h> +#if !defined(MPE) && !defined(__VMS) +#include <sys/param.h> +#endif /* MPE */ + +#else /* WIN32 */ + +#include <process.h> +#include <time.h> +#include <winsock2.h> +#define netperf_socklen_t socklen_t +#include <windows.h> + +/* the only time someone should need to define DONT_IPV6 in the + "sources" file is if they are trying to compile on Windows 2000 or + NT4 and I suspect this may not be their only problem :) */ +#ifndef DONT_IPV6 +#include <ws2tcpip.h> +#endif + +#include <windows.h> + +#define SIGALRM (14) +#define sleep(x) Sleep((x)*1000) + +#endif /* WIN32 */ + +#ifdef _AIX +#include <sys/select.h> +#include <sys/sched.h> +#include <sys/pri.h> +#define PRIORITY PRI_LOW +#else/* _AIX */ +#ifdef __sgi +#include <sys/prctl.h> +#include <sys/schedctl.h> +#define PRIORITY NDPLOMIN +#endif /* __sgi */ +#endif /* _AIX */ + +#ifdef WANT_DLPI +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/poll.h> +#ifdef __osf__ +#include <sys/dlpihdr.h> +#else /* __osf__ */ +#include <sys/dlpi.h> +#ifdef __hpux +#include <sys/dlpi_ext.h> +#endif /* __hpux */ +#endif /* __osf__ */ +#endif /* WANT_DLPI */ + +#ifdef HAVE_MPCTL +#include <sys/mpctl.h> +#endif + +#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) +# include "missing/getaddrinfo.h" +#endif + + +#ifdef WANT_HISTOGRAM +#include "hist.h" +#endif /* WANT_HISTOGRAM */ +/****************************************************************/ +/* */ +/* Local Include Files */ +/* */ +/****************************************************************/ +#define NETLIB +#include "netlib.h" +#include "netsh.h" +#include "netcpu.h" + +/****************************************************************/ +/* */ +/* Global constants, macros and variables */ +/* */ +/****************************************************************/ + +#if defined(WIN32) || defined(__VMS) +struct timezone { + int dummy ; + } ; +#ifndef __VMS +SOCKET win_kludge_socket = INVALID_SOCKET; +SOCKET win_kludge_socket2 = INVALID_SOCKET; +#endif /* __VMS */ +#endif /* WIN32 || __VMS */ + +#ifndef LONG_LONG_MAX +#define LONG_LONG_MAX 9223372036854775807LL +#endif /* LONG_LONG_MAX */ + + /* older versions of netperf knew about the HP kernel IDLE counter. */ + /* this is now obsolete - in favor of either pstat(), times, or a */ + /* process-level looper process. we also now require support for the */ + /* "long" integer type. raj 4/95. */ + +int + lib_num_loc_cpus, /* the number of cpus in the system */ + lib_num_rem_cpus; /* how many we think are in the remote */ + +#define PAGES_PER_CHILD 2 + +int lib_use_idle; +int cpu_method; + +struct timeval time1, time2; +struct timezone tz; +float lib_elapsed, + lib_local_maxrate, + lib_remote_maxrate, + lib_local_cpu_util, + lib_remote_cpu_util; + +float lib_local_per_cpu_util[MAXCPUS]; +int lib_cpu_map[MAXCPUS]; + +int *request_array; +int *response_array; + +/* INVALID_SOCKET == INVALID_HANDLE_VALUE == (unsigned int)(~0) == -1 */ +SOCKET netlib_control = INVALID_SOCKET; +SOCKET server_sock = INVALID_SOCKET; + +/* global variables to hold the value for processor affinity */ +int local_proc_affinity,remote_proc_affinity = -1; + +/* these are to allow netperf to be run easily through those evil, + end-to-end breaking things known as firewalls */ +char local_data_port[10]; +char remote_data_port[10]; + +char *local_data_address=NULL; +char *remote_data_address=NULL; + +int local_data_family=AF_UNSPEC; +int remote_data_family=AF_UNSPEC; + + /* in the past, I was overlaying a structure on an array of ints. now */ + /* I am going to have a "real" structure, and point an array of ints */ + /* at it. the real structure will be forced to the same alignment as */ + /* the type "double." this change will mean that pre-2.1 netperfs */ + /* cannot be mixed with 2.1 and later. raj 11/95 */ + +union netperf_request_struct netperf_request; +union netperf_response_struct netperf_response; + +FILE *where; + +char libfmt = '?'; + +#ifdef WANT_DLPI +/* some stuff for DLPI control messages */ +#define DLPI_DATA_SIZE 2048 + +unsigned long control_data[DLPI_DATA_SIZE]; +struct strbuf control_message = {DLPI_DATA_SIZE, 0, (char *)control_data}; + +#endif /* WANT_DLPI */ + +#ifdef WIN32 +HANDLE hAlarm = INVALID_HANDLE_VALUE; +#endif + +int times_up; + +#ifdef WIN32 + /* we use a getopt implementation from net.sources */ +/* + * get option letter from argument vector + */ +int + opterr = 1, /* should error messages be printed? */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char + *optarg; /* argument associated with option */ + +#define EMSG "" + +#endif /* WIN32 */ + +static int measuring_cpu; +int +netlib_get_page_size(void) { + + /* not all systems seem to have the sysconf for page size. for + those which do not, we will assume that the page size is 8192 + bytes. this should be more than enough to be sure that there is + no page or cache thrashing by looper processes on MP + systems. otherwise that's really just too bad - such systems + should define _SC_PAGE_SIZE - raj 4/95 */ + +#ifndef _SC_PAGE_SIZE +#ifdef WIN32 + +SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + + return SystemInfo.dwPageSize; +#else + return(8192L); +#endif /* WIN32 */ +#else + return(sysconf(_SC_PAGE_SIZE)); +#endif /* _SC_PAGE_SIZE */ + +} + + +#ifdef WANT_INTERVALS +static unsigned int usec_per_itvl; + + +void +stop_itimer() + +{ + + struct itimerval new_interval; + struct itimerval old_interval; + + new_interval.it_interval.tv_sec = 0; + new_interval.it_interval.tv_usec = 0; + new_interval.it_value.tv_sec = 0; + new_interval.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL,&new_interval,&old_interval) != 0) { + /* there was a problem arming the interval timer */ + perror("netperf: setitimer"); + exit(1); + } + return; +} +#endif /* WANT_INTERVALS */ + + + +#ifdef WIN32 +static void +error(char *pch) +{ + if (!opterr) { + return; /* without printing */ + } + fprintf(stderr, "%s: %s: %c\n", + (NULL != program) ? program : "getopt", pch, optopt); +} + +int +getopt(int argc, char **argv, char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + + if (!*place) { + /* update scanning pointer */ + if (optind >= argc || *(place = argv[optind]) != '-' || !*++place) { + return EOF; + } + if (*place == '-') { + /* found "--" */ + ++optind; + place = EMSG ; /* Added by shiva for Netperf */ + return EOF; + } + } + + /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' + || !(oli = strchr(ostr, optopt))) { + if (!*place) { + ++optind; + } + error("illegal option"); + return BADCH; + } + if (*++oli != ':') { + /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } else { + /* need an argument */ + if (*place) { + optarg = place; /* no white space */ + } else if (argc <= ++optind) { + /* no arg */ + place = EMSG; + error("option requires an argument"); + return BADCH; + } else { + optarg = argv[optind]; /* white space */ + } + place = EMSG; + ++optind; + } + return optopt; /* return option letter */ +} +#endif /* WIN32 */ + +/*---------------------------------------------------------------------------- + * WIN32 implementation of perror, does not deal very well with WSA errors + * The stdlib.h version of perror only deals with the ancient XENIX error codes. + * + * +*+SAF Why can't all WSA errors go through GetLastError? Most seem to... + *--------------------------------------------------------------------------*/ + +#ifdef WIN32 +void PrintWin32Error(FILE *stream, LPSTR text) +{ + LPSTR szTemp; + DWORD dwResult; + DWORD dwError; + + dwError = GetLastError(); + dwResult = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, + dwError, + LANG_NEUTRAL, + (LPTSTR)&szTemp, + 0, + NULL ); + + if (dwResult) + fprintf(stream, "%s: %s\n", text, szTemp); + else + fprintf(stream, "%s: error 0x%x\n", text, dwError); + fflush(stream); + + if (szTemp) + LocalFree((HLOCAL)szTemp); +} +#endif /* WIN32 */ + + +char * +inet_ttos(int type) +{ + switch (type) { + case SOCK_DGRAM: + return("SOCK_DGRAM"); + break; + case SOCK_STREAM: + return("SOCK_STREAM"); + break; + default: + return("SOCK_UNKNOWN"); + } +} + + + +char unknown[32]; + +char * +inet_ptos(int protocol) { + switch (protocol) { + case IPPROTO_TCP: + return("IPPROTO_TCP"); + break; + case IPPROTO_UDP: + return("IPPROTO_UDP"); + break; +#if defined(IPPROTO_SCTP) + case IPPROTO_SCTP: + return("IPPROTO_SCTP"); + break; +#endif + default: + snprintf(unknown,sizeof(unknown),"IPPROTO_UNKNOWN(%d)",protocol); + return(unknown); + } +} + +/* one of these days, this should not be required */ +#ifndef AF_INET_SDP +#define AF_INET_SDP 27 +#define PF_INET_SDP AF_INET_SDP +#endif + +char * +inet_ftos(int family) +{ + switch(family) { + case AF_INET: + return("AF_INET"); + break; +#if defined(AF_INET6) + case AF_INET6: + return("AF_INET6"); + break; +#endif +#if defined(AF_INET_SDP) + case AF_INET_SDP: + return("AF_INET_SDP"); + break; +#endif + default: + return("AF_UNSPEC"); + } +} + +int +inet_nton(int af, const void *src, char *dst, int cnt) + +{ + + switch (af) { + case AF_INET: + /* magic constants again... :) */ + if (cnt >= 4) { + memcpy(dst,src,4); + return 4; + } + else { + Set_errno(ENOSPC); + return(-1); + } + break; +#if defined(AF_INET6) + case AF_INET6: + if (cnt >= 16) { + memcpy(dst,src,16); + return(16); + } + else { + Set_errno(ENOSPC); + return(-1); + } + break; +#endif + default: + Set_errno(EAFNOSUPPORT); + return(-1); + } +} + +double +ntohd(double net_double) + +{ + /* we rely on things being nicely packed */ + union { + double whole_thing; + unsigned int words[2]; + unsigned char bytes[8]; + } conv_rec; + + unsigned char scratch; + int i; + + /* on those systems where ntohl is a no-op, we want to return the */ + /* original value, unchanged */ + + if (ntohl(1L) == 1L) { + return(net_double); + } + + conv_rec.whole_thing = net_double; + + /* we know that in the message passing routines that ntohl will have */ + /* been called on the 32 bit quantities. we need to put those back */ + /* the way they belong before we swap */ + conv_rec.words[0] = htonl(conv_rec.words[0]); + conv_rec.words[1] = htonl(conv_rec.words[1]); + + /* now swap */ + for (i=0; i<= 3; i++) { + scratch = conv_rec.bytes[i]; + conv_rec.bytes[i] = conv_rec.bytes[7-i]; + conv_rec.bytes[7-i] = scratch; + } + +#if defined(__FLOAT_WORD_ORDER) && defined(__BYTE_ORDER) + if (__FLOAT_WORD_ORDER != __BYTE_ORDER) { + /* Fixup mixed endian floating point machines */ + unsigned int scratch = conv_rec.words[0]; + conv_rec.words[0] = conv_rec.words[1]; + conv_rec.words[1] = scratch; + } +#endif + + return(conv_rec.whole_thing); + +} + +double +htond(double host_double) + +{ + /* we rely on things being nicely packed */ + union { + double whole_thing; + unsigned int words[2]; + unsigned char bytes[8]; + } conv_rec; + + unsigned char scratch; + int i; + + /* on those systems where ntohl is a no-op, we want to return the */ + /* original value, unchanged */ + + if (ntohl(1L) == 1L) { + return(host_double); + } + + conv_rec.whole_thing = host_double; + + /* now swap */ + for (i=0; i<= 3; i++) { + scratch = conv_rec.bytes[i]; + conv_rec.bytes[i] = conv_rec.bytes[7-i]; + conv_rec.bytes[7-i] = scratch; + } + +#if defined(__FLOAT_WORD_ORDER) && defined(__BYTE_ORDER) + if (__FLOAT_WORD_ORDER != __BYTE_ORDER) { + /* Fixup mixed endian floating point machines */ + unsigned int scratch = conv_rec.words[0]; + conv_rec.words[0] = conv_rec.words[1]; + conv_rec.words[1] = scratch; + } +#endif + + /* we know that in the message passing routines htonl will */ + /* be called on the 32 bit quantities. we need to set things up so */ + /* that when this happens, the proper order will go out on the */ + /* network */ + conv_rec.words[0] = htonl(conv_rec.words[0]); + conv_rec.words[1] = htonl(conv_rec.words[1]); + + return(conv_rec.whole_thing); + +} + + +/* one of these days, this should be abstracted-out just like the CPU + util stuff. raj 2005-01-27 */ +int +get_num_cpus() + +{ + + /* on HP-UX, even when we use the looper procs we need the pstat */ + /* call */ + + int temp_cpus; + +#ifdef __hpux +#include <sys/pstat.h> + + struct pst_dynamic psd; + + if (pstat_getdynamic((struct pst_dynamic *)&psd, + (size_t)sizeof(psd), (size_t)1, 0) != -1) { + temp_cpus = psd.psd_proc_cnt; + } + else { + temp_cpus = 1; + } + +#else + /* MW: <unistd.h> was included for non-Windows systems above. */ + /* Thus if _SC_NPROC_ONLN is defined, we should be able to use sysconf. */ +#ifdef _SC_NPROCESSORS_ONLN + temp_cpus = sysconf(_SC_NPROCESSORS_ONLN); + +#ifdef USE_PERFSTAT + temp_cpus = perfstat_cpu(NULL,NULL, sizeof(perfstat_cpu_t), 0); +#endif /* USE_PERFSTAT */ + +#else /* no _SC_NPROCESSORS_ONLN */ + +#ifdef WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + + temp_cpus = SystemInfo.dwNumberOfProcessors; +#else + /* we need to know some other ways to do this, or just fall-back on */ + /* a global command line option - raj 4/95 */ + temp_cpus = shell_num_cpus; +#endif /* WIN32 */ +#endif /* _SC_NPROCESSORS_ONLN */ +#endif /* __hpux */ + + if (temp_cpus > MAXCPUS) { + fprintf(where, + "Sorry, this system has more CPUs (%d) than I can handle (%d).\n", + temp_cpus, + MAXCPUS); + fprintf(where, + "Please alter MAXCPUS in netlib.h and recompile.\n"); + fflush(where); + exit(1); + } + + return(temp_cpus); + +} + +#ifdef WIN32 +#ifdef __GNUC__ + #define S64_SUFFIX(x) x##LL +#else + #define S64_SUFFIX(x) x##i64 +#endif + +/* + * Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 + */ +#define EPOCH_BIAS S64_SUFFIX(116444736000000000) + +/* + * Union to facilitate converting from FILETIME to unsigned __int64 + */ +typedef union { + unsigned __int64 ft_scalar; + FILETIME ft_struct; +} FT; + +void +gettimeofday( struct timeval *tv , struct timezone *not_used ) +{ + FT nt_time; + __int64 UnixTime; /* microseconds since 1/1/1970 */ + + GetSystemTimeAsFileTime( &(nt_time.ft_struct) ); + + UnixTime = ((nt_time.ft_scalar - EPOCH_BIAS) / S64_SUFFIX(10)); + tv->tv_sec = (long)(time_t)(UnixTime / S64_SUFFIX(1000000)); + tv->tv_usec = (unsigned long)(UnixTime % S64_SUFFIX(1000000)); +} +#endif /* WIN32 */ + + + +/************************************************************************/ +/* */ +/* signal catcher */ +/* */ +/************************************************************************/ + +void +#if defined(__hpux) +catcher(sig, code, scp) + int sig; + int code; + struct sigcontext *scp; +#else +catcher(int sig) +#endif /* __hpux || __VMS */ +{ + +#ifdef __hpux + if (debug > 2) { + fprintf(where,"caught signal %d ",sig); + if (scp) { + fprintf(where,"while in syscall %d\n", + scp->sc_syscall); + } + else { + fprintf(where,"null scp\n"); + } + fflush(where); + } +#endif /* RAJ_DEBUG */ + + switch(sig) { + + case SIGINT: + fprintf(where,"netperf: caught SIGINT\n"); + fflush(where); + exit(1); + break; + case SIGALRM: + if (--test_len_ticks == 0) { + /* the test is over */ + if (times_up != 0) { + fprintf(where,"catcher: timer popped with times_up != 0\n"); + fflush(where); + } + times_up = 1; +#if defined(WANT_INTERVALS) && !defined(WANT_SPIN) + stop_itimer(); +#endif /* WANT_INTERVALS */ + break; + } + else { +#ifdef WANT_INTERVALS +#ifdef __hpux + /* the test is not over yet and we must have been using the */ + /* interval timer. if we were in SYS_SIGSUSPEND we want to */ + /* re-start the system call. Otherwise, we want to get out of */ + /* the sigsuspend call. I NEED TO KNOW HOW TO DO THIS FOR OTHER */ + /* OPERATING SYSTEMS. If you know how, please let me know. rick */ + /* jones <raj@cup.hp.com> */ + if (scp->sc_syscall != SYS_SIGSUSPEND) { + if (debug > 2) { + fprintf(where, + "catcher: Time to send burst > interval!\n"); + fflush(where); + } + scp->sc_syscall_action = SIG_RESTART; + } +#endif /* __hpux */ +#else /* WANT_INTERVALS */ + fprintf(where, + "catcher: interval timer running unexpectedly!\n"); + fflush(where); + times_up = 1; +#endif /* WANT_INTERVALS */ + break; + } + } + return; +} + + +void +install_signal_catchers() + +{ + /* just a simple little routine to catch a bunch of signals */ + +#ifndef WIN32 + struct sigaction action; + int i; + + fprintf(where,"installing catcher for all signals\n"); + fflush(where); + + sigemptyset(&(action.sa_mask)); + action.sa_handler = catcher; + +#ifdef SA_INTERRUPT + action.sa_flags = SA_INTERRUPT; +#else /* SA_INTERRUPT */ + action.sa_flags = 0; +#endif /* SA_INTERRUPT */ + + + for (i = 1; i <= NSIG; i++) { + if (i != SIGALRM) { + if (sigaction(i,&action,NULL) != 0) { + fprintf(where, + "Could not install signal catcher for sig %d, errno %d\n", + i, + errno); + fflush(where); + + } + } + } +#else + return; +#endif /* WIN32 */ +} + + +#ifdef WIN32 +#define SIGALRM (14) +void +emulate_alarm( int seconds ) +{ + DWORD ErrorCode; + + /* Wait on this event for parm seconds. */ + + ErrorCode = WaitForSingleObject(hAlarm, seconds*1000); + if (ErrorCode == WAIT_FAILED) + { + perror("WaitForSingleObject failed"); + exit(1); + } + + if (ErrorCode == WAIT_TIMEOUT) + { + /* WaitForSingleObject timed out; this means the timer + wasn't canceled. */ + + times_up = 1; + + /* We have yet to find a good way to fully emulate the effects */ + /* of signals and getting EINTR from system calls under */ + /* winsock, so what we do here is close the socket out from */ + /* under the other thread. It is rather kludgy, but should be */ + /* sufficient to get this puppy shipped. The concept can be */ + /* attributed/blamed :) on Robin raj 1/96 */ + + if (win_kludge_socket != INVALID_SOCKET) { + closesocket(win_kludge_socket); + } + if (win_kludge_socket2 != INVALID_SOCKET) { + closesocket(win_kludge_socket2); + } + } +} + +#endif /* WIN32 */ + +void +start_timer(int time) +{ + +#ifdef WIN32 + /*+*+SAF What if StartTimer is called twice without the first timer */ + /*+*+SAF expiring? */ + + DWORD thread_id ; + HANDLE tHandle; + + if (hAlarm == (HANDLE) INVALID_HANDLE_VALUE) + { + /* Create the Alarm event object */ + hAlarm = CreateEvent( + (LPSECURITY_ATTRIBUTES) NULL, /* no security */ + FALSE, /* auto reset event */ + FALSE, /* init. state = reset */ + (void *)NULL); /* unnamed event object */ + if (hAlarm == (HANDLE) INVALID_HANDLE_VALUE) + { + perror("CreateEvent failure"); + exit(1); + } + } + else + { + ResetEvent(hAlarm); + } + + + tHandle = CreateThread(0, + 0, + (LPTHREAD_START_ROUTINE)emulate_alarm, + (LPVOID)(ULONG_PTR)time, + 0, + &thread_id ) ; + CloseHandle(tHandle); + +#else /* not WIN32 */ + +struct sigaction action; + +if (debug) { + fprintf(where,"About to start a timer for %d seconds.\n",time); + fflush(where); +} + + action.sa_handler = catcher; + sigemptyset(&(action.sa_mask)); + sigaddset(&(action.sa_mask),SIGALRM); + +#ifdef SA_INTERRUPT + /* on some systems (SunOS 4.blah), system calls are restarted. we do */ + /* not want that */ + action.sa_flags = SA_INTERRUPT; +#else /* SA_INTERRUPT */ + action.sa_flags = 0; +#endif /* SA_INTERRUPT */ + + if (sigaction(SIGALRM, &action, NULL) < 0) { + fprintf(where,"start_timer: error installing alarm handler "); + fprintf(where,"errno %d\n",errno); + fflush(where); + exit(1); + } + + /* this is the easy case - just set the timer for so many seconds */ + if (alarm(time) != 0) { + fprintf(where, + "error starting alarm timer, errno %d\n", + errno); + fflush(where); + } +#endif /* WIN32 */ + + test_len_ticks = 1; + +} + + + /* this routine will disable any running timer */ +void +stop_timer() +{ +#ifndef WIN32 + alarm(0); +#else + /* at some point we may need some win32 equivalent */ + if (hAlarm != (HANDLE) INVALID_HANDLE_VALUE) + { + SetEvent(hAlarm); + } +#endif /* WIN32 */ + +} + + +#ifdef WANT_INTERVALS + /* this routine will enable the interval timer and set things up so */ + /* that for a timed test the test will end at the proper time. it */ + /* should detect the presence of POSIX.4 timer_* routines one of */ + /* these days */ +void +start_itimer(unsigned int interval_len_msec ) +{ + + unsigned int ticks_per_itvl; + + struct itimerval new_interval; + struct itimerval old_interval; + + /* if -DWANT_INTERVALS was used, we will use the ticking of the itimer to */ + /* tell us when the test is over. while the user will be specifying */ + /* some number of milliseconds, we know that the interval timer is */ + /* really in units of 1/HZ. so, to prevent the test from running */ + /* "long" it would be necessary to keep this in mind when calculating */ + /* the number of itimer events */ + + ticks_per_itvl = ((interval_wate * sysconf(_SC_CLK_TCK) * 1000) / + 1000000); + + if (ticks_per_itvl == 0) ticks_per_itvl = 1; + + /* how many usecs in each interval? */ + usec_per_itvl = ticks_per_itvl * (1000000 / sysconf(_SC_CLK_TCK)); + + /* how many times will the timer pop before the test is over? */ + if (test_time > 0) { + /* this was a timed test */ + test_len_ticks = (test_time * 1000000) / usec_per_itvl; + } + else { + /* this was not a timed test, use MAXINT */ + test_len_ticks = INT_MAX; + } + + if (debug) { + fprintf(where,"setting the interval timer to %d sec %d usec ", + usec_per_itvl / 1000000, + usec_per_itvl % 1000000); + fprintf(where,"test len %d ticks\n", + test_len_ticks); + fflush(where); + } + + /* if this was not a timed test, then we really aught to enable the */ + /* signal catcher raj 2/95 */ + + new_interval.it_interval.tv_sec = usec_per_itvl / 1000000; + new_interval.it_interval.tv_usec = usec_per_itvl % 1000000; + new_interval.it_value.tv_sec = usec_per_itvl / 1000000; + new_interval.it_value.tv_usec = usec_per_itvl % 1000000; + if (setitimer(ITIMER_REAL,&new_interval,&old_interval) != 0) { + /* there was a problem arming the interval timer */ + perror("netperf: setitimer"); + exit(1); + } +} +#endif /* WANT_INTERVALS */ + +void +netlib_init_cpu_map() { + + int i; +#ifdef HAVE_MPCTL + int num; + i = 0; + /* I go back and forth on whether this should be the system-wide set + of calls, or if the processor set versions (sans the _SYS) should + be used. at the moment I believe that the system-wide version + should be used. raj 2006-04-03 */ + num = mpctl(MPC_GETNUMSPUS_SYS,0,0); + lib_cpu_map[i] = mpctl(MPC_GETFIRSTSPU_SYS,0,0); + for (i = 1;((i < num) && (i < MAXCPUS)); i++) { + lib_cpu_map[i] = mpctl(MPC_GETNEXTSPU_SYS,lib_cpu_map[i-1],0); + } + /* from here, we set them all to -1 because if we launch more + loopers than actual CPUs, well, I'm not sure why :) */ + for (; i < MAXCPUS; i++) { + lib_cpu_map[i] = -1; + } + +#else + /* we assume that there is indeed a contiguous mapping */ + for (i = 0; i < MAXCPUS; i++) { + lib_cpu_map[i] = i; + } +#endif +} + + +/****************************************************************/ +/* */ +/* netlib_init() */ +/* */ +/* initialize the performance library... */ +/* */ +/****************************************************************/ + +void +netlib_init() +{ + int i; + + where = stdout; + + request_array = (int *)(&netperf_request); + response_array = (int *)(&netperf_response); + + for (i = 0; i < MAXCPUS; i++) { + lib_local_per_cpu_util[i] = 0.0; + } + + /* on those systems where we know that CPU numbers may not start at + zero and be contiguous, we provide a way to map from a + contiguous, starting from 0 CPU id space to the actual CPU ids. + at present this is only used for the netcpu_looper stuff because + we ass-u-me that someone setting processor affinity from the + netperf commandline will provide a "proper" CPU identifier. raj + 2006-04-03 */ + + netlib_init_cpu_map(); + + if (debug) { + fprintf(where, + "netlib_init: request_array at %p\n", + request_array); + fprintf(where, + "netlib_init: response_array at %p\n", + response_array); + + fflush(where); + } + +} + + /* this routine will conver the string into an unsigned integer. it */ + /* is used primarily for the command-line options taking a number */ + /* (such as the socket size) which could be rather large. If someone */ + /* enters 32M, then the number will be converted to 32 * 1024 * 1024. */ + /* If they inter 32m, the number will be converted to 32 * 1000 * */ + /* 1000 */ +unsigned int +convert(char *string) + +{ + unsigned int base; + base = atoi(string); + if (strstr(string,"K")) { + base *= 1024; + } + if (strstr(string,"M")) { + base *= (1024 * 1024); + } + if (strstr(string,"G")) { + base *= (1024 * 1024 * 1024); + } + if (strstr(string,"k")) { + base *= (1000); + } + if (strstr(string,"m")) { + base *= (1000 * 1000); + } + if (strstr(string,"g")) { + base *= (1000 * 1000 * 1000); + } + return(base); +} + +/* this routine is like convert, but it is used for an interval time + specification instead of stuff like socket buffer or send sizes. + it converts everything to microseconds for internal use. if there + is an 'm' at the end it assumes the user provided milliseconds, s + will imply seconds, u will imply microseconds. in the future n + will imply nanoseconds but for now it will be ignored. if there is + no suffix or an unrecognized suffix, it will be assumed the user + provided milliseconds, which was the long-time netperf default. one + of these days, we should probably revisit that nanosecond business + wrt the return value being just an int rather than a uint64_t or + something. raj 2006-02-06 */ + +unsigned int +convert_timespec(char *string) { + + unsigned int base; + base = atoi(string); + if (strstr(string,"m")) { + base *= 1000; + } + else if (strstr(string,"u")) { + base *= (1); + } + else if (strstr(string,"s")) { + base *= (1000 * 1000); + } + else { + base *= (1000); + } + return(base); +} + + + /* this routine will allocate a circular list of buffers for either */ + /* send or receive operations. each of these buffers will be aligned */ + /* and offset as per the users request. the circumference of this */ + /* ring will be controlled by the setting of send_width. the buffers */ + /* will be filled with data from the file specified in fill_file. if */ + /* fill_file is an empty string, the buffers will not be filled with */ + /* any particular data */ + +struct ring_elt * +allocate_buffer_ring(int width, int buffer_size, int alignment, int offset) +{ + + struct ring_elt *first_link = NULL; + struct ring_elt *temp_link = NULL; + struct ring_elt *prev_link; + + int i; + int malloc_size; + int bytes_left; + int bytes_read; + int do_fill; + + FILE *fill_source; + char default_fill[] = "netperf"; + int fill_cursor = 0; + + malloc_size = buffer_size + alignment + offset; + + /* did the user wish to have the buffers pre-filled with data from a */ + /* particular source? */ + if (strcmp(fill_file,"") == 0) { + do_fill = 0; + fill_source = NULL; + } + else { + do_fill = 1; + fill_source = (FILE *)fopen(fill_file,"r"); + if (fill_source == (FILE *)NULL) { + perror("Could not open requested fill file"); + exit(1); + } + } + + assert(width >= 1); + + prev_link = NULL; + for (i = 1; i <= width; i++) { + /* get the ring element */ + temp_link = (struct ring_elt *)malloc(sizeof(struct ring_elt)); + if (temp_link == NULL) { + printf("malloc(%u) failed!\n", sizeof(struct ring_elt)); + exit(1); + } + /* remember the first one so we can close the ring at the end */ + if (i == 1) { + first_link = temp_link; + } + temp_link->buffer_base = (char *)malloc(malloc_size); + if (temp_link == NULL) { + printf("malloc(%d) failed!\n", malloc_size); + exit(1); + } + +#ifndef WIN32 + temp_link->buffer_ptr = (char *)(( (long)(temp_link->buffer_base) + + (long)alignment - 1) & + ~((long)alignment - 1)); +#else + temp_link->buffer_ptr = (char *)(( (ULONG_PTR)(temp_link->buffer_base) + + (ULONG_PTR)alignment - 1) & + ~((ULONG_PTR)alignment - 1)); +#endif + temp_link->buffer_ptr += offset; + /* is where the buffer fill code goes. */ + if (do_fill) { + char *bufptr = temp_link->buffer_ptr; + bytes_left = buffer_size; + while (bytes_left) { + if (((bytes_read = (int)fread(bufptr, + 1, + bytes_left, + fill_source)) == 0) && + (feof(fill_source))){ + rewind(fill_source); + } + bufptr += bytes_read; + bytes_left -= bytes_read; + } + } + else { + /* use the default fill to ID our data traffic on the + network. it ain't exactly pretty, but it should work */ + int j; + char *bufptr = temp_link->buffer_ptr; + for (j = 0; j < buffer_size; j++) { + bufptr[j] = default_fill[fill_cursor]; + fill_cursor += 1; + /* the Windows DDK compiler with an x86_64 target wants a cast + here */ + if (fill_cursor > (int)strlen(default_fill)) { + fill_cursor = 0; + } + } + + } + temp_link->next = prev_link; + prev_link = temp_link; + } + if (first_link) { /* SAF Prefast made me do it... */ + first_link->next = temp_link; + } + + return(first_link); /* it's a circle, doesn't matter which we return */ +} + +/* this routine will dirty the first dirty_count bytes of the + specified buffer and/or read clean_count bytes from the buffer. it + will go N bytes at a time, the only question is how large should N + be and if we should be going continguously, or based on some + assumption of cache line size */ + +void +access_buffer(char *buffer_ptr,int length, int dirty_count, int clean_count) { + + char *temp_buffer; + char *limit; + int i, dirty_totals; + + temp_buffer = buffer_ptr; + limit = temp_buffer + length; + dirty_totals = 0; + + for (i = 0; + ((i < dirty_count) && (temp_buffer < limit)); + i++) { + *temp_buffer += (char)i; + dirty_totals += *temp_buffer; + temp_buffer++; + } + + for (i = 0; + ((i < clean_count) && (temp_buffer < limit)); + i++) { + dirty_totals += *temp_buffer; + temp_buffer++; + } + + if (debug > 100) { + fprintf(where, + "This was here to try to avoid dead-code elimination %d\n", + dirty_totals); + fflush(where); + } +} + + +#ifdef HAVE_ICSC_EXS + +#include <sys/mman.h> +#include <sys/exs.h> + + /* this routine will allocate a circular list of buffers for either */ + /* send or receive operations. each of these buffers will be aligned */ + /* and offset as per the users request. the circumference of this */ + /* ring will be controlled by the setting of send_width. the buffers */ + /* will be filled with data from the file specified in fill_file. if */ + /* fill_file is an empty string, the buffers will not be filled with */ + /* any particular data */ + +struct ring_elt * +allocate_exs_buffer_ring (int width, int buffer_size, int alignment, int offset, exs_mhandle_t *mhandlep) +{ + + struct ring_elt *first_link; + struct ring_elt *temp_link; + struct ring_elt *prev_link; + + int i; + int malloc_size; + int bytes_left; + int bytes_read; + int do_fill; + + FILE *fill_source; + + int mmap_size; + char *mmap_buffer, *mmap_buffer_aligned; + + malloc_size = buffer_size + alignment + offset; + + /* did the user wish to have the buffers pre-filled with data from a */ + /* particular source? */ + if (strcmp (fill_file, "") == 0) { + do_fill = 0; + fill_source = NULL; + } else { + do_fill = 1; + fill_source = (FILE *) fopen (fill_file, "r"); + if (fill_source == (FILE *) NULL) { + perror ("Could not open requested fill file"); + exit (1); + } + } + + assert (width >= 1); + + if (debug) { + fprintf (where, "allocate_exs_buffer_ring: " + "width=%d buffer_size=%d alignment=%d offset=%d\n", + width, buffer_size, alignment, offset); + } + + /* allocate shared memory */ + mmap_size = width * malloc_size; + mmap_buffer = (char *) mmap ((caddr_t)NULL, mmap_size+NBPG-1, + PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (mmap_buffer == NULL) { + perror ("allocate_exs_buffer_ring: mmap failed"); + exit (1); + } + mmap_buffer_aligned = (char *) ((uintptr_t)mmap_buffer & ~(NBPG-1)); + if (debug) { + fprintf (where, "allocate_exs_buffer_ring: " + "mmap buffer size=%d address=0x%p aligned=0x%p\n", + mmap_size, mmap_buffer, mmap_buffer_aligned); + } + + /* register shared memory */ + *mhandlep = exs_mregister ((void *)mmap_buffer_aligned, (size_t)mmap_size, 0); + if (*mhandlep == EXS_MHANDLE_INVALID) { + perror ("allocate_exs_buffer_ring: exs_mregister failed"); + exit (1); + } + if (debug) { + fprintf (where, "allocate_exs_buffer_ring: mhandle=%d\n", + *mhandlep); + } + + /* allocate ring elements */ + first_link = (struct ring_elt *) malloc (width * sizeof (struct ring_elt)); + if (first_link == NULL) { + printf ("malloc(%d) failed!\n", width * sizeof (struct ring_elt)); + exit (1); + } + + /* initialize buffer ring */ + prev_link = first_link + width - 1; + + for (i = 0, temp_link = first_link; i < width; i++, temp_link++) { + + temp_link->buffer_base = (char *) mmap_buffer_aligned + (i*malloc_size); +#ifndef WIN32 + temp_link->buffer_ptr = (char *) + (((long)temp_link->buffer_base + (long)alignment - 1) & + ~((long)alignment - 1)); +#else + temp_link->buffer_ptr = (char *) + (((ULONG_PTR)temp_link->buffer_base + (ULONG_PTR)alignment - 1) & + ~((ULONG_PTR)alignment - 1)); +#endif + temp_link->buffer_ptr += offset; + + if (debug) { + fprintf (where, "allocate_exs_buffer_ring: " + "buffer: index=%d base=0x%p ptr=0x%p\n", + i, temp_link->buffer_base, temp_link->buffer_ptr); + } + + /* is where the buffer fill code goes. */ + if (do_fill) { + bytes_left = buffer_size; + while (bytes_left) { + if (((bytes_read = (int) fread (temp_link->buffer_ptr, + 1, + bytes_left, + fill_source)) == 0) && + (feof (fill_source))) { + rewind (fill_source); + } + bytes_left -= bytes_read; + } + } + + /* do linking */ + prev_link->next = temp_link; + prev_link = temp_link; + } + + return (first_link); /* it's a circle, doesn't matter which we return */ +} + +#endif /* HAVE_ICSC_EXS */ + + + +#ifdef HAVE_SENDFILE +/* this routine will construct a ring of sendfile_ring_elt structs + that the routine sendfile_tcp_stream() will use to get parameters + to its calls to sendfile(). It will setup the ring to point at the + file specified in the global -F option that is already used to + pre-fill buffers in the send() case. 08/2000 + + if there is no file specified in a global -F option, we will create + a tempoarary file and fill it with random data and use that + instead. raj 2007-08-09 */ + +struct sendfile_ring_elt * +alloc_sendfile_buf_ring(int width, + int buffer_size, + int alignment, + int offset) + +{ + + struct sendfile_ring_elt *first_link = NULL; + struct sendfile_ring_elt *temp_link = NULL; + struct sendfile_ring_elt *prev_link; + + int i; + int fildes; + struct stat statbuf; + + /* if the user has not specified a file with the -F option, we will + fail the test. otherwise, go ahead and try to open the + file. 08/2000 */ + if (strcmp(fill_file,"") == 0) { + /* use an temp file for the fill file */ + char *temp_file; + int *temp_buffer; + + /* make sure we have at least an ints worth, even if the user is + using an insane buffer size for a sendfile test. we are + ass-u-me-ing that malloc will return something at least aligned + on an int boundary... */ + temp_buffer = (int *) malloc(buffer_size + sizeof(int)); + if (temp_buffer) { + /* ok, we have the buffer we are going to write, lets get a + temporary filename */ + temp_file = tmpnam(NULL); + if (NULL != temp_file) { + fildes = open(temp_file,O_RDWR | O_EXCL | O_CREAT,0600); + if (-1 != fildes) { + int count; + int *int_ptr; + + /* initialize the random number generator */ + srand(getpid()); + + /* unlink the file so it goes poof when we + exit. unless/until shown to be a problem we will + blissfully ignore the return value. raj 2007-08-09 */ + unlink(temp_file); + + /* now fill-out the file with at least buffer_size * width bytes */ + for (count = 0; count < width; count++) { + /* fill the buffer with random data. it doesn't have to be + really random, just "random enough" :) we do this here rather + than up above because we want each write to the file to be + different random data */ + int_ptr = temp_buffer; + for (i = 0; i <= buffer_size/sizeof(int); i++) { + *int_ptr = rand(); + int_ptr++; + } + if (write(fildes,temp_buffer,buffer_size+sizeof(int)) != + buffer_size + sizeof(int)) { + perror("allocate_sendfile_buf_ring: incomplete write"); + exit(-1); + } + } + } + else { + perror("allocate_sendfile_buf_ring: could not open tempfile"); + exit(-1); + } + } + else { + perror("allocate_sendfile_buf_ring: could not allocate temp name"); + exit(-1); + } + } + else { + perror("alloc_sendfile_buf_ring: could not allocate buffer for file"); + exit(-1); + } + } + else { + /* the user pointed us at a file, so try it */ + fildes = open(fill_file , O_RDONLY); + if (fildes == -1){ + perror("alloc_sendfile_buf_ring: Could not open requested file"); + exit(1); + } + /* make sure there is enough file there to allow us to make a + complete ring. that way we do not need additional logic in the + ring setup to deal with wrap-around issues. we might want that + someday, but not just now. 08/2000 */ + if (stat(fill_file,&statbuf) != 0) { + perror("alloc_sendfile_buf_ring: could not stat file"); + exit(1); + } + if (statbuf.st_size < (width * buffer_size)) { + /* the file is too short */ + fprintf(stderr,"alloc_sendfile_buf_ring: specified file too small.\n"); + fprintf(stderr,"file must be larger than send_width * send_size\n"); + fflush(stderr); + exit(1); + } + } + + /* so, at this point we know that fildes is a descriptor which + references a file of sufficient size for our nefarious + porpoises. raj 2007-08-09 */ + + prev_link = NULL; + for (i = 1; i <= width; i++) { + /* get the ring element. we should probably make sure the malloc() + was successful, but for now we'll just let the code bomb + mysteriously. 08/2000 */ + + temp_link = (struct sendfile_ring_elt *) + malloc(sizeof(struct sendfile_ring_elt)); + if (temp_link == NULL) { + printf("malloc(%u) failed!\n", sizeof(struct sendfile_ring_elt)); + exit(1); + } + + /* remember the first one so we can close the ring at the end */ + + if (i == 1) { + first_link = temp_link; + } + + /* now fill-in the fields of the structure with the apropriate + stuff. just how should we deal with alignment and offset I + wonder? until something better comes-up, I think we will just + ignore them. 08/2000 */ + + temp_link->fildes = fildes; /* from which file do we send? */ + temp_link->offset = offset; /* starting at which offset? */ + offset += buffer_size; /* get ready for the next elt */ + temp_link->length = buffer_size; /* how many bytes to send */ + temp_link->hdtrl = NULL; /* no header or trailer */ + temp_link->flags = 0; /* no flags */ + + /* is where the buffer fill code went. */ + + temp_link->next = prev_link; + prev_link = temp_link; + } + /* close the ring */ + first_link->next = temp_link; + + return(first_link); /* it's a dummy ring */ +} + +#endif /* HAVE_SENDFILE */ + + + /***********************************************************************/ + /* */ + /* dump_request() */ + /* */ + /* display the contents of the request array to the user. it will */ + /* display the contents in decimal, hex, and ascii, with four bytes */ + /* per line. */ + /* */ + /***********************************************************************/ + +void +dump_request() +{ +int counter = 0; +fprintf(where,"request contents:\n"); +for (counter = 0; counter < ((sizeof(netperf_request)/4)-3); counter += 4) { + fprintf(where,"%d:\t%8x %8x %8x %8x \t|%4.4s| |%4.4s| |%4.4s| |%4.4s|\n", + counter, + request_array[counter], + request_array[counter+1], + request_array[counter+2], + request_array[counter+3], + (char *)&request_array[counter], + (char *)&request_array[counter+1], + (char *)&request_array[counter+2], + (char *)&request_array[counter+3]); +} +fflush(where); +} + + + /***********************************************************************/ + /* */ + /* dump_response() */ + /* */ + /* display the content of the response array to the user. it will */ + /* display the contents in decimal, hex, and ascii, with four bytes */ + /* per line. */ + /* */ + /***********************************************************************/ + +void +dump_response() +{ +int counter = 0; + +fprintf(where,"response contents\n"); +for (counter = 0; counter < ((sizeof(netperf_response)/4)-3); counter += 4) { + fprintf(where,"%d:\t%8x %8x %8x %8x \t>%4.4s< >%4.4s< >%4.4s< >%4.4s<\n", + counter, + response_array[counter], + response_array[counter+1], + response_array[counter+2], + response_array[counter+3], + (char *)&response_array[counter], + (char *)&response_array[counter+1], + (char *)&response_array[counter+2], + (char *)&response_array[counter+3]); +} +fflush(where); +} + + /* + + format_number() + + return a pointer to a formatted string containing the value passed + translated into the units specified. It assumes that the base units + are bytes. If the format calls for bits, it will use SI units (10^) + if the format calls for bytes, it will use CS units (2^)... This + routine should look familiar to uses of the latest ttcp... + + we would like to use "t" or "T" for transactions, but probably + should leave those for terabits and terabytes respectively, so for + transactions, we will use "x" which will, by default, do absolutely + nothing to the result. why? so we don't have to special case code + elsewhere such as in the TCP_RR-as-bidirectional test case. + + */ + + +char * +format_number(double number) +{ + static char fmtbuf[64]; + + switch (libfmt) { + case 'K': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f" , number / 1024.0); + break; + case 'M': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0 / 1024.0); + break; + case 'G': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0 / 1024.0 / 1024.0); + break; + case 'k': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0); + break; + case 'm': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0 / 1000.0); + break; + case 'g': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number * 8 / 1000.0 / 1000.0 / 1000.0); + break; + case 'x': + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number); + break; + default: + snprintf(fmtbuf, sizeof(fmtbuf), "%-7.2f", number / 1024.0); + } + + return fmtbuf; +} + +char +format_cpu_method(int method) +{ + + char method_char; + + switch (method) { + case CPU_UNKNOWN: + method_char = 'U'; + break; + case HP_IDLE_COUNTER: + method_char = 'I'; + break; + case PSTAT: + method_char = 'P'; + break; + case KSTAT: + method_char = 'K'; + break; + case KSTAT_10: + method_char = 'M'; + break; + case PERFSTAT: + method_char = 'E'; + break; + case TIMES: /* historical only, completely unsuitable + for netperf's purposes */ + method_char = 'T'; + break; + case GETRUSAGE: /* historical only, completely unsuitable + for netperf;s purposes */ + method_char = 'R'; + break; + case LOOPER: + method_char = 'L'; + break; + case NT_METHOD: + method_char = 'N'; + break; + case PROC_STAT: + method_char = 'S'; + break; + case SYSCTL: + method_char = 'C'; + break; + case OSX: + method_char = 'O'; + break; + default: + method_char = '?'; + } + + return method_char; + +} + +char * +format_units() +{ + static char unitbuf[64]; + + switch (libfmt) { + case 'K': + strcpy(unitbuf, "KBytes"); + break; + case 'M': + strcpy(unitbuf, "MBytes"); + break; + case 'G': + strcpy(unitbuf, "GBytes"); + break; + case 'k': + strcpy(unitbuf, "10^3bits"); + break; + case 'm': + strcpy(unitbuf, "10^6bits"); + break; + case 'g': + strcpy(unitbuf, "10^9bits"); + break; + case 'x': + strcpy(unitbuf, "Trans"); + break; + + default: + strcpy(unitbuf, "KBytes"); + } + + return unitbuf; +} + + +/****************************************************************/ +/* */ +/* shutdown_control() */ +/* */ +/* tear-down the control connection between me and the server. */ +/****************************************************************/ + +void +shutdown_control() +{ + + char *buf = (char *)&netperf_response; + int buflen = sizeof(netperf_response); + + /* stuff for select, use fd_set for better compliance */ + fd_set readfds; + struct timeval timeout; + + if (debug) { + fprintf(where, + "shutdown_control: shutdown of control connection requested.\n"); + fflush(where); + } + + /* first, we say that we will be sending no more data on the */ + /* connection */ + if (shutdown(netlib_control,1) == SOCKET_ERROR) { + Print_errno(where, + "shutdown_control: error in shutdown"); + fflush(where); + exit(1); + } + + /* Now, we hang on a select waiting for the socket to become */ + /* readable to receive the shutdown indication from the remote. this */ + /* will be "just" like the recv_response() code */ + + /* we only select once. it is assumed that if the response is split */ + /* (which should not be happening, that we will receive the whole */ + /* thing and not have a problem ;-) */ + + FD_ZERO(&readfds); + FD_SET(netlib_control,&readfds); + timeout.tv_sec = 60; /* wait one minute then punt */ + timeout.tv_usec = 0; + + /* select had better return one, or there was either a problem or a */ + /* timeout... */ + if (select(FD_SETSIZE, + &readfds, + 0, + 0, + &timeout) != 1) { + Print_errno(where, + "shutdown_control: no response received"); + fflush(where); + exit(1); + } + + /* we now assume that the socket has come ready for reading */ + recv(netlib_control, buf, buflen,0); + +} + +/* + bind_to_specific_processor will bind the calling process to the + processor in "processor" It has lots of ugly ifdefs to deal with + all the different ways systems do processor affinity. this is a + generalization of work initially done by stephen burger. raj + 2004/12/13 */ + +void +bind_to_specific_processor(int processor_affinity, int use_cpu_map) +{ + + int mapped_affinity; + + /* this is in place because the netcpu_looper processor affinity + ass-u-me-s a contiguous CPU id space starting with 0. for the + regular netperf/netserver affinity, we ass-u-me the user has used + a suitable CPU id even when the space is not contiguous and + starting from zero */ + if (use_cpu_map) { + mapped_affinity = lib_cpu_map[processor_affinity]; + } + else { + mapped_affinity = processor_affinity; + } + +#ifdef HAVE_MPCTL + /* indeed, at some point it would be a good idea to check the return + status and pass-along notification of error... raj 2004/12/13 */ + mpctl(MPC_SETPROCESS_FORCE, mapped_affinity, getpid()); +#elif HAVE_PROCESSOR_BIND +#include <sys/types.h> +#include <sys/processor.h> +#include <sys/procset.h> + processor_bind(P_PID,P_MYID,mapped_affinity,NULL); +#elif HAVE_BINDPROCESSOR +#include <sys/processor.h> + /* this is the call on AIX. It takes a "what" of BINDPROCESS or + BINDTHRAD, then "who" and finally "where" which is a CPU number + or it seems PROCESSOR_CLASS_ANY there also seems to be a mycpu() + call to return the current CPU assignment. this is all based on + the sys/processor.h include file. from empirical testing, it + would seem that the my_cpu() call returns the current CPU on + which we are running rather than the CPU binding, so it's return + value will not tell you if you are bound vs unbound. */ + bindprocessor(BINDPROCESS,getpid(),(cpu_t)mapped_affinity); +#elif HAVE_SCHED_SETAFFINITY +#include <sched.h> + /* in theory this should cover systems with more CPUs than bits in a + long, without having to specify __USE_GNU. we "cheat" by taking + defines from /usr/include/bits/sched.h, which we ass-u-me is + included by <sched.h>. If they are not there we will just + fall-back on what we had before, which is to use just the size of + an unsigned long. raj 2006-09-14 */ + +#if defined(__CPU_SETSIZE) +#define NETPERF_CPU_SETSIZE __CPU_SETSIZE +#define NETPERF_CPU_SET(cpu, cpusetp) __CPU_SET(cpu, cpusetp) +#define NETPERF_CPU_ZERO(cpusetp) __CPU_ZERO (cpusetp) + typedef cpu_set_t netperf_cpu_set_t; +#else +#define NETPERF_CPU_SETSIZE sizeof(unsigned long) +#define NETPERF_CPU_SET(cpu, cpusetp) *cpusetp = 1 << cpu +#define NETPERF_CPU_ZERO(cpusetp) *cpusetp = (unsigned long)0 + typedef unsigned long netperf_cpu_set_t; +#endif + + netperf_cpu_set_t netperf_cpu_set; + unsigned int len = sizeof(netperf_cpu_set); + + if (mapped_affinity < 8*sizeof(netperf_cpu_set)) { + NETPERF_CPU_ZERO(&netperf_cpu_set); + NETPERF_CPU_SET(mapped_affinity,&netperf_cpu_set); + + if (sched_setaffinity(getpid(), len, &netperf_cpu_set)) { + if (debug) { + fprintf(stderr, "failed to set PID %d's CPU affinity errno %d\n", + getpid(),errno); + fflush(stderr); + } + } + } + else { + if (debug) { + fprintf(stderr, + "CPU number larger than pre-compiled limits. Consider a recompile.\n"); + fflush(stderr); + } + } + +#elif HAVE_BIND_TO_CPU_ID + /* this is the one for Tru64 */ +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/processor.h> + + /* really should be checking a return code one of these days. raj + 2005/08/31 */ + + bind_to_cpu_id(getpid(), mapped_affinity,0); + +#elif WIN32 + + { + ULONG_PTR AffinityMask; + ULONG_PTR ProcessAffinityMask; + ULONG_PTR SystemAffinityMask; + + if ((mapped_affinity < 0) || + (mapped_affinity > MAXIMUM_PROCESSORS)) { + fprintf(where, + "Invalid processor_affinity specified: %d\n", mapped_affinity); fflush(where); + return; + } + + if (!GetProcessAffinityMask( + GetCurrentProcess(), + &ProcessAffinityMask, + &SystemAffinityMask)) + { + perror("GetProcessAffinityMask failed"); + fflush(stderr); + exit(1); + } + + AffinityMask = (ULONG_PTR)1 << mapped_affinity; + + if (AffinityMask & ProcessAffinityMask) { + if (!SetThreadAffinityMask( GetCurrentThread(), AffinityMask)) { + perror("SetThreadAffinityMask failed"); + fflush(stderr); + } + } else if (debug) { + fprintf(where, + "Processor affinity set to CPU# %d\n", mapped_affinity); + fflush(where); + } + } + +#else + if (debug) { + fprintf(where, + "Processor affinity not available for this platform!\n"); + fflush(where); + } +#endif +} + + +/* + * Sets a socket to non-blocking operation. + */ +int +set_nonblock (SOCKET sock) +{ +#ifdef WIN32 + unsigned long flags = 1; + return (ioctlsocket(sock, FIONBIO, &flags) != SOCKET_ERROR); +#else + return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1); +#endif +} + + + + /***********************************************************************/ + /* */ + /* send_request() */ + /* */ + /* send a netperf request on the control socket to the remote half of */ + /* the connection. to get us closer to intervendor interoperability, */ + /* we will call htonl on each of the int that compose the message to */ + /* be sent. the server-half of the connection will call the ntohl */ + /* routine to undo any changes that may have been made... */ + /* */ + /***********************************************************************/ + +void +send_request() +{ + int counter=0; + + /* display the contents of the request if the debug level is high */ + /* enough. otherwise, just send the darned thing ;-) */ + + if (debug > 1) { + fprintf(where,"entered send_request...contents before htonl:\n"); + dump_request(); + } + + /* pass the processor affinity request value to netserver */ + /* this is a kludge and I know it. sgb 8/11/04 */ + + netperf_request.content.dummy = remote_proc_affinity; + + /* put the entire request array into network order. We do this */ + /* arbitrarily rather than trying to figure-out just how much */ + /* of the request array contains real information. this should */ + /* be simpler, and at any rate, the performance of sending */ + /* control messages for this benchmark is not of any real */ + /* concern. */ + + for (counter=0;counter < sizeof(netperf_request)/4; counter++) { + request_array[counter] = htonl(request_array[counter]); + } + + if (debug > 1) { + fprintf(where,"send_request...contents after htonl:\n"); + dump_request(); + + fprintf(where, + "\nsend_request: about to send %u bytes from %p\n", + sizeof(netperf_request), + &netperf_request); + fflush(where); + } + + if (send(netlib_control, + (char *)&netperf_request, + sizeof(netperf_request), + 0) != sizeof(netperf_request)) { + perror("send_request: send call failure"); + + exit(1); + } +} + +/***********************************************************************/ + /* */ + /* send_response() */ + /* */ + /* send a netperf response on the control socket to the remote half of */ + /* the connection. to get us closer to intervendor interoperability, */ + /* we will call htonl on each of the int that compose the message to */ + /* be sent. the other half of the connection will call the ntohl */ + /* routine to undo any changes that may have been made... */ + /* */ + /***********************************************************************/ + +void +send_response() +{ + int counter=0; + int bytes_sent; + + /* display the contents of the request if the debug level is high */ + /* enough. otherwise, just send the darned thing ;-) */ + + if (debug > 1) { + fprintf(where, + "send_response: contents of %u ints before htonl\n", + sizeof(netperf_response)/4); + dump_response(); + } + + /* put the entire response_array into network order. We do this */ + /* arbitrarily rather than trying to figure-out just how much of the */ + /* request array contains real information. this should be simpler, */ + /* and at any rate, the performance of sending control messages for */ + /* this benchmark is not of any real concern. */ + + for (counter=0;counter < sizeof(netperf_response)/4; counter++) { + response_array[counter] = htonl(response_array[counter]); + } + + if (debug > 1) { + fprintf(where, + "send_response: contents after htonl\n"); + dump_response(); + fprintf(where, + "about to send %u bytes from %p\n", + sizeof(netperf_response), + &netperf_response); + fflush(where); + } + + /*KC*/ + if ((bytes_sent = send(server_sock, + (char *)&netperf_response, + sizeof(netperf_response), + 0)) != sizeof(netperf_response)) { + perror("send_response: send call failure"); + fprintf(where, "BytesSent: %d\n", bytes_sent); + exit(1); + } + +} + + /***********************************************************************/ + /* */ + /* recv_request() */ + /* */ + /* receive the remote's request on the control socket. we will put */ + /* the entire response into host order before giving it to the */ + /* calling routine. hopefully, this will go most of the way to */ + /* insuring intervendor interoperability. if there are any problems, */ + /* we will just punt the entire situation. */ + /* */ + /***********************************************************************/ + +void +recv_request() +{ +int tot_bytes_recvd, + bytes_recvd, + bytes_left; +char *buf = (char *)&netperf_request; +int buflen = sizeof(netperf_request); +int counter; + +tot_bytes_recvd = 0; + bytes_recvd = 0; /* nt_lint; bytes_recvd uninitialized if buflen == 0 */ +bytes_left = buflen; +while ((tot_bytes_recvd != buflen) && + ((bytes_recvd = recv(server_sock, buf, bytes_left,0)) > 0 )) { + tot_bytes_recvd += bytes_recvd; + buf += bytes_recvd; + bytes_left -= bytes_recvd; +} + +/* put the request into host order */ + +for (counter = 0; counter < sizeof(netperf_request)/sizeof(int); counter++) { + request_array[counter] = ntohl(request_array[counter]); +} + +if (debug) { + fprintf(where, + "recv_request: received %d bytes of request.\n", + tot_bytes_recvd); + fflush(where); +} + +if (bytes_recvd == SOCKET_ERROR) { + Print_errno(where, + "recv_request: error on recv"); + fflush(where); + exit(1); +} + +if (bytes_recvd == 0) { + /* the remote has shutdown the control connection, we should shut it */ + /* down as well and exit */ + + if (debug) { + fprintf(where, + "recv_request: remote requested shutdown of control\n"); + fflush(where); + } + + if (netlib_control != INVALID_SOCKET) { + shutdown_control(); + } + exit(0); +} + +if (tot_bytes_recvd < buflen) { + if (debug > 1) + dump_request(); + + fprintf(where, + "recv_request: partial request received of %d bytes\n", + tot_bytes_recvd); + fflush(where); + exit(1); +} + + if (debug > 1) { + dump_request(); + } + + /* get the processor affinity request value from netperf */ + /* this is a kludge and I know it. sgb 8/11/04 */ + + local_proc_affinity = netperf_request.content.dummy; + + if (local_proc_affinity != -1) { + bind_to_specific_processor(local_proc_affinity,0); + } + +} + + /* + + recv_response_timed() + + receive the remote's response on the control socket. we will put the + entire response into host order before giving it to the calling + routine. hopefully, this will go most of the way to insuring + intervendor interoperability. if there are any problems, we will just + punt the entire situation. + + The call to select at the beginning is to get us out of hang + situations where the remote gives-up but we don't find-out about + it. This seems to happen only rarely, but it would be nice to be + somewhat robust ;-) + + The "_timed" part is to allow the caller to add (or I suppose + subtract) from the length of timeout on the select call. this was + added since not all the CPU utilization mechanisms require a 40 + second calibration, and we used to have an aribtrary 40 second sleep + in "calibrate_remote_cpu" - since we don't _always_ need that, we + want to simply add 40 seconds to the select() timeout from that call, + but don't want to change all the "recv_response" calls in the code + right away. sooo, we push the functionality of the old + recv_response() into a new recv_response_timed(addl_timout) call, and + have recv_response() call recv_response_timed(0). raj 2005-05-16 + + */ + + +void +recv_response_timed(int addl_time) +{ +int tot_bytes_recvd, + bytes_recvd = 0, + bytes_left; +char *buf = (char *)&netperf_response; +int buflen = sizeof(netperf_response); +int counter; + + /* stuff for select, use fd_set for better compliance */ +fd_set readfds; +struct timeval timeout; + +tot_bytes_recvd = 0; +bytes_left = buflen; + +/* zero out the response structure */ + +/* BUG FIX SJB 2/4/93 - should be < not <= */ +for (counter = 0; counter < sizeof(netperf_response)/sizeof(int); counter++) { + response_array[counter] = 0; +} + + /* we only select once. it is assumed that if the response is split */ + /* (which should not be happening, that we will receive the whole */ + /* thing and not have a problem ;-) */ + +FD_ZERO(&readfds); +FD_SET(netlib_control,&readfds); +timeout.tv_sec = 120 + addl_time; /* wait at least two minutes + before punting - the USE_LOOPER + CPU stuff may cause remote's to + have a bit longer time of it + than 60 seconds would allow. + triggered by fix from Jeff + Dwork. */ +timeout.tv_usec = 0; + + /* select had better return one, or there was either a problem or a */ + /* timeout... */ + +if ((counter = select(FD_SETSIZE, + &readfds, + 0, + 0, + &timeout)) != 1) { + fprintf(where, + "netperf: receive_response: no response received. errno %d counter %d\n", + errno, + counter); + exit(1); +} + +while ((tot_bytes_recvd != buflen) && + ((bytes_recvd = recv(netlib_control, buf, bytes_left,0)) > 0 )) { + tot_bytes_recvd += bytes_recvd; + buf += bytes_recvd; + bytes_left -= bytes_recvd; +} + +if (debug) { + fprintf(where,"recv_response: received a %d byte response\n", + tot_bytes_recvd); + fflush(where); +} + +/* put the response into host order */ + +for (counter = 0; counter < sizeof(netperf_response)/sizeof(int); counter++) { + response_array[counter] = ntohl(response_array[counter]); +} + +if (bytes_recvd == SOCKET_ERROR) { + perror("recv_response"); + exit(1); +} +if (tot_bytes_recvd < buflen) { + fprintf(stderr, + "recv_response: partial response received: %d bytes\n", + tot_bytes_recvd); + fflush(stderr); + if (debug > 1) + dump_response(); + exit(1); +} +if (debug > 1) { + dump_response(); +} +} + +void +recv_response() +{ + recv_response_timed(0); +} + + + +#if defined(USE_PSTAT) || defined (USE_SYSCTL) +int +hi_32(big_int) + long long *big_int; +{ + union overlay_u { + long long dword; + long words[2]; + } *overlay; + + overlay = (union overlay_u *)big_int; + /* on those systems which are byte swapped, we really wish to */ + /* return words[1] - at least I think so - raj 4/95 */ + if (htonl(1L) == 1L) { + /* we are a "normal" :) machine */ + return(overlay->words[0]); + } + else { + return(overlay->words[1]); + } +} + +int +lo_32(big_int) + long long *big_int; +{ + union overlay_u { + long long dword; + long words[2]; + } *overlay; + + overlay = (union overlay_u *)big_int; + /* on those systems which are byte swapped, we really wish to */ + /* return words[0] - at least I think so - raj 4/95 */ + if (htonl(1L) == 1L) { + /* we are a "normal" :) machine */ + return(overlay->words[1]); + } + else { + return(overlay->words[0]); + } +} + +#endif /* USE_PSTAT || USE_SYSCTL */ + + +void libmain() +{ +fprintf(where,"hello world\n"); +fprintf(where,"debug: %d\n",debug); +} + + +void +set_sock_buffer (SOCKET sd, enum sock_buffer which, int requested_size, int *effective_sizep) +{ +#ifdef SO_SNDBUF + int optname = (which == SEND_BUFFER) ? SO_SNDBUF : SO_RCVBUF; + netperf_socklen_t sock_opt_len; + + /* seems that under Windows, setting a value of zero is how one + tells the stack you wish to enable copy-avoidance. Knuth only + knows what it will do on other stacks, but it might be + interesting to find-out, so we won't bother #ifdef'ing the change + to allow asking for 0 bytes. Courtesy of SAF, 2007-05 raj + 2007-05-31 */ + if (requested_size >= 0) { + if (setsockopt(sd, SOL_SOCKET, optname, + (char *)&requested_size, sizeof(int)) < 0) { + fprintf(where, "netperf: set_sock_buffer: %s option: errno %d\n", + (which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF", + errno); + fflush(where); + exit(1); + } + if (debug > 1) { + fprintf(where, "netperf: set_sock_buffer: %s of %d requested.\n", + (which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF", + requested_size); + fflush(where); + } + } + + /* Now, we will find-out what the size actually became, and report */ + /* that back to the user. If the call fails, we will just report a -1 */ + /* back to the initiator for the recv buffer size. */ + + sock_opt_len = sizeof(netperf_socklen_t); + if (getsockopt(sd, SOL_SOCKET, optname, (char *)effective_sizep, + &sock_opt_len) < 0) { + fprintf(where, "netperf: set_sock_buffer: getsockopt %s: errno %d\n", + (which == SEND_BUFFER) ? "SO_SNDBUF" : "SO_RCVBUF", errno); + fflush(where); + *effective_sizep = -1; + } + + if (debug) { + fprintf(where, "netperf: set_sock_buffer: " + "%s socket size determined to be %d\n", + (which == SEND_BUFFER) ? "send" : "receive", *effective_sizep); + fflush(where); + } +#else /* SO_SNDBUF */ + *effective_size = -1; +#endif /* SO_SNDBUF */ +} + +void +dump_addrinfo(FILE *dumploc, struct addrinfo *info, + char *host, char *port, int family) +{ + struct sockaddr *ai_addr; + struct addrinfo *temp; + temp=info; + + fprintf(dumploc, "getaddrinfo returned the following for host '%s' ", host); + fprintf(dumploc, "port '%s' ", port); + fprintf(dumploc, "family %s\n", inet_ftos(family)); + while (temp) { + /* seems that Solaris 10 GA bits will not give a canonical name + for ::0 or 0.0.0.0, and their fprintf() cannot deal with a null + pointer, so we have to check for a null pointer. probably a + safe thing to do anyway, eventhough it was not necessary on + linux or hp-ux. raj 2005-02-09 */ + if (temp->ai_canonname) { + fprintf(dumploc, + "\tcannonical name: '%s'\n",temp->ai_canonname); + } + else { + fprintf(dumploc, + "\tcannonical name: '%s'\n","(nil)"); + } + fprintf(dumploc, + "\tflags: %x family: %s: socktype: %s protocol %s addrlen %d\n", + temp->ai_flags, + inet_ftos(temp->ai_family), + inet_ttos(temp->ai_socktype), + inet_ptos(temp->ai_protocol), + temp->ai_addrlen); + ai_addr = temp->ai_addr; + if (ai_addr != NULL) { + fprintf(dumploc, + "\tsa_family: %s sadata: %d %d %d %d %d %d\n", + inet_ftos(ai_addr->sa_family), + (u_char)ai_addr->sa_data[0], + (u_char)ai_addr->sa_data[1], + (u_char)ai_addr->sa_data[2], + (u_char)ai_addr->sa_data[3], + (u_char)ai_addr->sa_data[4], + (u_char)ai_addr->sa_data[5]); + } + temp = temp->ai_next; + } + fflush(dumploc); +} + +/* + establish_control() + +set-up the control connection between netperf and the netserver so we +can actually run some tests. if we cannot establish the control +connection, that may or may not be a good thing, so we will let the +caller decide what to do. + +to assist with pesky end-to-end-unfriendly things like firewalls, we +allow the caller to specify both the remote hostname and port, and the +local addressing info. i believe that in theory it is possible to +have an IPv4 endpoint and an IPv6 endpoint communicate with one +another, but for the time being, we are only going to take-in one +requested address family parameter. this means that the only way +(iirc) that we might get a mixed-mode connection would be if the +address family is specified as AF_UNSPEC, and getaddrinfo() returns +different families for the local and server names. + +the "names" can also be IP addresses in ASCII string form. + +raj 2003-02-27 */ + +SOCKET +establish_control_internal(char *hostname, + char *port, + int remfam, + char *localhost, + char *localport, + int locfam) +{ + int not_connected; + SOCKET control_sock; + int count; + int error; + + struct addrinfo hints; + struct addrinfo *local_res; + struct addrinfo *remote_res; + struct addrinfo *local_res_temp; + struct addrinfo *remote_res_temp; + + if (debug) { + fprintf(where, + "establish_control called with host '%s' port '%s' remfam %s\n", + hostname, + port, + inet_ftos(remfam)); + fprintf(where, + "\t\tlocal '%s' port '%s' locfam %s\n", + localhost, + localport, + inet_ftos(locfam)); + fflush(where); + } + + /* first, we do the remote */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = remfam; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = 0|AI_CANONNAME; + count = 0; + do { + error = getaddrinfo((char *)hostname, + (char *)port, + &hints, + &remote_res); + count += 1; + if (error == EAI_AGAIN) { + if (debug) { + fprintf(where,"Sleeping on getaddrinfo EAI_AGAIN\n"); + fflush(where); + } + sleep(1); + } + } while ((error == EAI_AGAIN) && (count <= 5)); + + if (error) { + printf("establish control: could not resolve remote '%s' port '%s' af %s", + hostname, + port, + inet_ftos(remfam)); + printf("\n\tgetaddrinfo returned %d %s\n", + error, + gai_strerror(error)); + return(INVALID_SOCKET); + } + + if (debug) { + dump_addrinfo(where, remote_res, hostname, port, remfam); + } + + /* now we do the local */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = locfam; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE|AI_CANONNAME; + count = 0; + do { + count += 1; + error = getaddrinfo((char *)localhost, + (char *)localport, + &hints, + &local_res); + if (error == EAI_AGAIN) { + if (debug) { + fprintf(where, + "Sleeping on getaddrinfo(%s,%s) EAI_AGAIN count %d \n", + localhost, + localport, + count); + fflush(where); + } + sleep(1); + } + } while ((error == EAI_AGAIN) && (count <= 5)); + + if (error) { + printf("establish control: could not resolve local '%s' port '%s' af %s", + localhost, + localport, + inet_ftos(locfam)); + printf("\n\tgetaddrinfo returned %d %s\n", + error, + gai_strerror(error)); + return(INVALID_SOCKET); + } + + if (debug) { + dump_addrinfo(where, local_res, localhost, localport, locfam); + } + + not_connected = 1; + local_res_temp = local_res; + remote_res_temp = remote_res; + /* we want to loop through all the possibilities. looping on the + local addresses will be handled within the while loop. I suppose + these is some more "C-expert" way to code this, but it has not + lept to mind just yet :) raj 2003-02024 */ + + while (remote_res_temp != NULL) { + + /* I am guessing that we should use the address family of the + local endpoint, and we will not worry about mixed family types + - presumeably the stack or other transition mechanisms will be + able to deal with that for us. famous last words :) raj 2003-02-26 */ + control_sock = socket(local_res_temp->ai_family, + SOCK_STREAM, + 0); + if (control_sock == INVALID_SOCKET) { + /* at some point we'll need a more generic "display error" + message for when/if we use GUIs and the like. unlike a bind + or connect failure, failure to allocate a socket is + "immediately fatal" and so we return to the caller. raj 2003-02-24 */ + if (debug) { + perror("establish_control: unable to allocate control socket"); + } + return(INVALID_SOCKET); + } + + /* if we are going to control the local enpoint addressing, we + need to call bind. of course, we should probably be setting one + of the SO_REUSEmumble socket options? raj 2005-02-04 */ + if (bind(control_sock, + local_res_temp->ai_addr, + local_res_temp->ai_addrlen) == 0) { + if (debug) { + fprintf(where, + "bound control socket to %s and %s\n", + localhost, + localport); + } + + if (connect(control_sock, + remote_res_temp->ai_addr, + remote_res_temp->ai_addrlen) == 0) { + /* we have successfully connected to the remote netserver */ + if (debug) { + fprintf(where, + "successful connection to remote netserver at %s and %s\n", + hostname, + port); + } + not_connected = 0; + /* this should get us out of the while loop */ + break; + } else { + /* the connect call failed */ + if (debug) { + fprintf(where, + "establish_control: connect failed, errno %d %s\n", + errno, + strerror(errno)); + fprintf(where, " trying next address combination\n"); + fflush(where); + } + } + } + else { + /* the bind failed */ + if (debug) { + fprintf(where, + "establish_control: bind failed, errno %d %s\n", + errno, + strerror(errno)); + fprintf(where, " trying next address combination\n"); + fflush(where); + } + } + + if ((local_res_temp = local_res_temp->ai_next) == NULL) { + /* wrap the local and move to the next server, don't forget to + close the current control socket. raj 2003-02-24 */ + local_res_temp = local_res; + /* the outer while conditions will deal with the case when we + get to the end of all the possible remote addresses. */ + remote_res_temp = remote_res_temp->ai_next; + /* it is simplest here to just close the control sock. since + this is not a performance critical section of code, we + don't worry about overheads for socket allocation or + close. raj 2003-02-24 */ + } + close(control_sock); + } + + /* we no longer need the addrinfo stuff */ + freeaddrinfo(local_res); + freeaddrinfo(remote_res); + + /* so, we are either connected or not */ + if (not_connected) { + fprintf(where, "establish control: are you sure there is a netserver listening on %s at port %s?\n",hostname,port); + fflush(where); + return(INVALID_SOCKET); + } + /* at this point, we are connected. we probably want some sort of + version check with the remote at some point. raj 2003-02-24 */ + return(control_sock); +} + +void +establish_control(char *hostname, + char *port, + int remfam, + char *localhost, + char *localport, + int locfam) + +{ + + netlib_control = establish_control_internal(hostname, + port, + remfam, + localhost, + localport, + locfam); + if (netlib_control == INVALID_SOCKET) { + fprintf(where, + "establish_control could not establish the control connection from %s port %s address family %s to %s port %s address family %s\n", + localhost,localport,inet_ftos(locfam), + hostname,port,inet_ftos(remfam)); + fflush(where); + exit(INVALID_SOCKET); + } +} + + + + + /***********************************************************************/ + /* */ + /* get_id() */ + /* */ + /* Return a string to the calling routine that contains the */ + /* identifying information for the host we are running on. This */ + /* information will then either be displayed locally, or returned to */ + /* a remote caller for display there. */ + /* */ + /***********************************************************************/ + +char * +get_id() +{ + static char id_string[80]; +#ifdef WIN32 +char system_name[MAX_COMPUTERNAME_LENGTH+1] ; +DWORD name_len = MAX_COMPUTERNAME_LENGTH + 1 ; +#else +struct utsname system_name; +#endif /* WIN32 */ + +#ifdef WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo( &SystemInfo ) ; + if ( !GetComputerName(system_name , &name_len) ) + strcpy(system_name , "no_name") ; +#else + if (uname(&system_name) <0) { + perror("identify_local: uname"); + exit(1); + } +#endif /* WIN32 */ + + snprintf(id_string, sizeof(id_string), +#ifdef WIN32 + "%-15s%-15s%d.%d%d", + "Windows NT", + system_name , + GetVersion() & 0xFF , + GetVersion() & 0xFF00 , + SystemInfo.dwProcessorType + +#else + "%-15s%-15s%-15s%-15s%-15s", + system_name.sysname, + system_name.nodename, + system_name.release, + system_name.version, + system_name.machine +#endif /* WIN32 */ + ); + return (id_string); +} + + + /***********************************************************************/ + /* */ + /* identify_local() */ + /* */ + /* Display identifying information about the local host to the user. */ + /* At first release, this information will be the same as that which */ + /* is returned by the uname -a command, with the exception of the */ + /* idnumber field, which seems to be a non-POSIX item, and hence */ + /* non-portable. */ + /* */ + /***********************************************************************/ + +void +identify_local() +{ + +char *local_id; + +local_id = get_id(); + +fprintf(where,"Local Information \n\ +Sysname Nodename Release Version Machine\n"); + +fprintf(where,"%s\n", + local_id); + +} + + + /***********************************************************************/ + /* */ + /* identify_remote() */ + /* */ + /* Display identifying information about the remote host to the user. */ + /* At first release, this information will be the same as that which */ + /* is returned by the uname -a command, with the exception of the */ + /* idnumber field, which seems to be a non-POSIX item, and hence */ + /* non-portable. A request is sent to the remote side, which will */ + /* return a string containing the utsname information in a */ + /* pre-formatted form, which is then displayed after the header. */ + /* */ + /***********************************************************************/ + +void +identify_remote() +{ + +char *remote_id=""; + +/* send a request for node info to the remote */ +netperf_request.content.request_type = NODE_IDENTIFY; + +send_request(); + +/* and now wait for the reply to come back */ + +recv_response(); + +if (netperf_response.content.serv_errno) { + Set_errno(netperf_response.content.serv_errno); + perror("identify_remote: on remote"); + exit(1); +} + +fprintf(where,"Remote Information \n\ +Sysname Nodename Release Version Machine\n"); + +fprintf(where,"%s", + remote_id); +} + +void +cpu_start(int measure_cpu) +{ + + gettimeofday(&time1, + &tz); + + if (measure_cpu) { + cpu_util_init(); + measuring_cpu = 1; + cpu_method = get_cpu_method(); + cpu_start_internal(); + } +} + + +void +cpu_stop(int measure_cpu, float *elapsed) + +{ + + int sec, + usec; + + if (measure_cpu) { + cpu_stop_internal(); + cpu_util_terminate(); + } + + gettimeofday(&time2, + &tz); + + if (time2.tv_usec < time1.tv_usec) { + time2.tv_usec += 1000000; + time2.tv_sec -= 1; + } + + sec = time2.tv_sec - time1.tv_sec; + usec = time2.tv_usec - time1.tv_usec; + lib_elapsed = (float)sec + ((float)usec/(float)1000000.0); + + *elapsed = lib_elapsed; + +} + + +double +calc_thruput_interval(double units_received,double elapsed) + +{ + double divisor; + + /* We will calculate the thruput in libfmt units/second */ + switch (libfmt) { + case 'K': + divisor = 1024.0; + break; + case 'M': + divisor = 1024.0 * 1024.0; + break; + case 'G': + divisor = 1024.0 * 1024.0 * 1024.0; + break; + case 'k': + divisor = 1000.0 / 8.0; + break; + case 'm': + divisor = 1000.0 * 1000.0 / 8.0; + break; + case 'g': + divisor = 1000.0 * 1000.0 * 1000.0 / 8.0; + break; + + default: + divisor = 1024.0; + } + + return (units_received / divisor / elapsed); + +} + +double +calc_thruput(double units_received) + +{ + return(calc_thruput_interval(units_received,lib_elapsed)); +} + +/* these "_omni" versions are ones which understand 'x' as a unit, + meaning transactions/s. we have a separate routine rather than + convert the existing routine so we don't have to go and change + _all_ the nettest_foo.c files at one time. raj 2007-06-08 */ + +double +calc_thruput_interval_omni(double units_received,double elapsed) + +{ + double divisor; + + /* We will calculate the thruput in libfmt units/second */ + switch (libfmt) { + case 'K': + divisor = 1024.0; + break; + case 'M': + divisor = 1024.0 * 1024.0; + break; + case 'G': + divisor = 1024.0 * 1024.0 * 1024.0; + break; + case 'k': + divisor = 1000.0 / 8.0; + break; + case 'm': + divisor = 1000.0 * 1000.0 / 8.0; + break; + case 'g': + divisor = 1000.0 * 1000.0 * 1000.0 / 8.0; + break; + case 'x': + divisor = 1.0; + break; + + default: + fprintf(where, + "WARNING calc_throughput_internal_omni: unknown units %c\n", + libfmt); + fflush(where); + divisor = 1024.0; + } + + return (units_received / divisor / elapsed); + +} + +double +calc_thruput_omni(double units_received) + +{ + return(calc_thruput_interval_omni(units_received,lib_elapsed)); +} + + + + + +float +calc_cpu_util(float elapsed_time) +{ + return(calc_cpu_util_internal(elapsed_time)); +} + +float +calc_service_demand_internal(double unit_divisor, + double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus) + +{ + + double service_demand; + double thruput; + + if (debug) { + fprintf(where,"calc_service_demand called: units_sent = %f\n", + units_sent); + fprintf(where," elapsed_time = %f\n", + elapsed_time); + fprintf(where," cpu_util = %f\n", + cpu_utilization); + fprintf(where," num cpu = %d\n", + num_cpus); + fflush(where); + } + + if (num_cpus == 0) num_cpus = lib_num_loc_cpus; + + if (elapsed_time == 0.0) { + elapsed_time = lib_elapsed; + } + if (cpu_utilization == 0.0) { + cpu_utilization = lib_local_cpu_util; + } + + thruput = (units_sent / + (double) unit_divisor / + (double) elapsed_time); + + /* on MP systems, it is necessary to multiply the service demand by */ + /* the number of CPU's. at least, I believe that to be the case:) */ + /* raj 10/95 */ + + /* thruput has a "per second" component. if we were using 100% ( */ + /* 100.0) of the CPU in a second, that would be 1 second, or 1 */ + /* millisecond, so we multiply cpu_utilization by 10 to go to */ + /* milliseconds, or 10,000 to go to micro seconds. With revision */ + /* 2.1, the service demand measure goes to microseconds per unit. */ + /* raj 12/95 */ + service_demand = (cpu_utilization*10000.0/thruput) * + (float) num_cpus; + + if (debug) { + fprintf(where,"calc_service_demand using: units_sent = %f\n", + units_sent); + fprintf(where," elapsed_time = %f\n", + elapsed_time); + fprintf(where," cpu_util = %f\n", + cpu_utilization); + fprintf(where," num cpu = %d\n", + num_cpus); + fprintf(where,"calc_service_demand got: thruput = %f\n", + thruput); + fprintf(where," servdem = %f\n", + service_demand); + fflush(where); + } + return (float)service_demand; +} + +float calc_service_demand(double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus) + +{ + + double unit_divisor = (double)1024.0; + + return(calc_service_demand_internal(unit_divisor, + units_sent, + elapsed_time, + cpu_utilization, + num_cpus)); +} + +float calc_service_demand_trans(double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus) + +{ + + double unit_divisor = (double)1.0; + + return(calc_service_demand_internal(unit_divisor, + units_sent, + elapsed_time, + cpu_utilization, + num_cpus)); +} + + + +float +calibrate_local_cpu(float local_cpu_rate) +{ + + lib_num_loc_cpus = get_num_cpus(); + + lib_use_idle = 0; +#ifdef USE_LOOPER + cpu_util_init(); + lib_use_idle = 1; +#endif /* USE_LOOPER */ + + if (local_cpu_rate > 0) { + /* The user think that he knows what the cpu rate is. We assume */ + /* that all the processors of an MP system are essentially the */ + /* same - for this reason we do not have a per processor maxrate. */ + /* if the machine has processors which are different in */ + /* performance, the CPU utilization will be skewed. raj 4/95 */ + lib_local_maxrate = local_cpu_rate; + } + else { + /* if neither USE_LOOPER nor USE_PSTAT are defined, we return a */ + /* 0.0 to indicate that times or getrusage should be used. raj */ + /* 4/95 */ + lib_local_maxrate = (float)0.0; +#if defined(USE_PROC_STAT) || defined(USE_LOOPER) || defined(USE_PSTAT) || defined(USE_KSTAT) || defined(USE_PERFSTAT) || defined(USE_SYSCTL) + lib_local_maxrate = calibrate_idle_rate(4,10); +#endif + } + return lib_local_maxrate; +} + + +float +calibrate_remote_cpu() +{ + float remrate; + + netperf_request.content.request_type = CPU_CALIBRATE; + send_request(); + /* we know that calibration will last at least 40 seconds, so go to */ + /* sleep for that long so the 60 second select in recv_response will */ + /* not pop. raj 7/95 */ + + /* we know that CPU calibration may last as long as 40 seconds, so + make sure we "select" for at least that long while looking for + the response. raj 2005-05-16 */ + recv_response_timed(40); + + if (netperf_response.content.serv_errno) { + /* initially, silently ignore remote errors and pass */ + /* back a zero to the caller this should allow us to */ + /* mix rev 1.0 and rev 1.1 netperfs... */ + return((float)0.0); + } + else { + /* the rate is the first word of the test_specific data */ + bcopy((char *)netperf_response.content.test_specific_data, + (char *)&remrate, + sizeof(remrate)); + bcopy((char *)netperf_response.content.test_specific_data + sizeof(remrate), + (char *)&lib_num_rem_cpus, + sizeof(lib_num_rem_cpus)); +/* remrate = (float) netperf_response.content.test_specific_data[0]; */ + return(remrate); + } +} + +#ifndef WIN32 +/* WIN32 requires that at least one of the file sets to select be non-null. */ +/* Since msec_sleep routine is only called by nettest_dlpi & nettest_unix, */ +/* let's duck this issue. */ + +int +msec_sleep( int msecs ) +{ + int rval ; + + struct timeval timeout; + + timeout.tv_sec = msecs / 1000; + timeout.tv_usec = (msecs - (msecs/1000) *1000) * 1000; + if ((rval = select(0, + 0, + 0, + 0, + &timeout))) { + if ( SOCKET_EINTR(rval) ) { + return(1); + } + perror("msec_sleep: select"); + exit(1); + } + return(0); +} +#endif /* WIN32 */ + +#ifdef WANT_HISTOGRAM +/* hist.c + + Given a time difference in microseconds, increment one of 61 + different buckets: + + 0 - 9 in increments of 1 usec + 0 - 9 in increments of 10 usecs + 0 - 9 in increments of 100 usecs + 1 - 9 in increments of 1 msec + 1 - 9 in increments of 10 msecs + 1 - 9 in increments of 100 msecs + 1 - 9 in increments of 1 sec + 1 - 9 in increments of 10 sec + > 100 secs + + This will allow any time to be recorded to within an accuracy of + 10%, and provides a compact representation for capturing the + distribution of a large number of time differences (e.g. + request-response latencies). + + Colin Low 10/6/93 + Rick Jones 2004-06-15 extend to unit and ten usecs +*/ + +/* #include "sys.h" */ + +/*#define HIST_TEST*/ + +HIST +HIST_new(void){ + HIST h; + if((h = (HIST) malloc(sizeof(struct histogram_struct))) == NULL) { + perror("HIST_new - malloc failed"); + exit(1); + } + HIST_clear(h); + return h; +} + +void +HIST_clear(HIST h){ + int i; + for(i = 0; i < 10; i++){ + h->unit_usec[i] = 0; + h->ten_usec[i] = 0; + h->hundred_usec[i] = 0; + h->unit_msec[i] = 0; + h->ten_msec[i] = 0; + h->hundred_msec[i] = 0; + h->unit_sec[i] = 0; + h->ten_sec[i] = 0; + } + h->ridiculous = 0; + h->total = 0; +} + +void +HIST_add(register HIST h, int time_delta){ + register int val; + h->total++; + val = time_delta; + if(val <= 9) h->unit_usec[val]++; + else { + val = val/10; + if(val <= 9) h->ten_usec[val]++; + else { + val = val/10; + if(val <= 9) h->hundred_usec[val]++; + else { + val = val/10; + if(val <= 9) h->unit_msec[val]++; + else { + val = val/10; + if(val <= 9) h->ten_msec[val]++; + else { + val = val/10; + if(val <= 9) h->hundred_msec[val]++; + else { + val = val/10; + if(val <= 9) h->unit_sec[val]++; + else { + val = val/10; + if(val <= 9) h->ten_sec[val]++; + else h->ridiculous++; + } + } + } + } + } + } + } +} + +#define RB_printf printf + +void +output_row(FILE *fd, char *title, int *row){ + register int i; + RB_printf("%s", title); + for(i = 0; i < 10; i++) RB_printf(": %4d", row[i]); + RB_printf("\n"); +} + +int +sum_row(int *row) { + int sum = 0; + int i; + for (i = 0; i < 10; i++) sum += row[i]; + return(sum); +} + +void +HIST_report(HIST h){ +#ifndef OLD_HISTOGRAM + output_row(stdout, "UNIT_USEC ", h->unit_usec); + output_row(stdout, "TEN_USEC ", h->ten_usec); + output_row(stdout, "HUNDRED_USEC ", h->hundred_usec); +#else + h->hundred_usec[0] += sum_row(h->unit_usec); + h->hundred_usec[0] += sum_row(h->ten_usec); + output_row(stdout, "TENTH_MSEC ", h->hundred_usec); +#endif + output_row(stdout, "UNIT_MSEC ", h->unit_msec); + output_row(stdout, "TEN_MSEC ", h->ten_msec); + output_row(stdout, "HUNDRED_MSEC ", h->hundred_msec); + output_row(stdout, "UNIT_SEC ", h->unit_sec); + output_row(stdout, "TEN_SEC ", h->ten_sec); + RB_printf(">100_SECS: %d\n", h->ridiculous); + RB_printf("HIST_TOTAL: %d\n", h->total); +} + +#endif + +/* with the advent of sit-and-spin intervals support, we might as well + make these things available all the time, not just for demo or + histogram modes. raj 2006-02-06 */ +#ifdef HAVE_GETHRTIME + +void +HIST_timestamp(hrtime_t *timestamp) +{ + *timestamp = gethrtime(); +} + +int +delta_micro(hrtime_t *begin, hrtime_t *end) +{ + long nsecs; + nsecs = (*end) - (*begin); + return(nsecs/1000); +} + +#elif defined(HAVE_GET_HRT) +#include "hrt.h" + +void +HIST_timestamp(hrt_t *timestamp) +{ + *timestamp = get_hrt(); +} + +int +delta_micro(hrt_t *begin, hrt_t *end) +{ + + return((int)get_hrt_delta(*end,*begin)); + +} +#elif defined(WIN32) +void HIST_timestamp(LARGE_INTEGER *timestamp) +{ + QueryPerformanceCounter(timestamp); +} + +int delta_micro(LARGE_INTEGER *begin, LARGE_INTEGER *end) +{ + LARGE_INTEGER DeltaTimestamp; + static LARGE_INTEGER TickHz = {0,0}; + + if (TickHz.QuadPart == 0) + { + QueryPerformanceFrequency(&TickHz); + } + + /*+*+ Rick; this will overflow after ~2000 seconds, is that + good enough? Spencer: Yes, that should be more than good + enough for histogram support */ + + DeltaTimestamp.QuadPart = (end->QuadPart - begin->QuadPart) * + 1000000/TickHz.QuadPart; + assert((DeltaTimestamp.HighPart == 0) && + ((int)DeltaTimestamp.LowPart >= 0)); + + return (int)DeltaTimestamp.LowPart; +} + +#else + +void +HIST_timestamp(struct timeval *timestamp) +{ + gettimeofday(timestamp,NULL); +} + + /* return the difference (in micro seconds) between two timeval */ + /* timestamps */ +int +delta_micro(struct timeval *begin,struct timeval *end) + +{ + + int usecs, secs; + + if (end->tv_usec < begin->tv_usec) { + /* borrow a second from the tv_sec */ + end->tv_usec += 1000000; + end->tv_sec--; + } + usecs = end->tv_usec - begin->tv_usec; + secs = end->tv_sec - begin->tv_sec; + + usecs += (secs * 1000000); + + return(usecs); + +} +#endif /* HAVE_GETHRTIME */ + + +#ifdef WANT_DLPI + +int +put_control(fd, len, pri, ack) + int fd, len, pri, ack; +{ + int error; + int flags = 0; + dl_error_ack_t *err_ack = (dl_error_ack_t *)control_data; + + control_message.len = len; + + if ((error = putmsg(fd, &control_message, 0, pri)) < 0 ) { + fprintf(where,"put_control: putmsg error %d\n",error); + fflush(where); + return(-1); + } + if ((error = getmsg(fd, &control_message, 0, &flags)) < 0) { + fprintf(where,"put_control: getsmg error %d\n",error); + fflush(where); + return(-1); + } + if (err_ack->dl_primitive != ack) { + fprintf(where,"put_control: acknowledgement error wanted %u got %u \n", + ack,err_ack->dl_primitive); + if (err_ack->dl_primitive == DL_ERROR_ACK) { + fprintf(where," dl_error_primitive: %u\n", + err_ack->dl_error_primitive); + fprintf(where," dl_errno: %u\n", + err_ack->dl_errno); + fprintf(where," dl_unix_errno %u\n", + err_ack->dl_unix_errno); + } + fflush(where); + return(-1); + } + + return(0); +} + +int +dl_open(char devfile[], int ppa) +{ + int fd; + dl_attach_req_t *attach_req = (dl_attach_req_t *)control_data; + + if ((fd = open(devfile, O_RDWR)) == -1) { + fprintf(where,"netperf: dl_open: open of %s failed, errno = %d\n", + devfile, + errno); + return(-1); + } + + attach_req->dl_primitive = DL_ATTACH_REQ; + attach_req->dl_ppa = ppa; + + if (put_control(fd, sizeof(dl_attach_req_t), 0, DL_OK_ACK) < 0) { + fprintf(where, + "netperf: dl_open: could not send control message, errno = %d\n", + errno); + return(-1); + } + return(fd); +} + +int +dl_bind(int fd, int sap, int mode, char *dlsap_ptr, int *dlsap_len) +{ + dl_bind_req_t *bind_req = (dl_bind_req_t *)control_data; + dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)control_data; + + bind_req->dl_primitive = DL_BIND_REQ; + bind_req->dl_sap = sap; + bind_req->dl_max_conind = 1; + bind_req->dl_service_mode = mode; + bind_req->dl_conn_mgmt = 0; + bind_req->dl_xidtest_flg = 0; + + if (put_control(fd, sizeof(dl_bind_req_t), 0, DL_BIND_ACK) < 0) { + fprintf(where, + "netperf: dl_bind: could not send control message, errno = %d\n", + errno); + return(-1); + } + + /* at this point, the control_data portion of the control message */ + /* structure should contain a DL_BIND_ACK, which will have a full */ + /* DLSAP in it. we want to extract this and pass it up so that */ + /* it can be passed around. */ + if (*dlsap_len >= bind_ack->dl_addr_length) { + bcopy((char *)bind_ack+bind_ack->dl_addr_offset, + dlsap_ptr, + bind_ack->dl_addr_length); + *dlsap_len = bind_ack->dl_addr_length; + return(0); + } + else { + return (-1); + } +} + +int +dl_connect(int fd, unsigned char *remote_addr, int remote_addr_len) +{ + dl_connect_req_t *connection_req = (dl_connect_req_t *)control_data; + dl_connect_con_t *connection_con = (dl_connect_con_t *)control_data; + struct pollfd pinfo; + + int flags = 0; + + /* this is here on the off chance that we really want some data */ + u_long data_area[512]; + struct strbuf data_message; + + int error; + + data_message.maxlen = 2048; + data_message.len = 0; + data_message.buf = (char *)data_area; + + connection_req->dl_primitive = DL_CONNECT_REQ; + connection_req->dl_dest_addr_length = remote_addr_len; + connection_req->dl_dest_addr_offset = sizeof(dl_connect_req_t); + connection_req->dl_qos_length = 0; + connection_req->dl_qos_offset = 0; + bcopy (remote_addr, + (unsigned char *)control_data + sizeof(dl_connect_req_t), + remote_addr_len); + + /* well, I would call the put_control routine here, but the sequence */ + /* of connection stuff with DLPI is a bit screwey with all this */ + /* message passing - Toto, I don't think were in Berkeley anymore. */ + + control_message.len = sizeof(dl_connect_req_t) + remote_addr_len; + if ((error = putmsg(fd,&control_message,0,0)) !=0) { + fprintf(where,"dl_connect: putmsg failure, errno = %d, error 0x%x \n", + errno,error); + fflush(where); + return(-1); + }; + + pinfo.fd = fd; + pinfo.events = POLLIN | POLLPRI; + pinfo.revents = 0; + + if ((error = getmsg(fd,&control_message,&data_message,&flags)) != 0) { + fprintf(where,"dl_connect: getmsg failure, errno = %d, error 0x%x \n", + errno,error); + fflush(where); + return(-1); + } + while (control_data[0] == DL_TEST_CON) { + /* i suppose we spin until we get an error, or a connection */ + /* indication */ + if((error = getmsg(fd,&control_message,&data_message,&flags)) !=0) { + fprintf(where,"dl_connect: getmsg failure, errno = %d, error = 0x%x\n", + errno,error); + fflush(where); + return(-1); + } + } + + /* we are out - it either worked or it didn't - which was it? */ + if (control_data[0] == DL_CONNECT_CON) { + return(0); + } + else { + return(-1); + } +} + +int +dl_accept(fd, remote_addr, remote_addr_len) + int fd; + unsigned char *remote_addr; + int remote_addr_len; +{ + dl_connect_ind_t *connect_ind = (dl_connect_ind_t *)control_data; + dl_connect_res_t *connect_res = (dl_connect_res_t *)control_data; + int tmp_cor; + int flags = 0; + + /* hang around and wait for a connection request */ + getmsg(fd,&control_message,0,&flags); + while (control_data[0] != DL_CONNECT_IND) { + getmsg(fd,&control_message,0,&flags); + } + + /* now respond to the request. at some point, we may want to be sure */ + /* that the connection came from the correct station address, but */ + /* will assume that we do not have to worry about it just now. */ + + tmp_cor = connect_ind->dl_correlation; + + connect_res->dl_primitive = DL_CONNECT_RES; + connect_res->dl_correlation = tmp_cor; + connect_res->dl_resp_token = 0; + connect_res->dl_qos_length = 0; + connect_res->dl_qos_offset = 0; + connect_res->dl_growth = 0; + + return(put_control(fd, sizeof(dl_connect_res_t), 0, DL_OK_ACK)); + +} + +int +dl_set_window(fd, window) + int fd, window; +{ + return(0); +} + +void +dl_stats(fd) + int fd; +{ +} + +int +dl_send_disc(fd) + int fd; +{ +} + +int +dl_recv_disc(fd) + int fd; +{ +} +#endif /* WANT_DLPI*/ + + /* these routines for confidence intervals are courtesy of IBM. They */ + /* have been modified slightly for more general usage beyond TCP/UDP */ + /* tests. raj 11/94 I would suspect that this code carries an IBM */ + /* copyright that is much the same as that for the original HP */ + /* netperf code */ +int confidence_iterations; /* for iterations */ + +double + result_confid=-10.0, + loc_cpu_confid=-10.0, + rem_cpu_confid=-10.0, + + measured_sum_result=0.0, + measured_square_sum_result=0.0, + measured_mean_result=0.0, + measured_var_result=0.0, + + measured_sum_local_cpu=0.0, + measured_square_sum_local_cpu=0.0, + measured_mean_local_cpu=0.0, + measured_var_local_cpu=0.0, + + measured_sum_remote_cpu=0.0, + measured_square_sum_remote_cpu=0.0, + measured_mean_remote_cpu=0.0, + measured_var_remote_cpu=0.0, + + measured_sum_local_service_demand=0.0, + measured_square_sum_local_service_demand=0.0, + measured_mean_local_service_demand=0.0, + measured_var_local_service_demand=0.0, + + measured_sum_remote_service_demand=0.0, + measured_square_sum_remote_service_demand=0.0, + measured_mean_remote_service_demand=0.0, + measured_var_remote_service_demand=0.0, + + measured_sum_local_time=0.0, + measured_square_sum_local_time=0.0, + measured_mean_local_time=0.0, + measured_var_local_time=0.0, + + measured_mean_remote_time=0.0, + + measured_fails, + measured_local_results, + confidence=-10.0; +/* interval=0.1; */ + +/************************************************************************/ +/* */ +/* Constants for Confidence Intervals */ +/* */ +/************************************************************************/ +void +init_stat() +{ + measured_sum_result=0.0; + measured_square_sum_result=0.0; + measured_mean_result=0.0; + measured_var_result=0.0; + + measured_sum_local_cpu=0.0; + measured_square_sum_local_cpu=0.0; + measured_mean_local_cpu=0.0; + measured_var_local_cpu=0.0; + + measured_sum_remote_cpu=0.0; + measured_square_sum_remote_cpu=0.0; + measured_mean_remote_cpu=0.0; + measured_var_remote_cpu=0.0; + + measured_sum_local_service_demand=0.0; + measured_square_sum_local_service_demand=0.0; + measured_mean_local_service_demand=0.0; + measured_var_local_service_demand=0.0; + + measured_sum_remote_service_demand=0.0; + measured_square_sum_remote_service_demand=0.0; + measured_mean_remote_service_demand=0.0; + measured_var_remote_service_demand=0.0; + + measured_sum_local_time=0.0; + measured_square_sum_local_time=0.0; + measured_mean_local_time=0.0; + measured_var_local_time=0.0; + + measured_mean_remote_time=0.0; + + measured_fails = 0.0; + measured_local_results=0.0, + confidence=-10.0; +} + + /* this routine does a simple table lookup for some statistical */ + /* function that I would remember if I stayed awake in my probstats */ + /* class... raj 11/94 */ +double +confid(int level, int freedom) +{ +double t99[35],t95[35]; + + t95[1]=12.706; + t95[2]= 4.303; + t95[3]= 3.182; + t95[4]= 2.776; + t95[5]= 2.571; + t95[6]= 2.447; + t95[7]= 2.365; + t95[8]= 2.306; + t95[9]= 2.262; + t95[10]= 2.228; + t95[11]= 2.201; + t95[12]= 2.179; + t95[13]= 2.160; + t95[14]= 2.145; + t95[15]= 2.131; + t95[16]= 2.120; + t95[17]= 2.110; + t95[18]= 2.101; + t95[19]= 2.093; + t95[20]= 2.086; + t95[21]= 2.080; + t95[22]= 2.074; + t95[23]= 2.069; + t95[24]= 2.064; + t95[25]= 2.060; + t95[26]= 2.056; + t95[27]= 2.052; + t95[28]= 2.048; + t95[29]= 2.045; + t95[30]= 2.042; + + t99[1]=63.657; + t99[2]= 9.925; + t99[3]= 5.841; + t99[4]= 4.604; + t99[5]= 4.032; + t99[6]= 3.707; + t99[7]= 3.499; + t99[8]= 3.355; + t99[9]= 3.250; + t99[10]= 3.169; + t99[11]= 3.106; + t99[12]= 3.055; + t99[13]= 3.012; + t99[14]= 2.977; + t99[15]= 2.947; + t99[16]= 2.921; + t99[17]= 2.898; + t99[18]= 2.878; + t99[19]= 2.861; + t99[20]= 2.845; + t99[21]= 2.831; + t99[22]= 2.819; + t99[23]= 2.807; + t99[24]= 2.797; + t99[25]= 2.787; + t99[26]= 2.779; + t99[27]= 2.771; + t99[28]= 2.763; + t99[29]= 2.756; + t99[30]= 2.750; + + if(level==95){ + return(t95[freedom]); + } else if(level==99){ + return(t99[freedom]); + } else{ + return(0); + } +} + +void +calculate_confidence(int confidence_iterations, + float time, + double result, + float loc_cpu, + float rem_cpu, + float loc_sd, + float rem_sd) +{ + + if (debug) { + fprintf(where, + "calculate_confidence: itr %d; time %f; res %f\n", + confidence_iterations, + time, + result); + fprintf(where, + " lcpu %f; rcpu %f\n", + loc_cpu, + rem_cpu); + fprintf(where, + " lsdm %f; rsdm %f\n", + loc_sd, + rem_sd); + fflush(where); + } + + /* the test time */ + measured_sum_local_time += + (double) time; + measured_square_sum_local_time += + (double) time*time; + measured_mean_local_time = + (double) measured_sum_local_time/confidence_iterations; + measured_var_local_time = + (double) measured_square_sum_local_time/confidence_iterations + -measured_mean_local_time*measured_mean_local_time; + + /* the test result */ + measured_sum_result += + (double) result; + measured_square_sum_result += + (double) result*result; + measured_mean_result = + (double) measured_sum_result/confidence_iterations; + measured_var_result = + (double) measured_square_sum_result/confidence_iterations + -measured_mean_result*measured_mean_result; + + /* local cpu utilization */ + measured_sum_local_cpu += + (double) loc_cpu; + measured_square_sum_local_cpu += + (double) loc_cpu*loc_cpu; + measured_mean_local_cpu = + (double) measured_sum_local_cpu/confidence_iterations; + measured_var_local_cpu = + (double) measured_square_sum_local_cpu/confidence_iterations + -measured_mean_local_cpu*measured_mean_local_cpu; + + /* remote cpu util */ + measured_sum_remote_cpu += + (double) rem_cpu; + measured_square_sum_remote_cpu+= + (double) rem_cpu*rem_cpu; + measured_mean_remote_cpu = + (double) measured_sum_remote_cpu/confidence_iterations; + measured_var_remote_cpu = + (double) measured_square_sum_remote_cpu/confidence_iterations + -measured_mean_remote_cpu*measured_mean_remote_cpu; + + /* local service demand */ + measured_sum_local_service_demand += + (double) loc_sd; + measured_square_sum_local_service_demand+= + (double) loc_sd*loc_sd; + measured_mean_local_service_demand = + (double) measured_sum_local_service_demand/confidence_iterations; + measured_var_local_service_demand = + (double) measured_square_sum_local_service_demand/confidence_iterations + -measured_mean_local_service_demand*measured_mean_local_service_demand; + + /* remote service demand */ + measured_sum_remote_service_demand += + (double) rem_sd; + measured_square_sum_remote_service_demand+= + (double) rem_sd*rem_sd; + measured_mean_remote_service_demand = + (double) measured_sum_remote_service_demand/confidence_iterations; + measured_var_remote_service_demand = + (double) measured_square_sum_remote_service_demand/confidence_iterations + -measured_mean_remote_service_demand*measured_mean_remote_service_demand; + + if(confidence_iterations>1){ + result_confid= (double) interval - + 2.0 * confid(confidence_level,confidence_iterations-1)* + sqrt(measured_var_result/(confidence_iterations-1.0)) / + measured_mean_result; + + loc_cpu_confid= (double) interval - + 2.0 * confid(confidence_level,confidence_iterations-1)* + sqrt(measured_var_local_cpu/(confidence_iterations-1.0)) / + measured_mean_local_cpu; + + rem_cpu_confid= (double) interval - + 2.0 * confid(confidence_level,confidence_iterations-1)* + sqrt(measured_var_remote_cpu/(confidence_iterations-1.0)) / + measured_mean_remote_cpu; + + if(debug){ + printf("Conf_itvl %2d: results:%4.1f%% loc_cpu:%4.1f%% rem_cpu:%4.1f%%\n", + confidence_iterations, + (interval-result_confid)*100.0, + (interval-loc_cpu_confid)*100.0, + (interval-rem_cpu_confid)*100.0); + } + + /* if the user has requested that we only wait for the result to + be confident rather than the result and CPU util(s) then do + so. raj 2007-08-08 */ + if (!result_confidence_only) { + confidence = min(min(result_confid,loc_cpu_confid),rem_cpu_confid); + } + else { + confidence = result_confid; + } + } +} + + /* here ends the IBM code */ + +void +retrieve_confident_values(float *elapsed_time, + double *thruput, + float *local_cpu_utilization, + float *remote_cpu_utilization, + float *local_service_demand, + float *remote_service_demand) + +{ + *elapsed_time = (float)measured_mean_local_time; + *thruput = measured_mean_result; + *local_cpu_utilization = (float)measured_mean_local_cpu; + *remote_cpu_utilization = (float)measured_mean_remote_cpu; + *local_service_demand = (float)measured_mean_local_service_demand; + *remote_service_demand = (float)measured_mean_remote_service_demand; +} + + /* display_confidence() is called when we could not achieve the */ + /* desirec confidence in the results. it will print the achieved */ + /* confidence to "where" raj 11/94 */ +void +display_confidence() + +{ + fprintf(where, + "!!! WARNING\n"); + fprintf(where, + "!!! Desired confidence was not achieved within "); + fprintf(where, + "the specified iterations.\n"); + fprintf(where, + "!!! This implies that there was variability in "); + fprintf(where, + "the test environment that\n"); + fprintf(where, + "!!! must be investigated before going further.\n"); + fprintf(where, + "!!! Confidence intervals: Throughput : %4.1f%%\n", + 100.0 * (interval - result_confid)); + fprintf(where, + "!!! Local CPU util : %4.1f%%\n", + 100.0 * (interval - loc_cpu_confid)); + fprintf(where, + "!!! Remote CPU util : %4.1f%%\n\n", + 100.0 * (interval - rem_cpu_confid)); +} + |