diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:07 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:07 -0800 |
commit | 02fb0aca1189a2c1fd20806c588e9ee80d9755f8 (patch) | |
tree | 90ccbb66266424ee5bef1ad707610c05e33e1ea4 | |
parent | 8eb453f761a608a321986303fd8fefecb463fd6a (diff) | |
download | netperf-02fb0aca1189a2c1fd20806c588e9ee80d9755f8.tar.gz |
auto import from //depot/cupcake/@135843
-rw-r--r-- | Android.mk | 42 | ||||
-rw-r--r-- | MODULE_LICENSE_HP | 43 | ||||
-rw-r--r-- | config.h | 351 | ||||
-rw-r--r-- | hist.h | 116 | ||||
-rw-r--r-- | netcpu.h | 19 | ||||
-rw-r--r-- | netcpu_kstat.c | 415 | ||||
-rw-r--r-- | netcpu_kstat10.c | 559 | ||||
-rw-r--r-- | netcpu_looper.c | 656 | ||||
-rw-r--r-- | netcpu_none.c | 67 | ||||
-rw-r--r-- | netcpu_ntperf.c | 497 | ||||
-rw-r--r-- | netcpu_osx.c | 149 | ||||
-rw-r--r-- | netcpu_perfstat.c | 351 | ||||
-rw-r--r-- | netcpu_procstat.c | 265 | ||||
-rw-r--r-- | netcpu_pstat.c | 307 | ||||
-rw-r--r-- | netcpu_pstatnew.c | 398 | ||||
-rw-r--r-- | netcpu_sysctl.c | 127 | ||||
-rw-r--r-- | netlib.c | 4161 | ||||
-rw-r--r-- | netlib.h | 621 | ||||
-rw-r--r-- | netperf.c | 284 | ||||
-rw-r--r-- | netperf_version.h | 1 | ||||
-rw-r--r-- | netserver.c | 1022 | ||||
-rw-r--r-- | netsh.c | 1002 | ||||
-rw-r--r-- | netsh.h | 149 | ||||
-rw-r--r-- | nettest_bsd.c | 12333 | ||||
-rw-r--r-- | nettest_bsd.h | 471 | ||||
-rw-r--r-- | nettest_dlpi.c | 3798 | ||||
-rw-r--r-- | nettest_dlpi.h | 215 | ||||
-rw-r--r-- | nettest_sctp.c | 4869 | ||||
-rw-r--r-- | nettest_sctp.h | 128 | ||||
-rw-r--r-- | nettest_sdp.c | 3553 | ||||
-rw-r--r-- | nettest_sdp.h | 170 | ||||
-rw-r--r-- | nettest_unix.c | 3431 | ||||
-rw-r--r-- | nettest_unix.h | 198 | ||||
-rw-r--r-- | nettest_xti.c | 6026 | ||||
-rw-r--r-- | nettest_xti.h | 264 |
35 files changed, 47058 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..a0d5b80 --- /dev/null +++ b/Android.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +L_DEFS := -DHAVE_CONFIG_H -UAF_INET6 +L_CFLAGS := $(L_DEFS) +L_USE_CPU_SOURCE := netcpu_none.c + +L_COMMON_SRC := hist.h netlib.c netsh.c nettest_bsd.c nettest_dlpi.c \ + nettest_unix.c nettest_xti.c nettest_sctp.c nettest_sdp.c + +netperf_SOURCES := netperf.c $(L_COMMON_SRC) $(L_USE_CPU_SOURCE) +netserver_SOURCES := netserver.c $(L_COMMON_SRC) $(L_USE_CPU_SOURCE) + +include $(CLEAR_VARS) +LOCAL_MODULE := netperf +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := tests eng +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(netperf_SOURCES) +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := netserver +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := tests eng +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(netserver_SOURCES) +include $(BUILD_EXECUTABLE) + diff --git a/MODULE_LICENSE_HP b/MODULE_LICENSE_HP new file mode 100644 index 0000000..3f3ceb2 --- /dev/null +++ b/MODULE_LICENSE_HP @@ -0,0 +1,43 @@ + + + Copyright (C) 1993 Hewlett-Packard Company + ALL RIGHTS RESERVED. + + The enclosed software and documentation includes copyrighted works + of Hewlett-Packard Co. For as long as you comply with the following + limitations, you are hereby authorized to (i) use, reproduce, and + modify the software and documentation, and to (ii) distribute the + software and documentation, including modifications, for + non-commercial purposes only. + + 1. The enclosed software and documentation is made available at no + charge in order to advance the general development of + high-performance networking products. + + 2. You may not delete any copyright notices contained in the + software or documentation. All hard copies, and copies in + source code or object code form, of the software or + documentation (including modifications) must contain at least + one of the copyright notices. + + 3. The enclosed software and documentation has not been subjected + to testing and quality control and is not a Hewlett-Packard Co. + product. At a future time, Hewlett-Packard Co. may or may not + offer a version of the software and documentation as a product. + + 4. THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS". + HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE, + REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR + DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL + PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR + DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES, + EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE + DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + 5. HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY + DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES + (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION, + MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION. + + diff --git a/config.h b/config.h new file mode 100644 index 0000000..5c89615 --- /dev/null +++ b/config.h @@ -0,0 +1,351 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to one to enable dirty buffer support. May affect results. */ +/* #undef DIRTY */ + +#undef AF_INET6 + +/* Define to 1 if you have the `alarm' function. */ +#define HAVE_ALARM 1 + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `bindprocessor' function. */ +/* #undef HAVE_BINDPROCESSOR */ + +/* Define to 1 if you have the `bind_to_cpu_id' function. */ +/* #undef HAVE_BIND_TO_CPU_ID */ + +/* Define to 1 if you have the `bzero' function. */ +#define HAVE_BZERO 1 + +/* Define to 1 if you have the <endian.h> header file. */ +/* #define HAVE_ENDIAN_H 1 */ + +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `gethostbyname' function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethrtime' function. */ +/* #undef HAVE_GETHRTIME */ + +/* Define to 1 if you have the `getnameinfo' function. */ +#define HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to one to include ICSC-EXS tests. */ +/* #undef HAVE_ICSC_EXS */ + +/* Define to 1 if you have the `inet_ntoa' function. */ +#define HAVE_INET_NTOA 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `exs' library (-lexs). */ +/* #undef HAVE_LIBEXS */ + +/* Define to 1 if you have the `kstat' library (-lkstat). */ +/* #undef HAVE_LIBKSTAT */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `mach' library (-lmach). */ +/* #undef HAVE_LIBMACH */ + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define to 1 if you have the `perfstat' library (-lperfstat). */ +/* #undef HAVE_LIBPERFSTAT */ + +/* Define to 1 if you have the `sctp' library (-lsctp). */ +/* #undef HAVE_LIBSCTP */ + +/* Define to 1 if you have the `sdp' library (-lsdp). */ +/* #undef HAVE_LIBSDP */ + +/* Define to 1 if you have the `sendfile' library (-lsendfile). */ +/* #undef HAVE_LIBSENDFILE */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the <malloc.h> header file. */ +/* #define HAVE_MALLOC_H 1 */ + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the `mpctl' function. */ +/* #undef HAVE_MPCTL */ + +/* Define to 1 if you have the `munmap' function. */ +#define HAVE_MUNMAP 1 + +/* Define to 1 if you have the <netdb.h> header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the <netinet/sctp.h> header file. */ +/* #undef HAVE_NETINET_SCTP_H */ + +/* Define to 1 if you have the `processor_bind' function. */ +/* #undef HAVE_PROCESSOR_BIND */ + +/* Define to 1 if you have the `sched_setaffinity' function. */ +/* #define HAVE_SCHED_SETAFFINITY 1 */ + +/* Define to 1 if `struct sctp_event_subscribe' has a + `sctp_adaptation_layer_event' member */ +/* #undef HAVE_SCTP_ADAPTATION_LAYER_EVENT */ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `sendfile' function. */ +/* #undef HAVE_SENDFILE */ + +/* Define to 1 if you have the <signal.h> header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the `sqrt' function. */ +#define HAVE_SQRT 1 + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if <netinet/in.h> defines `struct sockaddr_storage' */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/select.h> header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the `uname' function. */ +#define HAVE_UNAME 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the <vfork.h> header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Define to 1 if `h_errno' is declared by <netdb.h> */ +#define H_ERRNO_DECLARED 1 + +/* Name of package */ +#define PACKAGE "netperf" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "netperf" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "netperf 2.4.4" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "netperf" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.4.4" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (fd_set *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* Define to 1 if the `setpgrp' function takes no argument. */ +#define SETPGRP_VOID 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 + +/* Use Solaris's kstat interface to measure CPU util. */ +/* #undef USE_KSTAT */ + +/* Use looper/soaker processes to measure CPU util. */ +/* #undef USE_LOOPER */ + +/* Use MacOS X's host_info interface to measure CPU util. */ +/* #undef USE_OSX */ + +/* Use AIX's perfstat interface to measure CPU util. */ +/* #undef USE_PERFSTAT */ + +/* Use Linux's procstat interface to measure CPU util. */ +#define USE_PROC_STAT 1 + +/* Use HP-UX's pstat interface to measure CPU util. */ +/* #undef USE_PSTAT */ + +/* Use FreeBSD's sysctl interface to measure CPU util. */ +/* #undef USE_SYSCTL */ + +/* Version number of package */ +#define VERSION "2.4.4" + +/* Define to one to enable demo support. May affect results. */ +/* #undef WANT_DEMO */ + +/* Define to one to include DLPI tests. */ +/* #undef WANT_DLPI */ + +/* Define to one to enable initial _RR burst support. May affect results. */ +/* #undef WANT_FIRST_BURST */ + +/* Define to one to enable histogram support. May affect results. */ +/* #undef WANT_HISTOGRAM */ + +/* Define to one to enable paced operation support. May affect results. */ +/* #undef WANT_INTERVALS */ + +/* Define to one to include SCTP tests. */ +/* #define WANT_SCTP 1 */ + +/* Define to one to include SDP tests. */ +/* #undef WANT_SDP */ + +/* Define to one to spin waiting on paced operation. WILL AFFEFCT CPU + UTILIZATION */ +/* #undef WANT_SPIN */ + +/* Define to one to include Unix Domain socket tests. */ +/* #undef WANT_UNIX */ + +/* Define to one to include XTI tests. */ +/* #undef WANT_XTI */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* type to use in place of socklen_t if not defined */ +#define netperf_socklen_t size_t + +/* Define to `long int' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef size_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ @@ -0,0 +1,116 @@ +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +/* hist.h + + 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 + 0 - 9 in increments of 1 msec + 0 - 9 in increments of 10 msecs + 0 - 9 in increments of 100 msecs + 0 - 9 in increments of 1 sec + 0 - 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 1 and 10 usec +*/ +#ifndef _HIST_INCLUDED +#define _HIST_INCLUDED + +#ifdef IRIX +#include <sys/time.h> +#endif /* IRIX */ + +#if defined(HAVE_GET_HRT) +#include "hrt.h" +#endif + +struct histogram_struct { + int unit_usec[10]; + int ten_usec[10]; + int hundred_usec[10]; + int unit_msec[10]; + int ten_msec[10]; + int hundred_msec[10]; + int unit_sec[10]; + int ten_sec[10]; + int ridiculous; + int total; +}; + +typedef struct histogram_struct *HIST; + +/* + HIST_new - return a new, cleared histogram data type +*/ + +HIST HIST_new(void); + +/* + HIST_clear - reset a histogram by clearing all totals to zero +*/ + +void HIST_clear(HIST h); + +/* + HIST_add - add a time difference to a histogram. Time should be in + microseconds. +*/ + +void HIST_add(register HIST h, int time_delta); + +/* + HIST_report - create an ASCII report on the contents of a histogram. + Currently printsto standard out +*/ + +void HIST_report(HIST h); + +/* + HIST_timestamp - take a timestamp suitable for use in a histogram. +*/ + +#ifdef HAVE_GETHRTIME +void HIST_timestamp(hrtime_t *timestamp); +#elif defined(HAVE_GET_HRT) +void HIST_timestamp(hrt_t *timestamp); +#elif defined(WIN32) +void HIST_timestamp(LARGE_INTEGER *timestamp); +#else +void HIST_timestamp(struct timeval *timestamp); +#endif + +/* + delta_micro - calculate the difference in microseconds between two + timestamps +*/ +#ifdef HAVE_GETHRTIME +int delta_micro(hrtime_t *begin, hrtime_t *end); +#elif defined(HAVE_GET_HRT) +int delta_micro(hrt_t *begin, hrt_t *end); +#elif defined(WIN32) +int delta_micro(LARGE_INTEGER *begin, LARGE_INTEGER *end); +#else +int delta_micro(struct timeval *begin, struct timeval *end); +#endif + +#endif + diff --git a/netcpu.h b/netcpu.h new file mode 100644 index 0000000..58d1e1c --- /dev/null +++ b/netcpu.h @@ -0,0 +1,19 @@ +/* This should define all the common routines etc exported by the + various netcpu_mumble.c files raj 2005-01-26 */ + +extern void cpu_util_init(void); +extern void cpu_util_terminate(void); +extern int get_cpu_method(); + +#ifdef WIN32 +/* +*+ temp until I figure out what header this is in; I know it's + there someplace... */ +typedef unsigned __int64 uint64_t; +#endif + +extern void get_cpu_idle(uint64_t *res); +extern float calibrate_idle_rate(int iterations, int interval); +extern float calc_cpu_util_internal(float elapsed); +extern void cpu_start_internal(void); +extern void cpu_stop_internal(void); + diff --git a/netcpu_kstat.c b/netcpu_kstat.c new file mode 100644 index 0000000..6320658 --- /dev/null +++ b/netcpu_kstat.c @@ -0,0 +1,415 @@ +char netcpu_kstat_id[]="\ +@(#)netcpu_kstat.c Version 2.4.0"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif + +#include <kstat.h> +#include <sys/sysinfo.h> + +#include "netsh.h" +#include "netlib.h" + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibrarion to arrive at a CPU utilization + percentage. raj 2005-01-26 */ +static uint64_t lib_start_count[MAXCPUS]; +static uint64_t lib_end_count[MAXCPUS]; + +static kstat_t *cpu_ks[MAXCPUS]; /* the addresses that kstat will + need to pull the cpu info from + the kstat interface. at least I + think that is what this is :) raj + 8/2000 */ + +#define UPDKCID(nk,ok) \ +if (nk == -1) { \ + perror("kstat_read "); \ + exit(1); \ +} \ +if (nk != ok)\ + goto kcid_changed; + +static kstat_ctl_t *kc = NULL; +static kid_t kcid = 0; + +/* do the initial open of the kstat interface, get the chain id's all + straightened-out and set-up the addresses for get_kstat_idle to do + its thing. liberally borrowed from the sources to TOP. raj 8/2000 */ + +static int +open_kstat() +{ + kstat_t *ks; + kid_t nkcid; + int i; + int changed = 0; + static int ncpu = 0; + + kstat_named_t *kn; + + if (debug) { + fprintf(where,"open_kstat: enter\n"); + fflush(where); + } + + /* + * 0. kstat_open + */ + + if (!kc) + { + kc = kstat_open(); + if (!kc) + { + perror("kstat_open "); + exit(1); + } + changed = 1; + kcid = kc->kc_chain_id; + } +#ifdef rickwasstupid + else { + fprintf(where,"open_kstat double open!\n"); + fflush(where); + exit(1); + } +#endif + + /* keep doing it until no more changes */ + kcid_changed: + + if (debug) { + fprintf(where,"passing kcid_changed\n"); + fflush(where); + } + + /* + * 1. kstat_chain_update + */ + nkcid = kstat_chain_update(kc); + if (nkcid) + { + /* UPDKCID will abort if nkcid is -1, so no need to check */ + changed = 1; + kcid = nkcid; + } + UPDKCID(nkcid,0); + + if (debug) { + fprintf(where,"kstat_lookup for unix/system_misc\n"); + fflush(where); + } + + ks = kstat_lookup(kc, "unix", 0, "system_misc"); + if (kstat_read(kc, ks, 0) == -1) { + perror("kstat_read"); + exit(1); + } + + + if (changed) { + + /* + * 2. get data addresses + */ + + ncpu = 0; + + kn = kstat_data_lookup(ks, "ncpus"); + if (kn && kn->value.ui32 > lib_num_loc_cpus) { + fprintf(stderr,"number of CPU's mismatch!"); + exit(1); + } + + for (ks = kc->kc_chain; ks; + ks = ks->ks_next) + { + if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) + { + nkcid = kstat_read(kc, ks, NULL); + /* if kcid changed, pointer might be invalid. we'll deal + wtih changes at this stage, but will not accept them + when we are actually in the middle of reading + values. hopefully this is not going to be a big + issue. raj 8/2000 */ + UPDKCID(nkcid, kcid); + + if (debug) { + fprintf(where,"cpu_ks[%d] getting %p\n",ncpu,ks); + fflush(where); + } + + cpu_ks[ncpu] = ks; + ncpu++; + if (ncpu > lib_num_loc_cpus) + { + /* with the check above, would we ever hit this? */ + fprintf(stderr, + "kstat finds too many cpus %d: should be %d\n", + ncpu,lib_num_loc_cpus); + exit(1); + } + } + } + /* note that ncpu could be less than ncpus, but that's okay */ + changed = 0; + } +} + +/* return the value of the idle tick counter for the specified CPU */ +static long +get_kstat_idle(cpu) + int cpu; +{ + cpu_stat_t cpu_stat; + kid_t nkcid; + + if (debug) { + fprintf(where, + "get_kstat_idle reading with kc %x and ks %p\n", + kc, + cpu_ks[cpu]); + } + + nkcid = kstat_read(kc, cpu_ks[cpu], &cpu_stat); + /* if kcid changed, pointer might be invalid, fail the test */ + UPDKCID(nkcid, kcid); + + return(cpu_stat.cpu_sysinfo.cpu[CPU_IDLE]); + + kcid_changed: + perror("kcid changed midstream and I cannot deal with that!"); + exit(1); +} + +void +cpu_util_init(void) +{ + open_kstat(); + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return KSTAT; +} + +void +get_cpu_idle(uint64_t *res) +{ + + int i; + + /* this open may be redundant */ + open_kstat(); + + for (i = 0; i < lib_num_loc_cpus; i++){ + res[i] = get_kstat_idle(i); + } + return; +} + +float +calibrate_idle_rate(int iterations, int interval) +{ + + long + firstcnt[MAXCPUS], + secondcnt[MAXCPUS]; + + float + elapsed, + temp_rate, + rate[MAXTIMES], + local_maxrate; + + long + sec, + usec; + + int + i, + j; + + struct timeval time1, time2 ; + struct timezone tz; + + if (debug) { + fprintf(where,"calling open_kstat from calibrate_kstat\n"); + fflush(where); + } + + open_kstat(); + + if (iterations > MAXTIMES) { + iterations = MAXTIMES; + } + + local_maxrate = (float)-1.0; + + for(i = 0; i < iterations; i++) { + rate[i] = (float)0.0; + for (j = 0; j < lib_num_loc_cpus; j++) { + firstcnt[j] = get_kstat_idle(j); + } + gettimeofday (&time1, &tz); + sleep(interval); + 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; + elapsed = (float)sec + ((float)usec/(float)1000000.0); + + if(debug) { + fprintf(where, "Calibration for kstat counter run: %d\n",i); + fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec); + fprintf(where,"\telapsed time = %g\n",elapsed); + } + + for (j = 0; j < lib_num_loc_cpus; j++) { + secondcnt[j] = get_kstat_idle(j); + if(debug) { + /* I know that there are situations where compilers know about */ + /* long long, but the library functions do not... raj 4/95 */ + fprintf(where, + "\tfirstcnt[%d] = 0x%8.8lx%8.8lx secondcnt[%d] = 0x%8.8lx%8.8lx\n", + j, + firstcnt[j], + firstcnt[j], + j, + secondcnt[j], + secondcnt[j]); + } + /* we assume that it would wrap no more than once. we also */ + /* assume that the result of subtracting will "fit" raj 4/95 */ + temp_rate = (secondcnt[j] >= firstcnt[j]) ? + (float)(secondcnt[j] - firstcnt[j])/elapsed : + (float)(secondcnt[j]-firstcnt[j]+MAXLONG)/elapsed; + if (temp_rate > rate[i]) rate[i] = temp_rate; + if(debug) { + fprintf(where,"\trate[%d] = %g\n",i,rate[i]); + fflush(where); + } + if (local_maxrate < rate[i]) local_maxrate = rate[i]; + } + } + if(debug) { + fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate); + fflush(where); + } + return local_maxrate; +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + float correction_factor; + float actual_rate; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* it would appear that on some systems, in loopback, nice is + *very* effective, causing the looper process to stop dead in its + tracks. if this happens, we need to ensure that the calculation + does not go south. raj 6/95 and if we run completely out of idle, + the same thing could in theory happen to the USE_KSTAT path. raj + 8/2000 */ + + if (lib_end_count[i] == lib_start_count[i]) { + lib_end_count[i]++; + } + + actual_rate = (lib_end_count[i] > lib_start_count[i]) ? + (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed : + (float)(lib_end_count[i] - lib_start_count[i] + + MAXLONG)/ lib_elapsed; + if (debug) { + fprintf(where, + "calc_cpu_util: actual_rate on processor %d is %f start %lx end %lx\n", + i, + actual_rate, + lib_start_count[i], + lib_end_count[i]); + } + lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / + lib_local_maxrate * 100; + lib_local_cpu_util += lib_local_per_cpu_util[i]; + } + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + lib_local_cpu_util *= correction_factor; + return lib_local_cpu_util; + + +} + +void +cpu_start_internal(void) +{ + get_cpu_idle(lib_start_count); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_idle(lib_end_count); +} diff --git a/netcpu_kstat10.c b/netcpu_kstat10.c new file mode 100644 index 0000000..299e66d --- /dev/null +++ b/netcpu_kstat10.c @@ -0,0 +1,559 @@ +char netcpu_kstat10_id[]="\ +@(#)netcpu_kstat10.c (c) Copyright 2005-2007, Hewlett-Packard Company Version 2.4.3"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif + +#include <errno.h> + +#include <kstat.h> +#include <sys/sysinfo.h> + +#include "netsh.h" +#include "netlib.h" + +static kstat_ctl_t *kc = NULL; +static kid_t kcid = 0; + +typedef struct cpu_time_counters { + uint64_t idle; + uint64_t user; + uint64_t kernel; + uint64_t interrupt; +} cpu_time_counters_t; + +static cpu_time_counters_t starting_cpu_counters[MAXCPUS]; +static cpu_time_counters_t ending_cpu_counters[MAXCPUS]; +static cpu_time_counters_t delta_cpu_counters[MAXCPUS]; +static cpu_time_counters_t corrected_cpu_counters[MAXCPUS]; + +static void +print_cpu_time_counters(char *name, int instance, cpu_time_counters_t *counters) +{ + fprintf(where,"%s[%d]:\n",name,instance); + fprintf(where, + "\t idle %llu\n",counters[instance].idle); + fprintf(where, + "\t user %llu\n",counters[instance].user); + fprintf(where, + "\t kernel %llu\n",counters[instance].kernel); + fprintf(where, + "\t interrupt %llu\n",counters[instance].interrupt); +} + +void +cpu_util_init(void) +{ + kc = kstat_open(); + + if (kc == NULL) { + fprintf(where, + "cpu_util_init: kstat_open: errno %d %s\n", + errno, + strerror(errno)); + fflush(where); + exit(-1); + } + return; +} + +void +cpu_util_terminate(void) +{ + kstat_close(kc); + return; +} + +int +get_cpu_method(void) +{ + return KSTAT_10; +} + +static void +print_unexpected_statistic_warning(char *who, char *what, char *why) +{ + if (why) { + fprintf(where, + "WARNING! WARNING! WARNING! WARNING!\n"); + fprintf(where, + "%s found an unexpected %s statistic %.16s\n", + who, + why, + what); + } + else { + fprintf(where, + "%s is ignoring statistic %.16s\n", + who, + what); + } +} + +static void +get_cpu_counters(int cpu_num, cpu_time_counters_t *counters) +{ + + kstat_t *ksp; + int found=0; + kid_t nkcid; + kstat_named_t *knp; + int i; + + ksp = kstat_lookup(kc, "cpu", cpu_num, "sys"); + if ((ksp) && (ksp->ks_type == KSTAT_TYPE_NAMED)) { + /* happiness and joy, keep going */ + nkcid = kstat_read(kc, ksp, NULL); + if (nkcid != -1) { + /* happiness and joy, keep going. we could consider adding a + "found < 3" to the end conditions, but then we wouldn't + search to the end and find that Sun added some nsec. we + probably want to see if they add an nsec. raj 2005-01-28 */ + for (i = ksp->ks_ndata, knp = ksp->ks_data; + i > 0; + knp++,i--) { + /* we would be hosed if the same name could appear twice */ + if (!strcmp("cpu_nsec_idle",knp->name)) { + found++; + counters[cpu_num].idle = knp->value.ui64; + } + else if (!strcmp("cpu_nsec_user",knp->name)) { + found++; + counters[cpu_num].user = knp->value.ui64; + } + else if (!strcmp("cpu_nsec_kernel",knp->name)) { + found++; + counters[cpu_num].kernel = knp->value.ui64; + } + else if (strstr(knp->name,"nsec")) { + /* finding another nsec here means Sun have changed + something and we need to warn the user. raj 2005-01-28 */ + print_unexpected_statistic_warning("get_cpu_counters", + knp->name, + "nsec"); + } + else if (debug >=2) { + + /* might want to tell people about what we are skipping. + however, only display other names debug >=2. raj + 2005-01-28 + */ + + print_unexpected_statistic_warning("get_cpu_counters", + knp->name, + NULL); + } + } + if (3 == found) { + /* happiness and joy */ + return; + } + else { + fprintf(where, + "get_cpu_counters could not find one or more of the expected counters!\n"); + fflush(where); + exit(-1); + } + } + else { + /* the kstat_read returned an error or the chain changed */ + fprintf(where, + "get_cpu_counters: kstat_read failed or chain id changed %d %s\n", + errno, + strerror(errno)); + fflush(where); + exit(-1); + } + } + else { + /* the lookup failed or found the wrong type */ + fprintf(where, + "get_cpu_counters: kstat_lookup failed for module 'cpu' instance %d name 'sys' and KSTAT_TYPE_NAMED: errno %d %s\n", + cpu_num, + errno, + strerror(errno)); + fflush(where); + exit(-1); + } +} + +static void +get_interrupt_counters(int cpu_num, cpu_time_counters_t *counters) +{ + kstat_t *ksp; + int found=0; + kid_t nkcid; + kstat_named_t *knp; + int i; + + ksp = kstat_lookup(kc, "cpu", cpu_num, "intrstat"); + + counters[cpu_num].interrupt = 0; + if ((ksp) && (ksp->ks_type == KSTAT_TYPE_NAMED)) { + /* happiness and joy, keep going */ + nkcid = kstat_read(kc, ksp, NULL); + if (nkcid != -1) { + /* happiness and joy, keep going. we could consider adding a + "found < 15" to the end conditions, but then we wouldn't + search to the end and find that Sun added some "time." we + probably want to see if they add a "nsec." raj 2005-01-28 */ + for (i = ksp->ks_ndata, knp = ksp->ks_data; + i > 0; + knp++,i--) { + if (strstr(knp->name,"time")) { + found++; + counters[cpu_num].interrupt += knp->value.ui64; + } + else if (debug >=2) { + + /* might want to tell people about what we are skipping. + however, only display other names debug >=2. raj + 2005-01-28 + */ + + print_unexpected_statistic_warning("get_cpu_counters", + knp->name, + NULL); + } + } + if (15 == found) { + /* happiness and joy */ + return; + } + else { + fprintf(where, + "get_cpu_counters could not find one or more of the expected counters!\n"); + fflush(where); + exit(-1); + } + } + else { + /* the kstat_read returned an error or the chain changed */ + fprintf(where, + "get_cpu_counters: kstat_read failed or chain id changed %d %s\n", + errno, + strerror(errno)); + fflush(where); + exit(-1); + } + } + else { + /* the lookup failed or found the wrong type */ + fprintf(where, + "get_cpu_counters: kstat_lookup failed for module 'cpu' instance %d class 'intrstat' and KSTAT_TYPE_NAMED: errno %d %s\n", + cpu_num, + errno, + strerror(errno)); + fflush(where); + exit(-1); + } + +} + +static void +get_cpu_time_counters(cpu_time_counters_t *counters) +{ + + int i; + + for (i = 0; i < lib_num_loc_cpus; i++){ + get_cpu_counters(i, counters); + get_interrupt_counters(i, counters); + } + + return; +} + +/* the kstat10 mechanism, since it is based on actual nanosecond + counters is not going to use a comparison to an idle rate. so, the + calibrate_idle_rate routine will be rather simple :) raj 2005-01-28 + */ + +float +calibrate_idle_rate(int iterations, int interval) +{ + return 0.0; +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + float correction_factor; + float actual_rate; + + uint64_t total_cpu_nsec; + + /* multiply by 100 and divide by total and you get whole + percentages. multiply by 1000 and divide by total and you get + tenths of percentages. multiply by 10000 and divide by total and + you get hundredths of percentages. etc etc etc raj 2005-01-28 */ + +#define CALC_PERCENT 100 +#define CALC_TENTH_PERCENT 1000 +#define CALC_HUNDREDTH_PERCENT 10000 +#define CALC_THOUSANDTH_PERCENT 100000 +#define CALC_ACCURACY CALC_THOUSANDTH_PERCENT + + uint64_t fraction_idle; + uint64_t fraction_user; + uint64_t fraction_kernel; + uint64_t fraction_interrupt; + + uint64_t interrupt_idle; + uint64_t interrupt_user; + uint64_t interrupt_kernel; + + lib_local_cpu_util = (float)0.0; + + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* this is now the fun part. we have the nanoseconds _allegedly_ + spent in user, idle and kernel. We also have nanoseconds spent + servicing interrupts. Sadly, in the developer's finite wisdom, + the interrupt time accounting is in parallel with the other + accounting. this means that time accounted in user, kernel or + idle will also include time spent in interrupt. for netperf's + porpoises we do not really care about that for user and kernel, + but we certainly do care for idle. the $64B question becomes - + how to "correct" for this? + + we could just subtract interrupt time from idle. that has the + virtue of simplicity and also "punishes" Sun for doing + something that seems to be so stupid. however, we probably + have to be "fair" even to the allegedly stupid so the other + mechanism, suggested by a Sun engineer is to subtract interrupt + time from each of user, kernel and idle in proportion to their + numbers. then we sum the corrected user, kernel and idle along + with the interrupt time and use that to calculate a new idle + percentage and thus a CPU util percentage. + + that is what we will attempt to do here. raj 2005-01-28 + + of course, we also have to wonder what we should do if there is + more interrupt time than the sum of user, kernel and idle. + that is a theoretical possibility I suppose, but for the + time-being, one that we will blythly ignore, except perhaps for + a quick check. raj 2005-01-31 + */ + + /* we ass-u-me that these counters will never wrap during a + netperf run. this may not be a particularly safe thing to + do. raj 2005-01-28 */ + delta_cpu_counters[i].idle = ending_cpu_counters[i].idle - + starting_cpu_counters[i].idle; + delta_cpu_counters[i].user = ending_cpu_counters[i].user - + starting_cpu_counters[i].user; + delta_cpu_counters[i].kernel = ending_cpu_counters[i].kernel - + starting_cpu_counters[i].kernel; + delta_cpu_counters[i].interrupt = ending_cpu_counters[i].interrupt - + starting_cpu_counters[i].interrupt; + + if (debug) { + print_cpu_time_counters("delta_cpu_counters",i,delta_cpu_counters); + } + + /* for this summation, we do not include interrupt time */ + total_cpu_nsec = + delta_cpu_counters[i].idle + + delta_cpu_counters[i].user + + delta_cpu_counters[i].kernel; + + if (debug) { + fprintf(where,"total_cpu_nsec %llu\n",total_cpu_nsec); + } + + if (delta_cpu_counters[i].interrupt > total_cpu_nsec) { + /* we are not in Kansas any more Toto, and I am not quite sure + the best way to get our tails out of here so let us just + punt. raj 2005-01-31 */ + fprintf(where, + "WARNING! WARNING! WARNING! WARNING! WARNING! \n"); + fprintf(where, + "calc_cpu_util_internal: more interrupt time than others combined!\n"); + fprintf(where, + "\tso CPU util cannot be estimated\n"); + fprintf(where, + "\t delta[%d].interrupt %llu\n",i,delta_cpu_counters[i].interrupt); + fprintf(where, + "\t delta[%d].idle %llu\n",i,delta_cpu_counters[i].idle); + fprintf(where, + "\t delta[%d].user %llu\n",i,delta_cpu_counters[i].user); + fprintf(where, + "\t delta[%d].kernel %llu\n",i,delta_cpu_counters[i].kernel); + fflush(where); + + lib_local_cpu_util = -1.0; + lib_local_per_cpu_util[i] = -1.0; + return -1.0; + } + + /* and now some fun with integer math. i initially tried to + promote things to long doubled but that didn't seem to result + in happiness and joy. raj 2005-01-28 */ + + fraction_idle = + (delta_cpu_counters[i].idle * CALC_ACCURACY) / total_cpu_nsec; + + fraction_user = + (delta_cpu_counters[i].user * CALC_ACCURACY) / total_cpu_nsec; + + fraction_kernel = + (delta_cpu_counters[i].kernel * CALC_ACCURACY) / total_cpu_nsec; + + /* ok, we have our fractions, now we want to take that fraction of + the interrupt time and subtract that from the bucket. */ + + interrupt_idle = ((delta_cpu_counters[i].interrupt * fraction_idle) / + CALC_ACCURACY); + + interrupt_user = ((delta_cpu_counters[i].interrupt * fraction_user) / + CALC_ACCURACY); + + interrupt_kernel = ((delta_cpu_counters[i].interrupt * fraction_kernel) / + CALC_ACCURACY); + + if (debug) { + fprintf(where, + "\tfraction_idle %llu interrupt_idle %llu\n", + fraction_idle, + interrupt_idle); + fprintf(where, + "\tfraction_user %llu interrupt_user %llu\n", + fraction_user, + interrupt_user); + fprintf(where,"\tfraction_kernel %llu interrupt_kernel %llu\n", + fraction_kernel, + interrupt_kernel); + } + + corrected_cpu_counters[i].idle = delta_cpu_counters[i].idle - + interrupt_idle; + + corrected_cpu_counters[i].user = delta_cpu_counters[i].user - + interrupt_user; + + corrected_cpu_counters[i].kernel = delta_cpu_counters[i].kernel - + interrupt_kernel; + + corrected_cpu_counters[i].interrupt = delta_cpu_counters[i].interrupt; + + if (debug) { + print_cpu_time_counters("corrected_cpu_counters", + i, + corrected_cpu_counters); + } + + /* I was going to checkfor going less than zero, but since all the + calculations are in unsigned quantities that would seem to be a + triffle silly... raj 2005-01-28 */ + + /* ok, now we sum the numbers again, this time including interrupt + */ + + total_cpu_nsec = + corrected_cpu_counters[i].idle + + corrected_cpu_counters[i].user + + corrected_cpu_counters[i].kernel + + corrected_cpu_counters[i].interrupt; + + /* and recalculate our fractions we are really only going to use + fraction_idle, but lets calculate the rest just for the heck of + it. one day we may want to display them. raj 2005-01-28 */ + + /* multiply by 100 and divide by total and you get whole + percentages. multiply by 1000 and divide by total and you get + tenths of percentages. multiply by 10000 and divide by total + and you get hundredths of percentages. etc etc etc raj + 2005-01-28 */ + fraction_idle = + (corrected_cpu_counters[i].idle * CALC_ACCURACY) / total_cpu_nsec; + + fraction_user = + (corrected_cpu_counters[i].user * CALC_ACCURACY) / total_cpu_nsec; + + fraction_kernel = + (corrected_cpu_counters[i].kernel * CALC_ACCURACY) / total_cpu_nsec; + + fraction_interrupt = + (corrected_cpu_counters[i].interrupt * CALC_ACCURACY) / total_cpu_nsec; + + if (debug) { + fprintf(where,"\tfraction_idle %lu\n",fraction_idle); + fprintf(where,"\tfraction_user %lu\n",fraction_user); + fprintf(where,"\tfraction_kernel %lu\n",fraction_kernel); + fprintf(where,"\tfraction_interrupt %lu\n",fraction_interrupt); + } + + /* and finally, what is our CPU utilization? */ + lib_local_per_cpu_util[i] = 100.0 - (((float)fraction_idle / + (float)CALC_ACCURACY) * 100.0); + if (debug) { + fprintf(where, + "lib_local_per_cpu_util[%d] %g\n", + i, + lib_local_per_cpu_util[i]); + } + lib_local_cpu_util += lib_local_per_cpu_util[i]; + } + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + lib_local_cpu_util *= correction_factor; + return lib_local_cpu_util; + + +} + +void +cpu_start_internal(void) +{ + get_cpu_time_counters(starting_cpu_counters); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_time_counters(ending_cpu_counters); +} diff --git a/netcpu_looper.c b/netcpu_looper.c new file mode 100644 index 0000000..b76c7c9 --- /dev/null +++ b/netcpu_looper.c @@ -0,0 +1,656 @@ +char netcpu_looper_id[]="\ +@(#)netcpu_looper.c (c) Copyright 2005-2007. Version 2.4.3"; + +/* netcpu_looper.c + + Implement the soaker process specific portions of netperf CPU + utilization measurements. These are broken-out into a separate file + to make life much nicer over in netlib.c which had become a maze of + twisty, CPU-util-related, #ifdefs, all different. raj 2005-01-26 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#if defined(HAVE_MMAP) || defined(HAVE_SYS_MMAN_H) +# include <sys/mman.h> +#else +# error netcpu_looper requires mmap +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "netsh.h" +#include "netlib.h" + +#define PAGES_PER_CHILD 2 + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibrarion to arrive at a CPU utilization + percentage. raj 2005-01-26 */ +static uint64_t lib_start_count[MAXCPUS]; +static uint64_t lib_end_count[MAXCPUS]; + +static int *cpu_mappings; + +static int lib_idle_fd; +static uint64_t *lib_idle_address[MAXCPUS]; +static long *lib_base_pointer; +static pid_t lib_idle_pids[MAXCPUS]; +static int lib_loopers_running=0; + +/* we used to use this code to bind the loopers, but since we have + decided to enable processor affinity for the actual + netperf/netserver processes we will use that affinity routine, + which happens to know about more systems than this */ + +#ifdef NOTDEF +static void +bind_to_processor(int child_num) +{ + /* This routine will bind the calling process to a particular */ + /* processor. We are not choosy as to which processor, so it will be */ + /* the process id mod the number of processors - shifted by one for */ + /* those systems which name processor starting from one instead of */ + /* zero. on those systems where I do not yet know how to bind a */ + /* process to a processor, this routine will be a no-op raj 10/95 */ + + /* just as a reminder, this is *only* for the looper processes, not */ + /* the actual measurement processes. those will, should, MUST float */ + /* or not float from CPU to CPU as controlled by the operating */ + /* system defaults. raj 12/95 */ + +#ifdef __hpux +#include <sys/syscall.h> +#include <sys/mp.h> + + int old_cpu = -2; + + if (debug) { + fprintf(where, + "child %d asking for CPU %d as pid %d with %d CPUs\n", + child_num, + (child_num % lib_num_loc_cpus), + getpid(), + lib_num_loc_cpus); + fflush(where); + } + + SETPROCESS((child_num % lib_num_loc_cpus), getpid()); + return; + +#else +#if defined(__sun) && defined(__SVR4) + /* should only be Solaris */ +#include <sys/processor.h> +#include <sys/procset.h> + + int old_binding; + + if (debug) { + fprintf(where, + "bind_to_processor: child %d asking for CPU %d as pid %d with %d CPUs\n", + child_num, + (child_num % lib_num_loc_cpus), + getpid(), + lib_num_loc_cpus); + fflush(where); + } + + if (processor_bind(P_PID, + getpid(), + (child_num % lib_num_loc_cpus), + &old_binding) != 0) { + fprintf(where,"bind_to_processor: unable to perform processor binding\n"); + fprintf(where," errno %d\n",errno); + fflush(where); + } + return; +#else +#ifdef WIN32 + + if (!SetThreadAffinityMask(GetCurrentThread(), (ULONG_PTR)1 << (child_num % lib_num_loc_cpus))) { + perror("SetThreadAffinityMask failed"); + fflush(stderr); + } + + if (debug) { + fprintf(where, + "bind_to_processor: child %d asking for CPU %d of %d CPUs\n", + child_num, + (child_num % lib_num_loc_cpus), + lib_num_loc_cpus); + fflush(where); + } + +#endif + return; +#endif /* __sun && _SVR4 */ +#endif /* __hpux */ +} +#endif + + /* sit_and_spin will just spin about incrementing a value */ + /* this value will either be in a memory mapped region on Unix shared */ + /* by each looper process, or something appropriate on Windows/NT */ + /* (malloc'd or such). This routine is reasonably ugly in that it has */ + /* priority manipulating code for lots of different operating */ + /* systems. This routine never returns. raj 1/96 */ + +static void +sit_and_spin(int child_index) + +{ + uint64_t *my_counter_ptr; + + /* only use C stuff if we are not WIN32 unless and until we */ + /* switch from CreateThread to _beginthread. raj 1/96 */ +#ifndef WIN32 + /* we are the child. we could decide to exec some separate */ + /* program, but that doesn't really seem worthwhile - raj 4/95 */ + if (debug > 1) { + fprintf(where, + "Looper child %d is born, pid %d\n", + child_index, + getpid()); + fflush(where); + } + +#endif /* WIN32 */ + + /* reset our base pointer to be at the appropriate offset */ + my_counter_ptr = (uint64_t *) ((char *)lib_base_pointer + + (netlib_get_page_size() * + PAGES_PER_CHILD * child_index)); + + /* in the event we are running on an MP system, it would */ + /* probably be good to bind the soaker processes to specific */ + /* processors. I *think* this is the most reasonable thing to */ + /* do, and would be closes to simulating the information we get */ + /* on HP-UX with pstat. I could put all the system-specific code */ + /* here, but will "abstract it into another routine to keep this */ + /* area more readable. I'll probably do the same thine with the */ + /* "low pri code" raj 10/95 */ + + /* since we are "flying blind" wrt where we should bind the looper + processes, we want to use the cpu_map that was prepared by netlib + rather than assume that the CPU ids on the system start at zero + and are contiguous. raj 2006-04-03 */ + bind_to_specific_processor(child_index % lib_num_loc_cpus,1); + + for (*my_counter_ptr = 0L; + ; + (*my_counter_ptr)++) { + if (!(*lib_base_pointer % 1)) { + /* every once and again, make sure that our process priority is */ + /* nice and low. also, by making system calls, it may be easier */ + /* for us to be pre-empted by something that needs to do useful */ + /* work - like the thread of execution actually sending and */ + /* receiving data across the network :) */ +#ifdef _AIX + int pid,prio; + + prio = PRIORITY; + pid = getpid(); + /* if you are not root, this call will return EPERM - why one */ + /* cannot change one's own priority to lower value is beyond */ + /* me. raj 2/26/96 */ + setpri(pid, prio); +#else /* _AIX */ +#ifdef __sgi + int pid,prio; + + prio = PRIORITY; + pid = getpid(); + schedctl(NDPRI, pid, prio); + sginap(0); +#else /* __sgi */ +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_IDLE); +#else /* WIN32 */ +#if defined(__sun) && defined(__SVR4) +#include <sys/types.h> +#include <sys/priocntl.h> +#include <sys/rtpriocntl.h> +#include <sys/tspriocntl.h> + /* I would *really* like to know how to use priocntl to make the */ + /* priority low for this looper process. however, either my mind */ + /* is addled, or the manpage in section two for priocntl is not */ + /* terribly helpful - for one, it has no examples :( so, if you */ + /* can help, I'd love to hear from you. in the meantime, we will */ + /* rely on nice(39). raj 2/26/96 */ + nice(39); +#else /* __sun && __SVR4 */ + nice(39); +#endif /* __sun && _SVR4 */ +#endif /* WIN32 */ +#endif /* __sgi */ +#endif /* _AIX */ + } + } +} + + + + /* this routine will start all the looper processes or threads for */ + /* measuring CPU utilization. */ + +static void +start_looper_processes() +{ + + unsigned int i, file_size; + + /* we want at least two pages for each processor. the */ + /* child for any one processor will write to the first of his two */ + /* pages, and the second page will be a buffer in case there is page */ + /* prefetching. if your system pre-fetches more than a single page, */ + /* well, you'll have to modify this or live with it :( raj 4/95 */ + + file_size = ((netlib_get_page_size() * PAGES_PER_CHILD) * + lib_num_loc_cpus); + +#ifndef WIN32 + + /* we we are not using WINDOWS NT (or 95 actually :), then we want */ + /* to create a memory mapped region so we can see all the counting */ + /* rates of the loopers */ + + /* could we just use an anonymous memory region for this? it is */ + /* possible that using a mmap()'ed "real" file, while convenient for */ + /* debugging, could result in some filesystem activity - like */ + /* metadata updates? raj 4/96 */ + lib_idle_fd = open("/tmp/netperf_cpu",O_RDWR | O_CREAT | O_EXCL); + + if (lib_idle_fd == -1) { + fprintf(where,"create_looper: file creation; errno %d\n",errno); + fflush(where); + exit(1); + } + + if (chmod("/tmp/netperf_cpu",0644) == -1) { + fprintf(where,"create_looper: chmod; errno %d\n",errno); + fflush(where); + exit(1); + } + + /* with the file descriptor in place, lets be sure that the file is */ + /* large enough. */ + + if (truncate("/tmp/netperf_cpu",file_size) == -1) { + fprintf(where,"create_looper: truncate: errno %d\n",errno); + fflush(where); + exit(1); + } + + /* the file should be large enough now, so we can mmap it */ + + /* if the system does not have MAP_VARIABLE, just define it to */ + /* be zero. it is only used/needed on HP-UX (?) raj 4/95 */ +#ifndef MAP_VARIABLE +#define MAP_VARIABLE 0x0000 +#endif /* MAP_VARIABLE */ +#ifndef MAP_FILE +#define MAP_FILE 0x0000 +#endif /* MAP_FILE */ + if ((lib_base_pointer = (long *)mmap(NULL, + file_size, + PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED | MAP_VARIABLE, + lib_idle_fd, + 0)) == (long *)-1) { + fprintf(where,"create_looper: mmap: errno %d\n",errno); + fflush(where); + exit(1); + } + + + if (debug > 1) { + fprintf(where,"num CPUs %d, file_size %d, lib_base_pointer %p\n", + lib_num_loc_cpus, + file_size, + lib_base_pointer); + fflush(where); + } + + /* we should have a valid base pointer. lets fork */ + + for (i = 0; i < (unsigned int)lib_num_loc_cpus; i++) { + switch (lib_idle_pids[i] = fork()) { + case -1: + perror("netperf: fork"); + exit(1); + case 0: + /* we are the child. we could decide to exec some separate */ + /* program, but that doesn't really seem worthwhile - raj 4/95 */ + + signal(SIGTERM, SIG_DFL); + sit_and_spin(i); + + /* we should never really get here, but if we do, just exit(0) */ + exit(0); + break; + default: + /* we must be the parent */ + lib_idle_address[i] = (uint64_t *) ((char *)lib_base_pointer + + (netlib_get_page_size() * + PAGES_PER_CHILD * i)); + if (debug) { + fprintf(where,"lib_idle_address[%d] is %p\n", + i, + lib_idle_address[i]); + fflush(where); + } + } + } +#else + /* we are compiled -DWIN32 */ + if ((lib_base_pointer = malloc(file_size)) == NULL) { + fprintf(where, + "create_looper_process could not malloc %d bytes\n", + file_size); + fflush(where); + exit(1); + } + + /* now, create all the threads */ + for(i = 0; i < (unsigned int)lib_num_loc_cpus; i++) { + long place_holder; + if ((lib_idle_pids[i] = CreateThread(0, + 0, + (LPTHREAD_START_ROUTINE)sit_and_spin, + (LPVOID)(ULONG_PTR)i, + 0, + &place_holder)) == NULL ) { + fprintf(where, + "create_looper_process: CreateThread failed\n"); + fflush(where); + /* I wonder if I need to look for other threads to kill? */ + exit(1); + } + lib_idle_address[i] = (long *) ((char *)lib_base_pointer + + (netlib_get_page_size() * + PAGES_PER_CHILD * i)); + if (debug) { + fprintf(where,"lib_idle_address[%d] is %p\n", + i, + lib_idle_address[i]); + fflush(where); + } + } +#endif /* WIN32 */ + + /* we need to have the looper processes settled-in before we do */ + /* anything with them, so lets sleep for say 30 seconds. raj 4/95 */ + + sleep(30); +} + +void +cpu_util_init(void) +{ + cpu_method = LOOPER; + + /* we want to get the looper processes going */ + if (!lib_loopers_running) { + start_looper_processes(); + lib_loopers_running = 1; + } + + return; +} + +/* clean-up any left-over CPU util resources - looper processes, + files, whatever. raj 2005-01-26 */ +void +cpu_util_terminate() { + +#ifdef WIN32 + /* it would seem that if/when the process exits, all the threads */ + /* will go away too, so I don't think I need any explicit thread */ + /* killing calls here. raj 1/96 */ +#else + + int i; + + /* now go through and kill-off all the child processes */ + for (i = 0; i < lib_num_loc_cpus; i++){ + /* SIGKILL can leave core files behind - thanks to Steinar Haug */ + /* for pointing that out. */ + kill(lib_idle_pids[i],SIGTERM); + } + lib_loopers_running = 0; + /* reap the children */ + while(waitpid(-1, NULL, WNOHANG) > 0) { } + + /* finally, unlink the mmaped file */ + munmap((caddr_t)lib_base_pointer, + ((netlib_get_page_size() * PAGES_PER_CHILD) * + lib_num_loc_cpus)); + unlink("/tmp/netperf_cpu"); +#endif + return; +} + +int +get_cpu_method(void) +{ + return LOOPER; +} + + /* calibrate_looper */ + + /* Loop a number of iterations, sleeping interval seconds each and */ + /* count how high the idle counter gets each time. Return the */ + /* measured cpu rate to the calling routine. raj 4/95 */ + +float +calibrate_idle_rate (int iterations, int interval) +{ + + uint64_t + firstcnt[MAXCPUS], + secondcnt[MAXCPUS]; + + float + elapsed, + temp_rate, + rate[MAXTIMES], + local_maxrate; + + long + sec, + usec; + + int + i, + j; + + struct timeval time1, time2 ; + struct timezone tz; + + if (iterations > MAXTIMES) { + iterations = MAXTIMES; + } + + local_maxrate = (float)-1.0; + + for(i = 0; i < iterations; i++) { + rate[i] = (float)0.0; + for (j = 0; j < lib_num_loc_cpus; j++) { + firstcnt[j] = *(lib_idle_address[j]); + } + gettimeofday (&time1, &tz); + sleep(interval); + 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; + elapsed = (float)sec + ((float)usec/(float)1000000.0); + + if(debug) { + fprintf(where, "Calibration for counter run: %d\n",i); + fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec); + fprintf(where,"\telapsed time = %g\n",elapsed); + } + + for (j = 0; j < lib_num_loc_cpus; j++) { + secondcnt[j] = *(lib_idle_address[j]); + if(debug) { + /* I know that there are situations where compilers know about */ + /* long long, but the library fucntions do not... raj 4/95 */ + fprintf(where, + "\tfirstcnt[%d] = 0x%8.8lx%8.8lx secondcnt[%d] = 0x%8.8lx%8.8lx\n", + j, + (uint32_t)(firstcnt[j]>>32), + (uint32_t)(firstcnt[j]&0xffffffff), + j, + (uint32_t)(secondcnt[j]>>32), + (uint32_t)(secondcnt[j]&0xffffffff)); + } + /* we assume that it would wrap no more than once. we also */ + /* assume that the result of subtracting will "fit" raj 4/95 */ + temp_rate = (secondcnt[j] >= firstcnt[j]) ? + (float)(secondcnt[j] - firstcnt[j])/elapsed : + (float)(secondcnt[j]-firstcnt[j]+MAXLONG)/elapsed; + if (temp_rate > rate[i]) rate[i] = temp_rate; + if(debug) { + fprintf(where,"\trate[%d] = %g\n",i,rate[i]); + fflush(where); + } + if (local_maxrate < rate[i]) local_maxrate = rate[i]; + } + } + if(debug) { + fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate); + fflush(where); + } + return local_maxrate; +} + + +void +get_cpu_idle (uint64_t *res) +{ + int i; + + for (i = 0; i < lib_num_loc_cpus; i++){ + res[i] = *lib_idle_address[i]; + } + +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + float correction_factor; + float actual_rate; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* it would appear that on some systems, in loopback, nice is + *very* effective, causing the looper process to stop dead in its + tracks. if this happens, we need to ensure that the calculation + does not go south. raj 6/95 and if we run completely out of idle, + the same thing could in theory happen to the USE_KSTAT path. raj + 8/2000 */ + + if (lib_end_count[i] == lib_start_count[i]) { + lib_end_count[i]++; + } + + actual_rate = (lib_end_count[i] > lib_start_count[i]) ? + (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed : + (float)(lib_end_count[i] - lib_start_count[i] + + MAXLONG)/ lib_elapsed; + if (debug) { + fprintf(where, + "calc_cpu_util: actual_rate on processor %d is %f start 0x%8.8lx%8.8lx end 0x%8.8lx%8.8lx\n", + i, + actual_rate, + (uint32_t)(lib_start_count[i]>>32), + (uint32_t)(lib_start_count[i]&0xffffffff), + (uint32_t)(lib_end_count[i]>>32), + (uint32_t)(lib_end_count[i]&0xffffffff)); + } + lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / + lib_local_maxrate * 100; + lib_local_cpu_util += lib_local_per_cpu_util[i]; + } + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + lib_local_cpu_util *= correction_factor; + return lib_local_cpu_util; + + +} +void +cpu_start_internal(void) +{ + get_cpu_idle(lib_start_count); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_idle(lib_end_count); +} diff --git a/netcpu_none.c b/netcpu_none.c new file mode 100644 index 0000000..f71b240 --- /dev/null +++ b/netcpu_none.c @@ -0,0 +1,67 @@ +char netcpu_none_id[]="\ +@(#)netcpu_none.c (c) Copyright 2005, Hewlett-Packard Company, Version 2.4.0"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#include "netsh.h" +#include "netlib.h" + +void +cpu_util_init(void) +{ + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return CPU_UNKNOWN; +} + +void +get_cpu_idle(uint64_t *res) +{ + return; +} + +float +calibrate_idle_rate(int iterations, int interval) +{ + return 0.0; +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + return -1.0; +} + +void +cpu_start_internal(void) +{ + return; +} + +void +cpu_stop_internal(void) +{ + return; +} diff --git a/netcpu_ntperf.c b/netcpu_ntperf.c new file mode 100644 index 0000000..e8d8f76 --- /dev/null +++ b/netcpu_ntperf.c @@ -0,0 +1,497 @@ +char netcpu_ntperf_id[]="\ +@(#)netcpu_ntperf.c (c) Copyright 2005-2007, Hewlett-Packard Company, Version 2.4.3"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if 0 +#include <limits.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#endif + +#include <assert.h> + +#include <process.h> +#include <time.h> + +#include <windows.h> +#include <assert.h> + +#include <winsock2.h> +// If you are trying to compile on Windows 2000 or NT 4.0 you may +// need to define DONT_IPV6 in the "sources" files. +#ifndef DONT_IPV6 +#include <ws2tcpip.h> +#endif + +#include "netsh.h" +#include "netlib.h" + +// +// System CPU time information class. +// Used to get CPU time information. +// +// SDK\inc\ntexapi.h +// Function x8: SystemProcessorPerformanceInformation +// DataStructure: SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +// + +#define SystemProcessorPerformanceInformation 0x08 + +typedef struct +{ + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER DpcTime; + LARGE_INTEGER InterruptTime; + LONG InterruptCount; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + +// +// Calls to get the information +// +typedef ULONG (__stdcall *NT_QUERY_SYSTEM_INFORMATION)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + +NT_QUERY_SYSTEM_INFORMATION NtQuerySystemInformation = NULL; + + +static LARGE_INTEGER TickHz = {0,0}; + +_inline LARGE_INTEGER ReadPerformanceCounter(VOID) +{ + LARGE_INTEGER Counter; + QueryPerformanceCounter(&Counter); + + return(Counter); +} // ReadperformanceCounter + + +/* The NT performance data is accessed through the NtQuerySystemInformation + call. References to the PDH.DLL have been deleted. This structure + is the root for these data structures. */ + +typedef struct sPerfObj +{ + LARGE_INTEGER StartTime; + LARGE_INTEGER EndTime; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StartInfo[MAXCPUS +1]; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION EndInfo[MAXCPUS +1]; +} PerfObj, *PPerfObj; + +static PerfObj *PerfCntrs; + +// Forward declarations + +PerfObj *InitPerfCntrs(); +void RestartPerfCntrs(PerfObj *PerfCntrs); +double ReportPerfCntrs(PerfObj *PerfCntrs); /* returns CPU utilization */ +void ClosePerfCntrs(PerfObj *PerfCntrs); + + +void +cpu_util_init(void) +{ + if (NtQuerySystemInformation == NULL) { + // Open the performance counter interface + PerfCntrs = InitPerfCntrs(); + } + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return NT_METHOD; +} + +typedef unsigned __int64 uint64_t; + +void +get_cpu_idle(uint64_t *res) +{ + RestartPerfCntrs(PerfCntrs); + return; +} + +float +calibrate_idle_rate(int iterations, int interval) +{ + return (float)0.0; +} + + +/* + InitPerfCntrs() - + + Changed to no longer access the NT performance registry interfaces. + A direct call to NtQuerySystemInformation (an undocumented NT API) + is made instead. Parameters determined by decompilation of ntkrnlmp + and ntdll. +*/ + + +PerfObj *InitPerfCntrs() +{ + PerfObj *NewPerfCntrs; + DWORD NTVersion; + DWORD status; + SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + + NewPerfCntrs = (PerfObj *)GlobalAlloc(GPTR, sizeof(PerfObj)); + assert(NewPerfCntrs != NULL); + + ZeroMemory((PCHAR)NewPerfCntrs, sizeof(PerfObj)); + + // get NT version + NTVersion = GetVersion(); + if (NTVersion >= 0x80000000) + { + fprintf(stderr, "Not running on Windows NT\n"); + exit(1); + } + + // locate the calls we need in NTDLL + //Lint + NtQuerySystemInformation = + (NT_QUERY_SYSTEM_INFORMATION)GetProcAddress( GetModuleHandle("ntdll.dll"), + "NtQuerySystemInformation" ); + + if ( !(NtQuerySystemInformation) ) + { + //Lint + status = GetLastError(); + fprintf(stderr, "GetProcAddressFailed, status: %X\n", status); + exit(1); + } + + // setup to measure timestamps with the high resolution timers. + if (QueryPerformanceFrequency(&TickHz) == FALSE) + { + fprintf(stderr,"MAIN - QueryPerformanceFrequency Failed!\n"); + exit(2); + } + + RestartPerfCntrs(NewPerfCntrs); + + return(NewPerfCntrs); +} /* InitPerfCntrs */ + +/* + RestartPerfCntrs() - + + The Performance counters must be read twice to produce rate and + percentage results. This routine is called before the start of a + benchmark to establish the initial counters. It must be called a + second time after the benchmark completes to collect the final state + of the performance counters. ReportPerfCntrs is called to print the + results after the benchmark completes. +*/ + +void RestartPerfCntrs(PerfObj *PerfCntrs) +{ + DWORD returnLength = 0; //Lint + DWORD returnNumCPUs; //Lint + DWORD i; + + DWORD status; + SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + + // Move previous data from EndInfo to StartInfo. + CopyMemory((PCHAR)&PerfCntrs->StartInfo[0], + (PCHAR)&PerfCntrs->EndInfo[0], + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*(MAXCPUS +1)); + + PerfCntrs->StartTime = PerfCntrs->EndTime; + + // get the current CPUTIME information + if ( (status = NtQuerySystemInformation( SystemProcessorPerformanceInformation, + (PCHAR)&PerfCntrs->EndInfo[0], sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*MAXCPUS, + &returnLength )) != 0) + { + fprintf(stderr, "NtQuery failed, status: %X\n", status); + exit(1); + } + + PerfCntrs->EndTime = ReadPerformanceCounter(); + + // Validate that NtQuery returned a reasonable amount of data + if ((returnLength % sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) != 0) + { + fprintf(stderr, "NtQuery didn't return expected amount of data\n"); + fprintf(stderr, "Expected a multiple of %i, returned %i\n", + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), returnLength); + exit(1); + } + returnNumCPUs = returnLength / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); + + if (returnNumCPUs != (int)SystemInfo.dwNumberOfProcessors) + { + fprintf(stderr, "NtQuery didn't return expected amount of data\n"); + fprintf(stderr, "Expected data for %i CPUs, returned %i\n", + (int)SystemInfo.dwNumberOfProcessors, returnNumCPUs); + exit(1); + } + + // Zero entries not returned by NtQuery + ZeroMemory((PCHAR)&PerfCntrs->EndInfo[returnNumCPUs], + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)* + (MAXCPUS +1 - returnNumCPUs)); + + // Total all of the CPUs + // KernelTime needs to be fixed-up; it includes both idle & + // true kernel time + // Note that kernel time also includes DpcTime & InterruptTime, but + // I like this. + for (i=0; i < returnNumCPUs; i++) + { + PerfCntrs->EndInfo[i].KernelTime.QuadPart -= PerfCntrs->EndInfo[i].IdleTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].IdleTime.QuadPart += PerfCntrs->EndInfo[i].IdleTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].KernelTime.QuadPart += PerfCntrs->EndInfo[i].KernelTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].UserTime.QuadPart += PerfCntrs->EndInfo[i].UserTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].DpcTime.QuadPart += PerfCntrs->EndInfo[i].DpcTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].InterruptTime.QuadPart += PerfCntrs->EndInfo[i].InterruptTime.QuadPart; + PerfCntrs->EndInfo[MAXCPUS].InterruptCount += PerfCntrs->EndInfo[i].InterruptCount; + } + +} /* RestartPerfCntrs */ + +/* + ReportPerfCntrs() - + This routine reports the results of the various performance + counters. +*/ + +double ReportPerfCntrs(PerfObj *PerfCntrs) +{ + double tot_CPU_Util; + int i; + int duration; // in 100 usecs + + LARGE_INTEGER ActualDuration; + + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION DeltaInfo[MAXCPUS +1]; + + LARGE_INTEGER TotalCPUTime[MAXCPUS +1]; + + SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + + for (i=0; i <= MAXCPUS; i++) + { + DeltaInfo[i].IdleTime.QuadPart = PerfCntrs->EndInfo[i].IdleTime.QuadPart - + PerfCntrs->StartInfo[i].IdleTime.QuadPart; + DeltaInfo[i].KernelTime.QuadPart = PerfCntrs->EndInfo[i].KernelTime.QuadPart - + PerfCntrs->StartInfo[i].KernelTime.QuadPart; + DeltaInfo[i].UserTime.QuadPart = PerfCntrs->EndInfo[i].UserTime.QuadPart - + PerfCntrs->StartInfo[i].UserTime.QuadPart; + DeltaInfo[i].DpcTime.QuadPart = PerfCntrs->EndInfo[i].DpcTime.QuadPart - + PerfCntrs->StartInfo[i].DpcTime.QuadPart; + DeltaInfo[i].InterruptTime.QuadPart = PerfCntrs->EndInfo[i].InterruptTime.QuadPart - + PerfCntrs->StartInfo[i].InterruptTime.QuadPart; + DeltaInfo[i].InterruptCount = PerfCntrs->EndInfo[i].InterruptCount - + PerfCntrs->StartInfo[i].InterruptCount; + + TotalCPUTime[i].QuadPart = + DeltaInfo[i].IdleTime.QuadPart + + DeltaInfo[i].KernelTime.QuadPart + + DeltaInfo[i].UserTime.QuadPart; + // KernelTime already includes DpcTime & InterruptTime! + // + DeltaInfo[i].DpcTime.QuadPart + + // DeltaInfo[i].InterruptTime.QuadPart; + } + + tot_CPU_Util = 100.0*(1.0 - (double)DeltaInfo[MAXCPUS].IdleTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint + + // Re-calculate duration, since we may have stoped early due to cntr-C. + ActualDuration.QuadPart = PerfCntrs->EndTime.QuadPart - + PerfCntrs->StartTime.QuadPart; + + // convert to 1/10 milliseconds (100 usec) + ActualDuration.QuadPart = (ActualDuration.QuadPart*10000)/TickHz.QuadPart; + duration = ActualDuration.LowPart; + + if (verbosity > 1) + { + fprintf(where,"ActualDuration (ms): %d\n", duration/10); + } + + if (verbosity > 1) + { + fprintf(where, "%% CPU _Total"); + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t CPU %i", i); + } + } + fprintf(where, "\n"); + + fprintf(where, "Busy %5.2f", tot_CPU_Util); + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.2f", + 100.0*(1.0 - (double)DeltaInfo[i].IdleTime.QuadPart/(double)TotalCPUTime[i].QuadPart)); //Lint + } + } + fprintf(where, "\n"); + + fprintf(where, "Kernel %5.2f", + 100.0*(double)DeltaInfo[MAXCPUS].KernelTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint + + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.2f", + 100.0*(double)DeltaInfo[i].KernelTime.QuadPart/(double)TotalCPUTime[i].QuadPart); //Lint + } + } + fprintf(where, "\n"); + + fprintf(where, "User %5.2f", + 100.0*(double)DeltaInfo[MAXCPUS].UserTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); + + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.2f", + 100.0*(double)DeltaInfo[i].UserTime.QuadPart/TotalCPUTime[i].QuadPart); //Lint + } + } + fprintf(where, "\n"); + + fprintf(where, "Dpc %5.2f", + 100.0*(double)DeltaInfo[MAXCPUS].DpcTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint + + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.2f", + 100.0*(double)DeltaInfo[i].DpcTime.QuadPart/(double)TotalCPUTime[i].QuadPart); //Lint + } + } + fprintf(where, "\n"); + + fprintf(where, "Interrupt %5.2f", + 100.0*(double)DeltaInfo[MAXCPUS].InterruptTime.QuadPart/(double)TotalCPUTime[MAXCPUS].QuadPart); //Lint + + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.2f", + 100.0*(double)DeltaInfo[i].InterruptTime.QuadPart/TotalCPUTime[i].QuadPart); //Lint + } + } + fprintf(where, "\n\n"); + + fprintf(where, "Interrupt/Sec. %5.1f", + (double)DeltaInfo[MAXCPUS].InterruptCount*10000.0/(double)duration); + + if ((int)SystemInfo.dwNumberOfProcessors > 1) + { + for (i=0; i < (int)SystemInfo.dwNumberOfProcessors; i++) + { + fprintf(where, "\t %5.1f", + (double)DeltaInfo[i].InterruptCount*10000.0/(double)duration); + } + } + fprintf(where, "\n\n"); + fflush(where); + } + + return (tot_CPU_Util); + +} /* ReportPerfCntrs */ + +/* + ClosePerfCntrs() - + + This routine cleans up the performance counter APIs. +*/ + +void ClosePerfCntrs(PerfObj *PerfCntrs) +{ + GlobalFree(PerfCntrs); + + NtQuerySystemInformation = NULL; +} /* ClosePerfCntrs */ + +void +cpu_start_internal(void) +{ + RestartPerfCntrs(PerfCntrs);
+} + +void +cpu_stop_internal(void) +{ + RestartPerfCntrs(PerfCntrs);
+} + +float +calc_cpu_util_internal(float elapsed_time) +{ + float correction_factor; + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + if (debug) { + fprintf(where, "correction factor: %f\n", correction_factor); + } + + lib_local_cpu_util = (float)ReportPerfCntrs(PerfCntrs); + lib_local_cpu_util *= correction_factor; + return lib_local_cpu_util; + +} diff --git a/netcpu_osx.c b/netcpu_osx.c new file mode 100644 index 0000000..2132be1 --- /dev/null +++ b/netcpu_osx.c @@ -0,0 +1,149 @@ +char netcpu_sysctl_id[]="\ +@(#)netcpu_osx.c Version 2.4.3"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if HAVE_LIMITS_H +# include <limits.h> +# ifndef LONG_LONG_MAX +# define LONG_LONG_MAX LLONG_MAX +# endif /* LONG_LONG_MAX */ +#endif + + +#include <errno.h> + +#include <mach/host_info.h> +#include <mach/mach_types.h> +/* it would seem that on 10.3.9 mach_msg_type_number_t is in + <mach/message.h> so we'll see about including that one too. + hopefully it still exists in 10.4. if not, we will need to add some + .h file checks in configure so we can use "HAVE_mumble" ifdefs + here */ +#include <mach/message.h> + +#include "netsh.h" +#include "netlib.h" + +#define UNSIGNED_DIFFERENCE(x,y) (x >= y ? x - y : (0 - y) + x ) + +static host_cpu_load_info_data_t lib_start_ticks; +static host_cpu_load_info_data_t lib_end_ticks; + +static mach_port_t lib_host_port; + +void +cpu_util_init(void) +{ + lib_host_port = mach_host_self(); + return; +} + +void +cpu_util_terminate(void) +{ + mach_port_deallocate(lib_host_port); + return; +} + +int +get_cpu_method(void) +{ + return OSX; +} + +void +get_cpu_idle(uint64_t *res) +{ + return; +} + +void +get_host_ticks(host_cpu_load_info_t info) +{ + mach_msg_type_number_t count; + + count = HOST_CPU_LOAD_INFO_COUNT; + host_statistics(lib_host_port, HOST_CPU_LOAD_INFO, (host_info_t)info, &count); + return; +} + +/* calibrate_sysctl - perform the idle rate calculation using the + sysctl call - typically on BSD */ + +float +calibrate_idle_rate(int iterations, int interval) +{ + return (float)0.0; +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + float correction_factor; + natural_t userticks, systicks, idleticks, totalticks; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + if (debug) { + fprintf(where, "correction factor: %f\n", correction_factor); + } + + userticks = UNSIGNED_DIFFERENCE((lib_end_ticks.cpu_ticks[CPU_STATE_USER] + lib_end_ticks.cpu_ticks[CPU_STATE_NICE]), + (lib_start_ticks.cpu_ticks[CPU_STATE_USER] + lib_start_ticks.cpu_ticks[CPU_STATE_NICE])); + systicks = UNSIGNED_DIFFERENCE(lib_end_ticks.cpu_ticks[CPU_STATE_SYSTEM], lib_start_ticks.cpu_ticks[CPU_STATE_SYSTEM]); + idleticks = UNSIGNED_DIFFERENCE(lib_end_ticks.cpu_ticks[CPU_STATE_IDLE], lib_start_ticks.cpu_ticks[CPU_STATE_IDLE]); + totalticks = userticks + systicks + idleticks; + + lib_local_cpu_util = ((float)userticks + (float)systicks)/(float)totalticks * 100.0f; + lib_local_cpu_util *= correction_factor; + + return lib_local_cpu_util; + +} +void +cpu_start_internal(void) +{ + get_host_ticks(&lib_start_ticks); +} + +void +cpu_stop_internal(void) +{ + get_host_ticks(&lib_end_ticks); +} diff --git a/netcpu_perfstat.c b/netcpu_perfstat.c new file mode 100644 index 0000000..f928a25 --- /dev/null +++ b/netcpu_perfstat.c @@ -0,0 +1,351 @@ +char netcpu_perfstat_id[]="\ +@(#)netcpu_perfstat.c Version 2.4.0"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_LIMITS_H +# include <limits.h> +# ifndef LONG_LONG_MAX +# define LONG_LONG_MAX LLONG_MAX +# endif /* LONG_LONG_MAX */ +#endif + +#include <errno.h> + +#include "netsh.h" +#include "netlib.h" + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibration to arrive at a CPU utilization + percentage. raj 2005-01-26 */ +static uint64_t lib_start_count[MAXCPUS]; +static uint64_t lib_end_count[MAXCPUS]; + + +void +cpu_util_init(void) +{ + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return PERFSTAT; +} + +void +get_cpu_idle(uint64_t *res) +{ + perfstat_cpu_t *perfstat_buffer; + perfstat_cpu_t *per_cpu_pointer; + perfstat_id_t name; + int i,ret; + + /* a name of "" will cause us to start from the beginning */ + strcpy(name.name,""); + perfstat_buffer = (perfstat_cpu_t *)malloc(lib_num_loc_cpus * + sizeof(perfstat_cpu_t)); + if (perfstat_buffer == NULL) { + fprintf(where, + "cpu_start: malloc failed errno %d\n", + errno); + fflush(where); + exit(-1); + } + + /* happiness and joy, keep going */ + ret = perfstat_cpu(&name, + perfstat_buffer, + sizeof(perfstat_cpu_t), + lib_num_loc_cpus); + + if ((ret == -1) || + (ret != lib_num_loc_cpus)) { + fprintf(where, + "cpu_start: perfstat_cpu failed/count off; errno %d cpus %d count %d\n", + errno, + lib_num_loc_cpus, + ret); + fflush(where); + exit(-1); + } + + per_cpu_pointer = perfstat_buffer; + for (i = 0; i < lib_num_loc_cpus; i++){ + res[i] = per_cpu_pointer->idle; + per_cpu_pointer++; + } + free(perfstat_buffer); + + return; +} + +float +calibrate_idle_rate(int iterations, int interval) +{ + unsigned long long + firstcnt[MAXCPUS], + secondcnt[MAXCPUS]; + + float + elapsed, + temp_rate, + rate[MAXTIMES], + local_maxrate; + + long + sec, + usec; + + int + i, + j; + + struct timeval time1, time2 ; + struct timezone tz; + + perfstat_cpu_t *perfstat_buffer; + perfstat_cpu_t *per_cpu_pointer; + perfstat_id_t name; + int ret; + + if (debug) { + fprintf(where,"enter calibrate_perfstat\n"); + fflush(where); + } + + if (iterations > MAXTIMES) { + iterations = MAXTIMES; + } + + local_maxrate = (float)-1.0; + + perfstat_buffer = (perfstat_cpu_t *)malloc(lib_num_loc_cpus * + sizeof(perfstat_cpu_t)); + if (perfstat_buffer == NULL) { + fprintf(where, + "calibrate_perfstat: malloc failed errno %d\n", + errno); + fflush(where); + exit(-1); + } + + for(i = 0; i < iterations; i++) { + rate[i] = (float)0.0; + /* a name of "" will cause us to start from the beginning */ + strcpy(name.name,""); + + /* happiness and joy, keep going */ + ret = perfstat_cpu(&name, + perfstat_buffer, + sizeof(perfstat_cpu_t), + lib_num_loc_cpus); + + if ((ret == -1) || + (ret != lib_num_loc_cpus)) { + fprintf(where, + "calibrate_perfstat: perfstat_cpu failed/count off; errno %d cpus %d count %d\n", + errno, + lib_num_loc_cpus, + ret); + fflush(where); + exit(-1); + } + + per_cpu_pointer = perfstat_buffer; + for (j = 0; j < lib_num_loc_cpus; j++) { + firstcnt[j] = per_cpu_pointer->idle; + per_cpu_pointer++; + } + gettimeofday (&time1, &tz); + sleep(interval); + 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; + elapsed = (float)sec + ((float)usec/(float)1000000.0); + + /* happiness and joy, keep going */ + ret = perfstat_cpu(&name, + perfstat_buffer, + sizeof(perfstat_cpu_t), + lib_num_loc_cpus); + + if ((ret == -1) || + (ret != lib_num_loc_cpus)) { + fprintf(where, + "calibrate_perfstat: perfstat_cpu failed/count off; errno %d cpus %d count %d\n", + errno, + lib_num_loc_cpus, + ret); + fflush(where); + exit(-1); + } + + per_cpu_pointer = perfstat_buffer; + + if(debug) { + fprintf(where, "Calibration for perfstat counter run: %d\n",i); + fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec); + fprintf(where,"\telapsed time = %g\n",elapsed); + } + + for (j = 0; j < lib_num_loc_cpus; j++) { + secondcnt[j] = per_cpu_pointer->idle; + per_cpu_pointer++; + if(debug) { + /* I know that there are situations where compilers know about */ + /* long long, but the library functions do not... raj 4/95 */ + fprintf(where, + "\tfirstcnt[%d] = 0x%8.8lx%8.8lx secondcnt[%d] = 0x%8.8lx%8.8lx\n", + j, + firstcnt[j], + firstcnt[j], + j, + secondcnt[j], + secondcnt[j]); + } + /* we assume that it would wrap no more than once. we also */ + /* assume that the result of subtracting will "fit" raj 4/95 */ + temp_rate = (secondcnt[j] >= firstcnt[j]) ? + (float)(secondcnt[j] - firstcnt[j])/elapsed : + (float)(secondcnt[j]-firstcnt[j]+MAXLONG)/elapsed; + if (temp_rate > rate[i]) rate[i] = temp_rate; + if(debug) { + fprintf(where,"\trate[%d] = %g\n",i,rate[i]); + fflush(where); + } + if (local_maxrate < rate[i]) local_maxrate = rate[i]; + } + } + if(debug) { + fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate); + fflush(where); + } + free(perfstat_buffer); + return local_maxrate; +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + + float actual_rate; + float correction_factor; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + /* this looks just like the looper case. at least I think it */ + /* should :) raj 4/95 */ + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* we assume that the two are not more than a long apart. I */ + /* know that this is bad, but trying to go from long longs to */ + /* a float (perhaps a double) is boggling my mind right now. */ + /* raj 4/95 */ + + long long + diff; + + if (lib_end_count[i] >= lib_start_count[i]) { + diff = lib_end_count[i] - lib_start_count[i]; + } + else { + diff = lib_end_count[i] - lib_start_count[i] + LONG_LONG_MAX; + } + actual_rate = (float) diff / lib_elapsed; + lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / + lib_local_maxrate * 100; + lib_local_cpu_util += lib_local_per_cpu_util[i]; + if (debug) { + fprintf(where, + "calc_cpu_util: actual_rate on cpu %d is %g max_rate %g cpu %6.2f\n", + i, + actual_rate, + lib_local_maxrate, + lib_local_per_cpu_util[i]); + } + } + + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + if (debug) { + fprintf(where, + "calc_cpu_util: average across CPUs is %g\n",lib_local_cpu_util); + } + + lib_local_cpu_util *= correction_factor; + + if (debug) { + fprintf(where, + "calc_cpu_util: returning %g\n",lib_local_cpu_util); + } + + return lib_local_cpu_util; + +} +void +cpu_start_internal(void) +{ + get_cpu_idle(lib_start_count); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_idle(lib_end_count); +} + diff --git a/netcpu_procstat.c b/netcpu_procstat.c new file mode 100644 index 0000000..5bdadea --- /dev/null +++ b/netcpu_procstat.c @@ -0,0 +1,265 @@ +char netcpu_procstat_id[]="\ +@(#)netcpu_procstat.c (c) Copyright 2005-2007 Version 2.4.3"; + +/* netcpu_procstat.c + + Implement the /proc/stat specific portions of netperf CPU + utilization measurements. These are broken-out into a separate file + to make life much nicer over in netlib.c which had become a maze of + twisty, CPU-util-related, #ifdefs, all different. raj 2005-01-26 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif + +#include <string.h> + +#include "netsh.h" +#include "netlib.h" + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibrarion to arrive at a CPU utilization + percentage. raj 2005-01-26 */ +static uint64_t lib_start_count[MAXCPUS]; +static uint64_t lib_end_count[MAXCPUS]; + + +/* The max. length of one line of /proc/stat cpu output */ +#define CPU_LINE_LENGTH ((8 * sizeof (long) / 3 + 1) * 4 + 8) +#define PROC_STAT_FILE_NAME "/proc/stat" +#define N_CPU_LINES(nr) (nr == 1 ? 1 : 1 + nr) + +static int proc_stat_fd = -1; +static char *proc_stat_buf = NULL; +static int proc_stat_buflen = 0; + +void +cpu_util_init(void) +{ + + if (debug) { + fprintf(where, + "cpu_util_init enter, proc_stat_fd %d proc_stat_buf %p\n", + proc_stat_fd, + proc_stat_buf); + fflush(where); + } + if (proc_stat_fd < 0) { + proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); + if (proc_stat_fd < 0) { + fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); + exit (1); + }; + }; + + if (!proc_stat_buf) { + proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; + if (debug) { + fprintf(where, + "lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", + lib_num_loc_cpus, + N_CPU_LINES(lib_num_loc_cpus), + CPU_LINE_LENGTH, + proc_stat_buflen); + fflush(where); + } + proc_stat_buf = (char *)malloc (proc_stat_buflen); + if (!proc_stat_buf) { + fprintf (stderr, "Cannot allocate buffer memory!\n"); + exit (1); + } + } + return; +} + +void +cpu_util_terminate(void) +{ + close(proc_stat_fd); + proc_stat_fd = -1; + free(proc_stat_buf); + proc_stat_buf = NULL; + return; +} + +int +get_cpu_method() +{ + return PROC_STAT; +} + +float +calibrate_idle_rate (int iterations, int interval) +{ + if (proc_stat_fd < 0) { + proc_stat_fd = open (PROC_STAT_FILE_NAME, O_RDONLY, NULL); + if (proc_stat_fd < 0) { + fprintf (stderr, "Cannot open %s!\n", PROC_STAT_FILE_NAME); + exit (1); + }; + }; + + if (!proc_stat_buf) { + proc_stat_buflen = N_CPU_LINES (lib_num_loc_cpus) * CPU_LINE_LENGTH; + if (debug) { + fprintf(where, + "calibrate: lib_num_loc_cpus %d lines %d CPU_LINE_LENGTH %d proc_stat_buflen %d\n", + lib_num_loc_cpus, + N_CPU_LINES(lib_num_loc_cpus), + CPU_LINE_LENGTH, + proc_stat_buflen); + fflush(where); + } + proc_stat_buf = (char *)malloc (proc_stat_buflen); + if (!proc_stat_buf) { + fprintf (stderr, "Cannot allocate buffer memory!\n"); + exit (1); + }; + }; + + return sysconf (_SC_CLK_TCK); +} + +void +get_cpu_idle (uint64_t *res) +{ + int space; + int i; + int n = lib_num_loc_cpus; + char *p = proc_stat_buf; + + lseek (proc_stat_fd, 0, SEEK_SET); + read (proc_stat_fd, p, proc_stat_buflen); + + if (debug) { + fprintf(where,"proc_stat_buf '%.*s'\n",proc_stat_buflen,p); + fflush(where); + } + /* Skip first line (total) on SMP */ + if (n > 1) p = strchr (p, '\n'); + + /* Idle time is the 4th space-separated token */ + for (i = 0; i < n; i++) { + for (space = 0; space < 4; space ++) { + p = strchr (p, ' '); + while (*++p == ' '); + }; + res[i] = strtoul (p, &p, 10); + if (debug) { + fprintf(where,"res[%d] is %llu\n",i,res[i]); + fflush(where); + } + p = strchr (p, '\n'); + }; + +} + +/* take the initial timestamp and start collecting CPU utilization if + requested */ + +void +measure_cpu_start() +{ + cpu_method = PROC_STAT; + get_cpu_idle(lib_start_count); +} + +/* collect final CPU utilization raw data */ +void +measure_cpu_stop() +{ + get_cpu_idle(lib_end_count); +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + + float actual_rate; + float correction_factor; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* it would appear that on some systems, in loopback, nice is + *very* effective, causing the looper process to stop dead in its + tracks. if this happens, we need to ensure that the calculation + does not go south. raj 6/95 and if we run completely out of idle, + the same thing could in theory happen to the USE_KSTAT path. raj + 8/2000 */ + + if (lib_end_count[i] == lib_start_count[i]) { + lib_end_count[i]++; + } + + actual_rate = (lib_end_count[i] > lib_start_count[i]) ? + (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed : + (float)(lib_end_count[i] - lib_start_count[i] + + MAXLONG)/ lib_elapsed; + lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / + lib_local_maxrate * 100; + if (debug) { + fprintf(where, + "calc_cpu_util: actual_rate on processor %d is %f start %llx end %llx util %f\n", + i, + actual_rate, + lib_start_count[i], + lib_end_count[i], + lib_local_per_cpu_util[i]); + } + lib_local_cpu_util += lib_local_per_cpu_util[i]; + } + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + lib_local_cpu_util *= correction_factor; + return lib_local_cpu_util; +} + +void +cpu_start_internal(void) +{ + get_cpu_idle(lib_start_count); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_idle(lib_end_count); +} diff --git a/netcpu_pstat.c b/netcpu_pstat.c new file mode 100644 index 0000000..08f27c1 --- /dev/null +++ b/netcpu_pstat.c @@ -0,0 +1,307 @@ +char netcpu_pstat_id[]="\ +@(#)netcpu_pstat.c (c) Copyright 2005, Hewlett-Packard Company, Version 2.4.0"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <sys/dk.h> +#include <sys/pstat.h> + +#ifndef PSTAT_IPCINFO +# error Sorry, pstat() CPU utilization on 10.0 and later only +#endif + +#include "netsh.h" +#include "netlib.h" + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibrarion to arrive at a CPU utilization + percentage. raj 2005-01-26 */ +static uint64_t lib_start_count[MAXCPUS]; +static uint64_t lib_end_count[MAXCPUS]; + +void +cpu_util_init(void) +{ + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return HP_IDLE_COUNTER; +} + +void +get_cpu_idle(uint64_t *res) +{ + /* get the idle sycle counter for each processor */ + struct pst_processor *psp; + union overlay_u { + long long full; + long word[2]; + } *overlay; + + psp = (struct pst_processor *)malloc(lib_num_loc_cpus * sizeof(*psp)); + if (psp == NULL) { + printf("malloc(%d) failed!\n", lib_num_loc_cpus * sizeof(*psp)); + exit(1); + } + if (pstat_getprocessor(psp, sizeof(*psp), lib_num_loc_cpus, 0) != -1) { + int i; + for (i = 0; i < lib_num_loc_cpus; i++) { + overlay = (union overlay_u *)&(res[i]); + overlay->word[0] = psp[i].psp_idlecycles.psc_hi; + overlay->word[1] = psp[i].psp_idlecycles.psc_lo; + if(debug) { + fprintf(where, + "\tres[%d] = 0x%8.8x%8.8x\n", + i, + hi_32(&res[i]), + lo_32(&res[i])); + fflush(where); + } + } + free(psp); + } +} + +/* calibrate_pstat + Loop a number of iterations, sleeping wait_time seconds each and + count how high the idle counter gets each time. Return the measured + cpu rate to the calling routine. */ + +float +calibrate_idle_rate(int iterations, int interval) +{ + + uint64_t + firstcnt[MAXCPUS], + secondcnt[MAXCPUS]; + + float + elapsed, + temp_rate, + rate[MAXTIMES], + local_maxrate; + + long + sec, + usec; + + int + i, + j; + + long count; + + struct timeval time1, time2; + struct timezone tz; + + struct pst_processor *psp; + + if (iterations > MAXTIMES) { + iterations = MAXTIMES; + } + + local_maxrate = -1.0; + + psp = (struct pst_processor *)malloc(lib_num_loc_cpus * sizeof(*psp)); + if (psp == NULL) { + printf("malloc(%d) failed!\n", lib_num_loc_cpus * sizeof(*psp)); + exit(1); + } + + for(i = 0; i < iterations; i++) { + rate[i] = 0.0; + /* get the idle sycle counter for each processor */ + if (pstat_getprocessor(psp, sizeof(*psp), lib_num_loc_cpus, 0) != -1) { + for (j = 0; j < lib_num_loc_cpus; j++) { + union overlay_u { + long long full; + long word[2]; + } *overlay; + overlay = (union overlay_u *)&(firstcnt[j]); + overlay->word[0] = psp[j].psp_idlecycles.psc_hi; + overlay->word[1] = psp[j].psp_idlecycles.psc_lo; + } + } + else { + fprintf(where,"pstat_getprocessor failure errno %d\n",errno); + fflush(where); + exit(1); + } + + gettimeofday (&time1, &tz); + sleep(interval); + 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; + elapsed = (float)sec + ((float)usec/(float)1000000.0); + + if(debug) { + fprintf(where, "Calibration for counter run: %d\n",i); + fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec); + fprintf(where,"\telapsed time = %g\n",elapsed); + } + + if (pstat_getprocessor(psp, sizeof(*psp), lib_num_loc_cpus, 0) != -1) { + for (j = 0; j < lib_num_loc_cpus; j++) { + union overlay_u { + long long full; + long word[2]; + } *overlay; + overlay = (union overlay_u *)&(secondcnt[j]); + overlay->word[0] = psp[j].psp_idlecycles.psc_hi; + overlay->word[1] = psp[j].psp_idlecycles.psc_lo; + if(debug) { + /* I know that there are situations where compilers know about */ + /* long long, but the library fucntions do not... raj 4/95 */ + fprintf(where, + "\tfirstcnt[%d] = 0x%8.8x%8.8x secondcnt[%d] = 0x%8.8x%8.8x\n", + j, + hi_32(&firstcnt[j]), + lo_32(&firstcnt[j]), + j, + hi_32(&secondcnt[j]), + lo_32(&secondcnt[j])); + } + temp_rate = (secondcnt[j] >= firstcnt[j]) ? + (float)(secondcnt[j] - firstcnt[j] )/elapsed : + (float)(secondcnt[j] - firstcnt[j] + LONG_LONG_MAX)/elapsed; + if (temp_rate > rate[i]) rate[i] = temp_rate; + if(debug) { + fprintf(where,"\trate[%d] = %g\n",i,rate[i]); + fflush(where); + } + if (local_maxrate < rate[i]) local_maxrate = rate[i]; + } + } + else { + fprintf(where,"pstat failure; errno %d\n",errno); + fflush(where); + exit(1); + } + } + if(debug) { + fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate); + fflush(where); + } + return local_maxrate; + +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + + float actual_rate; + float correction_factor; + + lib_local_cpu_util = (float)0.0; + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + /* this looks just like the looper case. at least I think it */ + /* should :) raj 4/95 */ + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* we assume that the two are not more than a long apart. I */ + /* know that this is bad, but trying to go from long longs to */ + /* a float (perhaps a double) is boggling my mind right now. */ + /* raj 4/95 */ + + long long + diff; + + if (lib_end_count[i] >= lib_start_count[i]) { + diff = lib_end_count[i] - lib_start_count[i]; + } + else { + diff = lib_end_count[i] - lib_start_count[i] + LONG_LONG_MAX; + } + actual_rate = (float) diff / lib_elapsed; + lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / + lib_local_maxrate * 100; + lib_local_cpu_util += lib_local_per_cpu_util[i]; + if (debug) { + fprintf(where, + "calc_cpu_util: actual_rate on cpu %d is %g max_rate %g cpu %6.2f\n", + i, + actual_rate, + lib_local_maxrate, + lib_local_per_cpu_util[i]); + } + } + + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + if (debug) { + fprintf(where, + "calc_cpu_util: average across CPUs is %g\n",lib_local_cpu_util); + } + + lib_local_cpu_util *= correction_factor; + + if (debug) { + fprintf(where, + "calc_cpu_util: returning %g\n",lib_local_cpu_util); + } + + return lib_local_cpu_util; + +} +void +cpu_start_internal(void) +{ + get_cpu_idle(lib_start_count); + return; +} + +void +cpu_stop_internal(void) +{ + get_cpu_idle(lib_end_count); +} diff --git a/netcpu_pstatnew.c b/netcpu_pstatnew.c new file mode 100644 index 0000000..fd2d036 --- /dev/null +++ b/netcpu_pstatnew.c @@ -0,0 +1,398 @@ +char netcpu_pstatnew_id[]="\ +@(#)netcpu_pstatnew.c (c) Copyright 2005, Hewlett-Packard Company, Version 2.4.1"; + +/* since we "know" that this interface is available only on 11.23 and + later, and that 11.23 and later are strictly 64-bit kernels, we can + arbitrarily set _PSTAT64 here and not have to worry about it up in + the configure script and makefiles. raj 2005/09/06 */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#include <unistd.h> + +#if HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <sys/dk.h> +#include <sys/pstat.h> + +/* HP-UX 11.23 seems to have added three other cycle counters to the + original psp_idlecycles - one for user, one for kernel and one for + interrupt. so, we can now use those to calculate CPU utilization + without requiring any calibration phase. raj 2005-02-16 */ + +#ifndef PSTAT_IPCINFO +# error Sorry, pstat() CPU utilization on 10.0 and later only +#endif + +typedef struct cpu_time_counters { + uint64_t idle; + uint64_t user; + uint64_t kernel; + uint64_t interrupt; +} cpu_time_counters_t; + +uint64_t lib_iticksperclktick; + +#include "netsh.h" +#include "netlib.h" + +/* the lib_start_count and lib_end_count arrays hold the starting + and ending values of whatever is counting when the system is + idle. The rate at which this increments during a test is compared + with a previous calibrarion to arrive at a CPU utilization + percentage. raj 2005-01-26 */ + +static cpu_time_counters_t starting_cpu_counters[MAXCPUS]; +static cpu_time_counters_t ending_cpu_counters[MAXCPUS]; +static cpu_time_counters_t delta_cpu_counters[MAXCPUS]; + +void +cpu_util_init(void) +{ + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return HP_IDLE_COUNTER; +} + +void +get_cpu_counters(cpu_time_counters_t *res) +{ + /* get the idle sycle counter for each processor. now while on a + 64-bit kernel the ".psc_hi" and ".psc_lo" fields are 64 bits, + only the bottom 32-bits are actually valid. don't ask me + why, that is just the way it is. soo, we shift the psc_hi + value by 32 bits and then just sum-in the psc_lo value. raj + 2005/09/06 */ + struct pst_processor *psp; + + psp = (struct pst_processor *)malloc(lib_num_loc_cpus * sizeof(*psp)); + if (psp == NULL) { + printf("malloc(%d) failed!\n", lib_num_loc_cpus * sizeof(*psp)); + exit(1); + } + if (pstat_getprocessor(psp, sizeof(*psp), lib_num_loc_cpus, 0) != -1) { + int i; + /* we use lib_iticksperclktick in our sanity checking. we + ass-u-me it is the same value for each CPU - famous last + words no doubt. raj 2005/09/06 */ + lib_iticksperclktick = psp[0].psp_iticksperclktick; + for (i = 0; i < lib_num_loc_cpus; i++) { + res[i].idle = (((uint64_t)psp[i].psp_idlecycles.psc_hi << 32) + + psp[i].psp_idlecycles.psc_lo); + if(debug) { + fprintf(where, + "\tidle[%d] = 0x%"PRIx64" ", + i, + res[i].idle); + fflush(where); + } + res[i].user = (((uint64_t)psp[i].psp_usercycles.psc_hi << 32) + + psp[i].psp_usercycles.psc_lo); + if(debug) { + fprintf(where, + "user[%d] = 0x%"PRIx64" ", + i, + res[i].user); + fflush(where); + } + res[i].kernel = (((uint64_t)psp[i].psp_systemcycles.psc_hi << 32) + + psp[i].psp_systemcycles.psc_lo); + if(debug) { + fprintf(where, + "kern[%d] = 0x%"PRIx64" ", + i, + res[i].kernel); + fflush(where); + } + res[i].interrupt = (((uint64_t)psp[i].psp_interruptcycles.psc_hi << 32) + + psp[i].psp_interruptcycles.psc_lo); + if(debug) { + fprintf(where, + "intr[%d] = 0x%"PRIx64"\n", + i, + res[i].interrupt); + fflush(where); + } + } + free(psp); + } +} + +/* calibrate_pstatnew + there really isn't anything much to do here since we have all the + counters and use their ratios for CPU util measurement. raj + 2005-02-16 */ + +float +calibrate_idle_rate(int iterations, int interval) +{ + return 0.0; +} + +static void +print_cpu_time_counters(char *name, int instance, cpu_time_counters_t *counters) +{ + fprintf(where,"%s[%d]:\n",name,instance); + fprintf(where, + "\t idle %llu\n",counters[instance].idle); + fprintf(where, + "\t user %llu\n",counters[instance].user); + fprintf(where, + "\t kernel %llu\n",counters[instance].kernel); + fprintf(where, + "\t interrupt %llu\n",counters[instance].interrupt); +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + int i; + + uint64_t total_cpu_cycles; + uint64_t sanity_cpu_cycles; + +#ifndef USE_INTEGER_MATH + double fraction_idle; + double fraction_user; + double fraction_kernel; + double fraction_interrupt; + double estimated_fraction_interrupt; +#else + uint64_t fraction_idle; + uint64_t fraction_user; + uint64_t fraction_kernel; + uint64_t fraction_interrupt; + uint64_t estimated_fraction_interrupt; + +#define CALC_PERCENT 100 +#define CALC_TENTH_PERCENT 1000 +#define CALC_HUNDREDTH_PERCENT 10000 +#define CALC_THOUSANDTH_PERCENT 100000 +#define CALC_ACCURACY CALC_THOUSANDTH_PERCENT + +#endif /* USE_INTEGER_MATH */ + float actual_rate; + float correction_factor; + + lib_local_cpu_util = (float)0.0; + + /* It is possible that the library measured a time other than */ + /* the one that the user want for the cpu utilization */ + /* calculations - for example, tests that were ended by */ + /* watchdog timers such as the udp stream test. We let these */ + /* tests tell up what the elapsed time should be. */ + + if (elapsed_time != 0.0) { + correction_factor = (float) 1.0 + + ((lib_elapsed - elapsed_time) / elapsed_time); + } + else { + correction_factor = (float) 1.0; + } + + /* calculate our sanity check on cycles */ + if (debug) { + fprintf(where, + "lib_elapsed %g _SC_CLK_TCK %d lib_iticksperclktick %"PRIu64"\n", + lib_elapsed, + sysconf(_SC_CLK_TCK), + lib_iticksperclktick); + } + + /* Ok, elsewhere I may have said that HP-UX 11.23 does the "right" + thing in measuring user, kernel, interrupt and idle all together + instead of overlapping interrupt with the others like an OS that + shall not be named. However.... it seems there is a bug in the + accounting for interrupt cycles, whereby the cycles do not get + properly accounted. The sum of user, kernel, interrupt and idle + does not equal the clock rate multiplied by the elapsed time. + Some cycles go missing. + + Since we see agreement between netperf and glance/vsar with the + old "pstat" mechanism, we can presume that the accounting for + idle cycles is sufficiently accurate. So, while we will still do + math with user, kernel and interrupt cycles, we will only + caculate CPU utilization based on the ratio of idle to _real_ + total cycles. I am told that a "future release" of HP-UX will + fix the interupt cycle accounting. raj 2005/09/14 */ + + /* calculate what the sum of CPU cycles _SHOULD_ be */ + sanity_cpu_cycles = (uint64_t) ((double)lib_elapsed * + (double) sysconf(_SC_CLK_TCK) * (double)lib_iticksperclktick); + + /* this looks just like the looper case. at least I think it */ + /* should :) raj 4/95 */ + for (i = 0; i < lib_num_loc_cpus; i++) { + + /* we ass-u-me that these counters will never wrap during a + netperf run. this may not be a particularly safe thing to + do. raj 2005-01-28 */ + delta_cpu_counters[i].idle = ending_cpu_counters[i].idle - + starting_cpu_counters[i].idle; + delta_cpu_counters[i].user = ending_cpu_counters[i].user - + starting_cpu_counters[i].user; + delta_cpu_counters[i].kernel = ending_cpu_counters[i].kernel - + starting_cpu_counters[i].kernel; + delta_cpu_counters[i].interrupt = ending_cpu_counters[i].interrupt - + starting_cpu_counters[i].interrupt; + + if (debug) { + print_cpu_time_counters("delta_cpu_counters",i,delta_cpu_counters); + } + + /* now get the sum, which we ass-u-me does not overflow a 64-bit + counter. raj 2005-02-16 */ + total_cpu_cycles = + delta_cpu_counters[i].idle + + delta_cpu_counters[i].user + + delta_cpu_counters[i].kernel + + delta_cpu_counters[i].interrupt; + + if (debug) { + fprintf(where, + "total_cpu_cycles %"PRIu64" sanity_cpu_cycles %"PRIu64" missing %"PRIu64"\n", + total_cpu_cycles, + sanity_cpu_cycles, + sanity_cpu_cycles - total_cpu_cycles); + } + + /* since HP-UX 11.23 does the _RIGHT_ thing and idle/user/kernel + does _NOT_ overlap with interrupt, we do not have to apply any + correction kludge. raj 2005-02-16 */ + +#ifndef USE_INTEGER_MATH + /* when the accounting for interrupt time gets its act together, + we can use total_cpu_cycles rather than sanity_cpu_cycles, but + until then, use sanity_cpu_ccles. raj 2005/09/14 */ + + fraction_idle = (double)delta_cpu_counters[i].idle / + (double)sanity_cpu_cycles; + + fraction_user = (double)delta_cpu_counters[i].user / + (double)sanity_cpu_cycles; + + fraction_kernel = (double) delta_cpu_counters[i].kernel / + (double)sanity_cpu_cycles; + + fraction_interrupt = (double)delta_cpu_counters[i].interrupt / + (double)sanity_cpu_cycles; + + /* ass-u-me that it is only interrupt that is bogus, and assign + all the "missing" cycles to it. raj 2005/09/14 */ + estimated_fraction_interrupt = ((double)delta_cpu_counters[i].interrupt + + (sanity_cpu_cycles - total_cpu_cycles)) / + (double)sanity_cpu_cycles; + + if (debug) { + fprintf(where,"\tfraction_idle %g\n",fraction_idle); + fprintf(where,"\tfraction_user %g\n",fraction_user); + fprintf(where,"\tfraction_kernel %g\n",fraction_kernel); + fprintf(where,"\tfraction_interrupt %g WARNING, possibly under-counted!\n",fraction_interrupt); + fprintf(where,"\testimated_fraction_interrupt %g\n", + estimated_fraction_interrupt); + } + + /* and finally, what is our CPU utilization? */ + lib_local_per_cpu_util[i] = 100.0 - (fraction_idle * 100.0); +#else + /* and now some fun with integer math. i initially tried to + promote things to long doubled but that didn't seem to result + in happiness and joy. raj 2005-01-28 */ + + /* multiply by 100 and divide by total and you get whole + percentages. multiply by 1000 and divide by total and you get + tenths of percentages. multiply by 10000 and divide by total + and you get hundredths of percentages. etc etc etc raj + 2005-01-28 */ + + /* when the accounting for interrupt time gets its act together, + we can use total_cpu_cycles rather than sanity_cpu_cycles, but + until then, use sanity_cpu_ccles. raj 2005/09/14 */ + + fraction_idle = + (delta_cpu_counters[i].idle * CALC_ACCURACY) / sanity_cpu_cycles; + + fraction_user = + (delta_cpu_counters[i].user * CALC_ACCURACY) / sanity_cpu_cycles; + + fraction_kernel = + (delta_cpu_counters[i].kernel * CALC_ACCURACY) / sanity_cpu_cycles; + + fraction_interrupt = + (delta_cpu_counters[i].interrupt * CALC_ACCURACY) / sanity_cpu_cycles; + + + estimated_fraction_interrupt = + ((delta_cpu_counters[i].interrupt + + (sanity_cpu_cycles - total_cpu_cycles)) * + CALC_ACCURACY) / sanity_cpu_cycles; + + if (debug) { + fprintf(where,"\tfraction_idle %"PRIu64"\n",fraction_idle); + fprintf(where,"\tfraction_user %"PRIu64"\n",fraction_user); + fprintf(where,"\tfraction_kernel %"PRIu64"\n",fraction_kernel); + fprintf(where,"\tfraction_interrupt %"PRIu64"WARNING, possibly under-counted!\n",fraction_interrupt); + fprintf(where,"\testimated_fraction_interrupt %"PRIu64"\n", + estimated_fraction_interrupt); + } + + /* and finally, what is our CPU utilization? */ + lib_local_per_cpu_util[i] = 100.0 - (((float)fraction_idle / + (float)CALC_ACCURACY) * 100.0); +#endif + if (debug) { + fprintf(where, + "lib_local_per_cpu_util[%d] %g\n", + i, + lib_local_per_cpu_util[i]); + } + lib_local_cpu_util += lib_local_per_cpu_util[i]; + } + /* we want the average across all n processors */ + lib_local_cpu_util /= (float)lib_num_loc_cpus; + + lib_local_cpu_util *= correction_factor; + + if (debug) { + fprintf(where, + "calc_cpu_util: returning %g\n",lib_local_cpu_util); + } + + return lib_local_cpu_util; + +} +void +cpu_start_internal(void) +{ + get_cpu_counters(starting_cpu_counters); +} + +void +cpu_stop_internal(void) +{ + get_cpu_counters(ending_cpu_counters); +} diff --git a/netcpu_sysctl.c b/netcpu_sysctl.c new file mode 100644 index 0000000..919f62c --- /dev/null +++ b/netcpu_sysctl.c @@ -0,0 +1,127 @@ +char netcpu_sysctl_id[]="\ +@(#)netcpu_sysctl.c Version 2.4.3"; + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if HAVE_LIMITS_H +# include <limits.h> +# ifndef LONG_LONG_MAX +# define LONG_LONG_MAX LLONG_MAX +# endif /* LONG_LONG_MAX */ +#endif + + +#include <errno.h> + +/* need to have some sort of check for sys/sysctl.h versus sysctl.h */ +#include <sys/sysctl.h> + + +/* this has been liberally cut and pasted from <sys/resource.h> on + FreeBSD. in general, this would be a bad idea, but I don't want to + have to do a _KERNEL define to get these and that is what + sys/resource.h seems to want. raj 2002-03-03 */ +#define CP_USER 0 +#define CP_NICE 1 +#define CP_SYS 2 +#define CP_INTR 3 +#define CP_IDLE 4 +#define CPUSTATES 5 + + +#include "netsh.h" +#include "netlib.h" + +static long lib_start_count[CPUSTATES]; +static long lib_end_count[CPUSTATES]; + +void +cpu_util_init(void) +{ + return; +} + +void +cpu_util_terminate(void) +{ + return; +} + +int +get_cpu_method(void) +{ + return SYSCTL; +} + +static void +get_cpu_time(long *cpu_time) +{ + size_t cpu_time_len = CPUSTATES * sizeof (cpu_time[0]); + + if (sysctlbyname("kern.cp_time", cpu_time, &cpu_time_len, NULL, 0) == -1) { + fprintf (stderr, "Cannot get CPU time!\n"); + exit (1); + } +} + +/* calibrate_sysctl - perform the idle rate calculation using the + sysctl call - typically on BSD */ + +float +calibrate_idle_rate(int iterations, int interval) +{ + return sysconf (_SC_CLK_TCK); +} + +float +calc_cpu_util_internal(float elapsed_time) +{ + long sum_idle, sum_busy; + int i; + + for (sum_busy = 0, i = 0; i < CPUSTATES; i++) { + if (i != CP_IDLE) + sum_busy += lib_end_count[i] - lib_start_count[i]; + } + + sum_idle = lib_end_count[CP_IDLE] - lib_start_count[CP_IDLE]; + lib_local_cpu_util = (float)sum_busy / (float)(sum_busy + sum_idle); + lib_local_cpu_util *= 100.0; + + return lib_local_cpu_util; + +} +void +cpu_start_internal(void) +{ + get_cpu_time(lib_start_count); +} + +void +cpu_stop_internal(void) +{ + get_cpu_time(lib_end_count); +} 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)); +} + diff --git a/netlib.h b/netlib.h new file mode 100644 index 0000000..5b6900e --- /dev/null +++ b/netlib.h @@ -0,0 +1,621 @@ +/* + Copyright (C) 1993-2005 Hewlett-Packard Company +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(HAVE_SYS_SOCKET_H) +# include <sys/socket.h> +#endif +#if defined(HAVE_NETDB_H) +# include <netdb.h> +#endif +#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) +# include "missing/getaddrinfo.h" +#endif + +#define PAD_TIME 4 +/* library routine specifc defines */ +#define MAXSPECDATA 62 /* how many ints worth of data */ + /* can tests send... */ +#define MAXTIMES 4 /* how many times may we loop */ + /* to calibrate */ +#define MAXCPUS 256 /* how many CPU's can we track */ +#define MAXMESSAGESIZE 65536 +#define MAXALIGNMENT 16384 +#define MAXOFFSET 4096 +#define DATABUFFERLEN MAXMESSAGESIZE+MAXALIGNMENT+MAXOFFSET + +#define DEBUG_ON 1 +#define DEBUG_OFF 2 +#define DEBUG_OK 3 +#define NODE_IDENTIFY 4 +#define CPU_CALIBRATE 5 + +#define DO_TCP_STREAM 10 +#define TCP_STREAM_RESPONSE 11 +#define TCP_STREAM_RESULTS 12 + +#define DO_TCP_RR 13 +#define TCP_RR_RESPONSE 14 +#define TCP_RR_RESULTS 15 + +#define DO_UDP_STREAM 16 +#define UDP_STREAM_RESPONSE 17 +#define UDP_STREAM_RESULTS 18 + +#define DO_UDP_RR 19 +#define UDP_RR_RESPONSE 20 +#define UDP_RR_RESULTS 21 + +#define DO_DLPI_CO_STREAM 22 +#define DLPI_CO_STREAM_RESPONSE 23 +#define DLPI_CO_STREAM_RESULTS 24 + +#define DO_DLPI_CO_RR 25 +#define DLPI_CO_RR_RESPONSE 26 +#define DLPI_CO_RR_RESULTS 27 + +#define DO_DLPI_CL_STREAM 28 +#define DLPI_CL_STREAM_RESPONSE 29 +#define DLPI_CL_STREAM_RESULTS 30 + +#define DO_DLPI_CL_RR 31 +#define DLPI_CL_RR_RESPONSE 32 +#define DLPI_CL_RR_RESULTS 33 + +#define DO_TCP_CRR 34 +#define TCP_CRR_RESPONSE 35 +#define TCP_CRR_RESULTS 36 + +#define DO_STREAM_STREAM 37 +#define STREAM_STREAM_RESPONSE 38 +#define STREAM_STREAM_RESULTS 39 + +#define DO_STREAM_RR 40 +#define STREAM_RR_RESPONSE 41 +#define STREAM_RR_RESULTS 42 + +#define DO_DG_STREAM 43 +#define DG_STREAM_RESPONSE 44 +#define DG_STREAM_RESULTS 45 + +#define DO_DG_RR 46 +#define DG_RR_RESPONSE 47 +#define DG_RR_RESULTS 48 + +#define DO_FORE_STREAM 49 +#define FORE_STREAM_RESPONSE 50 +#define FORE_STREAM_RESULTS 51 + +#define DO_FORE_RR 52 +#define FORE_RR_RESPONSE 53 +#define FORE_RR_RESULTS 54 + +#define DO_HIPPI_STREAM 55 +#define HIPPI_STREAM_RESPONSE 56 +#define HIPPI_STREAM_RESULTS 57 + +#define DO_HIPPI_RR 52 +#define HIPPI_RR_RESPONSE 53 +#define HIPPI_RR_RESULTS 54 + +#define DO_XTI_TCP_STREAM 55 +#define XTI_TCP_STREAM_RESPONSE 56 +#define XTI_TCP_STREAM_RESULTS 57 + +#define DO_XTI_TCP_RR 58 +#define XTI_TCP_RR_RESPONSE 59 +#define XTI_TCP_RR_RESULTS 60 + +#define DO_XTI_UDP_STREAM 61 +#define XTI_UDP_STREAM_RESPONSE 62 +#define XTI_UDP_STREAM_RESULTS 63 + +#define DO_XTI_UDP_RR 64 +#define XTI_UDP_RR_RESPONSE 65 +#define XTI_UDP_RR_RESULTS 66 + +#define DO_XTI_TCP_CRR 67 +#define XTI_TCP_CRR_RESPONSE 68 +#define XTI_TCP_CRR_RESULTS 69 + +#define DO_TCP_TRR 70 +#define TCP_TRR_RESPONSE 71 +#define TCP_TRR_RESULTS 72 + +#define DO_TCP_NBRR 73 +#define TCP_NBRR_RESPONSE 74 +#define TCP_NBRR_RESULTS 75 + +#define DO_TCPIPV6_STREAM 76 +#define TCPIPV6_STREAM_RESPONSE 77 +#define TCPIPV6_STREAM_RESULTS 78 + +#define DO_TCPIPV6_RR 79 +#define TCPIPV6_RR_RESPONSE 80 +#define TCPIPV6_RR_RESULTS 81 + +#define DO_UDPIPV6_STREAM 82 +#define UDPIPV6_STREAM_RESPONSE 83 +#define UDPIPV6_STREAM_RESULTS 84 + +#define DO_UDPIPV6_RR 85 +#define UDPIPV6_RR_RESPONSE 86 +#define UDPIPV6_RR_RESULTS 87 + +#define DO_TCPIPV6_CRR 88 +#define TCPIPV6_CRR_RESPONSE 89 +#define TCPIPV6_CRR_RESULTS 90 + +#define DO_TCPIPV6_TRR 91 +#define TCPIPV6_TRR_RESPONSE 92 +#define TCPIPV6_TRR_RESULTS 93 + +#define DO_TCP_MAERTS 94 +#define TCP_MAERTS_RESPONSE 95 +#define TCP_MAERTS_RESULTS 96 + +#define DO_LWPSTR_STREAM 100 +#define LWPSTR_STREAM_RESPONSE 110 +#define LWPSTR_STREAM_RESULTS 120 + +#define DO_LWPSTR_RR 130 +#define LWPSTR_RR_RESPONSE 140 +#define LWPSTR_RR_RESULTS 150 + +#define DO_LWPDG_STREAM 160 +#define LWPDG_STREAM_RESPONSE 170 +#define LWPDG_STREAM_RESULTS 180 + +#define DO_LWPDG_RR 190 +#define LWPDG_RR_RESPONSE 200 +#define LWPDG_RR_RESULTS 210 + +#define DO_TCP_CC 300 +#define TCP_CC_RESPONSE 301 +#define TCP_CC_RESULTS 302 + +/* The DNS_RR test has been removed from netperf but we leave these + here for historical purposes. Those wanting to do DNS_RR tests + should use netperf4 instead. */ +#define DO_DNS_RR 400 +#define DNS_RR_RESPONSE 401 +#define DNS_RR_RESULTS 402 + +#define DO_SCTP_STREAM 500 +#define SCTP_STREAM_RESPONSE 501 +#define SCTP_STREAM_RESULT 502 + +#define DO_SCTP_STREAM_MANY 510 +#define SCTP_STREAM_MANY_RESPONSE 511 +#define SCTP_STREAM_MANY_RESULT 512 + +#define DO_SCTP_RR 520 +#define SCTP_RR_RESPONSE 521 +#define SCTP_RR_RESULT 502 + +#define DO_SCTP_RR_MANY 530 +#define SCTP_RR_MANY_RESPONSE 531 +#define SCTP_RR_MANY_RESULT 532 + +#define DO_SDP_STREAM 540 +#define SDP_STREAM_RESPONSE 541 +#define SDP_STREAM_RESULTS 542 + +#define DO_SDP_RR 543 +#define SDP_RR_RESPONSE 544 +#define SDP_RR_RESULTS 545 + +#define DO_SDP_MAERTS 546 +#define SDP_MAERTS_RESPONSE 547 +#define SDP_MAERTS_RESULTS 548 + +#define DO_SDP_CRR 549 +#define SDP_CRR_RESPONSE 550 +#define SDP_CRR_RESULTS 551 + +#define DO_SDP_CC 552 +#define SDP_CC_RESPONSE 553 +#define SDP_CC_RESULTS 554 + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +enum sock_buffer{ + SEND_BUFFER, + RECV_BUFFER +}; + + /* some of the fields in these structures are going to be doubles and */ + /* such. so, we probably want to ensure that they will start on */ + /* "double" boundaries. this will break compatability to pre-2.1 */ + /* releases, but then, backwards compatability has never been a */ + /* stated goal of netperf. raj 11/95 */ + +union netperf_request_struct { + struct { + int request_type; + int dummy; + int test_specific_data[MAXSPECDATA]; + } content; + double dummy; +}; + +union netperf_response_struct { + struct { + int response_type; + int serv_errno; + int test_specific_data[MAXSPECDATA]; + } content; + double dummy; +}; + +struct ring_elt { + struct ring_elt *next; /* next element in the ring */ + char *buffer_base; /* in case we have to free it at somepoint */ + char *buffer_ptr; /* the aligned and offset pointer */ +}; + +/* +*+ SAF Sorry about the hacks with errno; NT made me do it :( + + WinNT does define an errno. + It is mostly a legacy from the XENIX days. + + Depending upon the version of the C run time that is linked in, it is + either a simple variable (like UNIX code expects), but more likely it + is the address of a procedure to return the error number. So any + code that sets errno is likely to be overwriting the address of this + procedure. Worse, only a tiny fraction of NT's errors get set + through errno. + + So I have changed the netperf code to use a define Set_errno when + that is it's intent. On non-windows platforms this is just an + assignment to errno. But on NT this calls SetLastError. + + I also define errno (now only used on right side of assignments) + on NT to be GetLastError. + + Similarly, perror is defined on NT, but it only accesses the same + XENIX errors that errno covers. So on NT this is redefined to be + Perror and it expands all GetLastError texts. */ + + +#ifdef WIN32 +/* INVALID_SOCKET == INVALID_HANDLE_VALUE == (unsigned int)(~0) */ +/* SOCKET_ERROR == -1 */ +#define ENOTSOCK WSAENOTSOCK +#define EINTR WSAEINTR +#define ENOBUFS WSAENOBUFS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EAFNOSUPPORT WSAEAFNOSUPPORT +/* I don't use a C++ style of comment because it upsets some C + compilers, possibly even when it is inside an ifdef WIN32... */ +/* from public\sdk\inc\crt\errno.h */ +#define ENOSPC 28 + +#ifdef errno +/* delete the one from stdlib.h */ +/*#define errno (*_errno()) */ +#undef errno +#endif +#define errno GetLastError() +#define Set_errno(num) SetLastError((num)) + +#define perror(text) PrintWin32Error(stderr, (text)) +#define Print_errno(stream, text) PrintWin32Error((stream), (text)) + +extern void PrintWin32Error(FILE *stream, LPSTR text); + +#if !defined(NT_PERF) && !defined(USE_LOOPER) +#define NT_PERF +#endif +#else +/* Really shouldn't use manifest constants! */ +/*+*+SAF There are other examples of "== -1" and "<0" that probably */ +/*+*+SAF should be cleaned up as well. */ +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#define SOCKET int +#define Set_errno(num) errno = (num) + +#define Print_errno(stream, text) fprintf((stream), "%s errno %d\n", (text), errno) +#endif + +/* Robin & Rick's kludge to try to have a timer signal EINTR by closing */ +/* the socket from another thread can also return several other errors. */ +/* Let's define a macro to hide all of this. */ + +#ifndef WIN32 +#define SOCKET_EINTR(return_value) (errno == EINTR) +#define SOCKET_EADDRINUSE(return_value) (errno == EADDRINUSE) +#define SOCKET_EADDRNOTAVAIL(return_value) (errno == EADDRNOTAVAIL) + +#else + +/* not quite sure I like the extra cases for WIN32 but that is what my + WIN32 expert sugested. I'm not sure what WSA's to put for + EADDRINUSE */ + +#define SOCKET_EINTR(return_value) \ + (((return_value) == SOCKET_ERROR) && \ + ((errno == EINTR) || \ + (errno == WSAECONNABORTED) || \ + (errno == WSAECONNRESET) )) +#define SOCKET_EADDRINUSE(return_value) \ + (((return_value) == SOCKET_ERROR) && \ + ((errno == WSAEADDRINUSE) )) +#define SOCKET_EADDRNOTAVAIL(return_value) \ + (((return_value) == SOCKET_ERROR) && \ + ((errno == WSAEADDRNOTAVAIL) )) +#endif + +#ifdef HAVE_SENDFILE + +struct sendfile_ring_elt { + struct sendfile_ring_elt *next; /* next element in the ring */ + int fildes; /* the file descriptor of the source + file */ + off_t offset; /* the offset from the beginning of + the file for this send */ + size_t length; /* the number of bytes to send - + this is redundant with the + send_size variable but I decided + to include it anyway */ + struct iovec *hdtrl; /* a pointer to a header/trailer + that we do not initially use and + so should be set to NULL when the + ring is setup. */ + int flags; /* the flags to pass to sendfile() - + presently unused and should be + set to zero when the ring is + setup. */ +}; + +#endif /* HAVE_SENDFILE */ + + /* the diferent codes to denote the type of CPU utilization */ + /* methods used */ +#define CPU_UNKNOWN 0 +#define HP_IDLE_COUNTER 1 +#define PSTAT 2 +#define TIMES 3 +#define LOOPER 4 +#define GETRUSAGE 5 +#define NT_METHOD 6 +#define KSTAT 7 +#define PROC_STAT 8 +#define SYSCTL 9 +#define PERFSTAT 10 +#define KSTAT_10 11 +#define OSX 12 + +#define BADCH ('?') + +#ifndef NETLIB +#ifdef WIN32 +#ifndef _GETOPT_ +#define _GETOPT_ + +int getopt(int argc, char **argv, char *optstring); + +extern char *optarg; /* returned arg to go with this option */ +extern int optind; /* index to next argv element to process */ +extern int opterr; /* should error messages be printed? */ +extern int optopt; /* */ + +#endif /* _GETOPT_ */ + +extern SOCKET win_kludge_socket, win_kludge_socket2; +#endif /* WIN32 */ + +extern int local_proc_affinity, remote_proc_affinity; + +/* these are to allow netperf to be run easily through those evil, + end-to-end breaking things known as firewalls */ +extern char local_data_port[10]; +extern char remote_data_port[10]; + +extern char *local_data_address; +extern char *remote_data_address; + +extern int local_data_family; +extern int remote_data_family; + +extern union netperf_request_struct netperf_request; +extern union netperf_response_struct netperf_response; + +extern float lib_local_cpu_util; +extern float lib_elapsed; +extern float lib_local_maxrate; + +extern char libfmt; + +extern int cpu_method; +extern int lib_num_loc_cpus; +extern int lib_num_rem_cpus; +extern SOCKET server_sock; +extern int times_up; +extern FILE *where; +extern int loops_per_msec; +extern float lib_local_per_cpu_util[]; + +extern void netlib_init(); +extern int netlib_get_page_size(); +extern void install_signal_catchers(); +extern void establish_control(char hostname[], + char port[], + int af, + char local_hostname[], + char local_port[], + int local_af); +extern void shutdown_control(); +extern void init_stat(); +extern void send_request(); +extern void recv_response(); +extern void send_response(); +extern void recv_request(); +extern void dump_request(); +extern void dump_addrinfo(FILE *dumploc, struct addrinfo *info, + char *host, char *port, int family); +extern void start_timer(int time); +extern void stop_timer(); +extern void cpu_start(int measure_cpu); +extern void cpu_stop(int measure_cpu, float *elapsed); +extern void calculate_confidence(int confidence_iterations, + float time, + double result, + float loc_cpu, + float rem_cpu, + float loc_sd, + float rem_sd); +extern 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); +extern void display_confidence(); +extern void set_sock_buffer(SOCKET sd, + enum sock_buffer which, + int requested_size, + int *effective_sizep); +extern char *format_units(); + +extern char *inet_ftos(int family); +extern char *inet_ttos(int type); +extern char *inet_ptos(int protocol); +extern double ntohd(double net_double); +extern double htond(double host_double); +extern int inet_nton(int af, const void *src, char *dst, int cnt); +extern void libmain(); +extern double calc_thruput(double units_received); +extern double calc_thruput_interval(double units_received,double elapsed); +extern double calc_thruput_omni(double units_received); +extern double calc_thruput_interval_omni(double units_received,double elapsed); +extern float calibrate_local_cpu(float local_cpu_rate); +extern float calibrate_remote_cpu(); +extern void bind_to_specific_processor(int processor_affinity,int use_cpu_map); +extern int set_nonblock (SOCKET sock); + +#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. */ + +extern int msec_sleep( int msecs ); +#endif /* WIN32 */ +extern float calc_cpu_util(float elapsed_time); +extern float calc_service_demand(double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus); +extern float calc_service_demand_trans(double units_sent, + float elapsed_time, + float cpu_utilization, + int num_cpus); +#if defined(__hpux) +extern void catcher(int, siginfo_t *,void *); +#else +extern void catcher(int); +#endif /* __hpux */ +extern struct ring_elt *allocate_buffer_ring(); +extern void access_buffer(char *buffer_ptr, + int length, + int dirty_count, + int clean_count); + +#ifdef HAVE_ICSC_EXS +extern struct ring_elt *allocate_exs_buffer_ring(); +#endif /* HAVE_ICSC_EXS */ +#ifdef HAVE_SENDFILE +extern struct sendfile_ring_elt *alloc_sendfile_buf_ring(); +#endif /* HAVE_SENDFILE */ +#ifdef WANT_DLPI +/* it seems that AIX in its finite wisdom has some bogus define in an + include file which defines "rem_addr" which then screws-up this extern + unless we change the names to protect the guilty. reported by Eric + Jones */ +extern int dl_connect(int fd, unsigned char *remote_addr, int remote_addr_len); +extern int dl_bind(int fd, int sap, int mode, char *dlsap_ptr, int *dlsap_len); +extern int dl_open(char devfile[], int ppa); +#endif /* WANT_DLPI */ +extern char format_cpu_method(int method); +extern unsigned int convert(char *string); +extern unsigned int convert_timespec(char *string); + +#ifdef WANT_INTERVALS +extern void start_itimer(unsigned int interval_len_msec); +#endif + /* these are all for the confidence interval stuff */ +extern double confidence; + +#endif + +#ifdef WIN32 +#define close(x) closesocket(x) +#define strcasecmp(a,b) _stricmp(a,b) +#define getpid() ((int)GetCurrentProcessId()) +#endif + +#ifdef WIN32 +#if 0 +/* Should really use safe string functions; but not for now... */ +#include <strsafe.h> +/* Microsoft has deprecated _snprintf; it isn't guarenteed to null terminate the result buffer. */ +/* They want us to call StringCbPrintf instead; it always null terminates the string. */ +#endif + +#define snprintf _snprintf +#endif + +/* Define a macro to align a buffer with an offset from a power of 2 + boundary. */ + +#ifndef WIN32 +#define ULONG_PTR unsigned long +#endif + +#define ALIGN_BUFFER(BufPtr, Align, Offset) \ + (char *)(( (ULONG_PTR)(BufPtr) + \ + (ULONG_PTR) (Align) -1) & \ + ~((ULONG_PTR) (Align) - 1)) + (ULONG_PTR)(Offset) + + /* if your system has bcopy and bzero, include it here, otherwise, we */ + /* will try to use memcpy aand memset. fix from Bruce Barnett @ GE. */ +#if defined(hpux) || defined (__VMS) +#define HAVE_BCOPY +#define HAVE_BZERO +#endif + +#ifdef WIN32 +#define HAVE_MIN +#else +#define _stdcall +#define _cdecl +#endif + +#ifndef HAVE_BCOPY +#define bcopy(s,d,h) memcpy((d),(s),(h)) +#endif /* HAVE_BCOPY */ + +#ifndef HAVE_BZERO +#define bzero(p,h) memset((p),0,(h)) +#endif /* HAVE_BZERO */ + +#ifndef HAVE_MIN +#define min(a,b) ((a < b) ? a : b) +#endif /* HAVE_MIN */ + +#ifdef USE_PERFSTAT +# include <libperfstat.h> +#endif diff --git a/netperf.c b/netperf.c new file mode 100644 index 0000000..84717ac --- /dev/null +++ b/netperf.c @@ -0,0 +1,284 @@ +/* + + Copyright (C) 1993-2007 Hewlett-Packard Company + ALL RIGHTS RESERVED. + + The enclosed software and documentation includes copyrighted works + of Hewlett-Packard Co. For as long as you comply with the following + limitations, you are hereby authorized to (i) use, reproduce, and + modify the software and documentation, and to (ii) distribute the + software and documentation, including modifications, for + non-commercial purposes only. + + 1. The enclosed software and documentation is made available at no + charge in order to advance the general development of + high-performance networking products. + + 2. You may not delete any copyright notices contained in the + software or documentation. All hard copies, and copies in + source code or object code form, of the software or + documentation (including modifications) must contain at least + one of the copyright notices. + + 3. The enclosed software and documentation has not been subjected + to testing and quality control and is not a Hewlett-Packard Co. + product. At a future time, Hewlett-Packard Co. may or may not + offer a version of the software and documentation as a product. + + 4. THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS". + HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE, + REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR + DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL + PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR + DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES, + EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE + DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + 5. HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY + DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES + (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION, + MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION. + +*/ +char netperf_id[]="\ +@(#)netperf.c (c) Copyright 1993-2007 Hewlett-Packard Company. Version 2.4.3"; + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +/* FreeBSD doesn't like socket.h before types are set. */ +#if __FreeBSD__ +# include <sys/types.h> +#endif + +#ifndef WIN32 +/* this should only be temporary */ +#include <sys/socket.h> +#endif + +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +#endif /* WIN32 */ + +#include "netsh.h" +#include "netlib.h" +#include "nettest_bsd.h" + +#ifdef WANT_UNIX +#include "nettest_unix.h" +#endif /* WANT_UNIX */ + +#ifdef WANT_XTI +#include "nettest_xti.h" +#endif /* WANT_XTI */ + +#ifdef WANT_DLPI +#include "nettest_dlpi.h" +#endif /* WANT_DLPI */ + +#ifdef WANT_SDP +#include "nettest_sdp.h" +#endif + +/* The DNS tests have been removed from netperf2. Those wanting to do + DNS_RR tests should use netperf4 instead. */ + +#ifdef DO_DNS +#error DNS tests have been removed from netperf. Use netperf4 instead +#endif /* DO_DNS */ + +#ifdef WANT_SCTP +#include "nettest_sctp.h" +#endif + + /* this file contains the main for the netperf program. all the other */ + /* routines can be found in the file netsh.c */ + + +int _cdecl +main(int argc, char *argv[]) +{ + +#ifdef WIN32 + WSADATA wsa_data ; + + /* Initialize the winsock lib ( version 2.2 ) */ + if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){ + printf("WSAStartup() failed : %d\n", GetLastError()) ; + return 1 ; + } +#endif /* WIN32 */ + + netlib_init(); + set_defaults(); + scan_cmd_line(argc,argv); + + if (debug) { + dump_globals(); + install_signal_catchers(); + } + + if (debug) { + printf("remotehost is %s and port %s\n",host_name,test_port); + fflush(stdout); + } + + + if (!no_control) { + establish_control(host_name,test_port,address_family, + local_host_name,local_test_port,local_address_family); + } + + if (strcasecmp(test_name,"TCP_STREAM") == 0) { + send_tcp_stream(host_name); + } + else if (strcasecmp(test_name,"TCP_MAERTS") == 0) { + send_tcp_maerts(host_name); + } +#ifdef HAVE_ICSC_EXS + else if (strcasecmp(test_name,"EXS_TCP_STREAM") == 0) { + send_exs_tcp_stream(host_name); + } +#endif /* HAVE_ICSC_EXS */ +#ifdef HAVE_SENDFILE + else if (strcasecmp(test_name,"TCP_SENDFILE") == 0) { + sendfile_tcp_stream(host_name); + } +#endif /* HAVE_SENDFILE */ + else if (strcasecmp(test_name,"TCP_RR") == 0) { + send_tcp_rr(host_name); + } + else if (strcasecmp(test_name,"TCP_CRR") == 0) { + send_tcp_conn_rr(host_name); + } + else if (strcasecmp(test_name,"TCP_CC") == 0) { + send_tcp_cc(host_name); + } +#ifdef DO_1644 + else if (strcasecmp(test_name,"TCP_TRR") == 0) { + send_tcp_tran_rr(host_name); + } +#endif /* DO_1644 */ +#ifdef DO_NBRR + else if (strcasecmp(test_name,"TCP_NBRR") == 0) { + send_tcp_nbrr(host_name); + } +#endif /* DO_NBRR */ + else if (strcasecmp(test_name,"UDP_STREAM") == 0) { + send_udp_stream(host_name); + } + else if (strcasecmp(test_name,"UDP_RR") == 0) { + send_udp_rr(host_name); + } + else if (strcasecmp(test_name,"LOC_CPU") == 0) { + loc_cpu_rate(); + } + else if (strcasecmp(test_name,"REM_CPU") == 0) { + rem_cpu_rate(); + } +#ifdef WANT_DLPI + else if (strcasecmp(test_name,"DLCO_RR") == 0) { + send_dlpi_co_rr(host_name); + } + else if (strcasecmp(test_name,"DLCL_RR") == 0) { + send_dlpi_cl_rr(host_name); + } + else if (strcasecmp(test_name,"DLCO_STREAM") == 0) { + send_dlpi_co_stream(host_name); + } + else if (strcasecmp(test_name,"DLCL_STREAM") == 0) { + send_dlpi_cl_stream(host_name); + } +#endif /* WANT_DLPI */ +#ifdef WANT_UNIX + else if (strcasecmp(test_name,"STREAM_RR") == 0) { + send_stream_rr(host_name); + } + else if (strcasecmp(test_name,"DG_RR") == 0) { + send_dg_rr(host_name); + } + else if (strcasecmp(test_name,"STREAM_STREAM") == 0) { + send_stream_stream(host_name); + } + else if (strcasecmp(test_name,"DG_STREAM") == 0) { + send_dg_stream(host_name); + } +#endif /* WANT_UNIX */ +#ifdef WANT_XTI + else if (strcasecmp(test_name,"XTI_TCP_STREAM") == 0) { + send_xti_tcp_stream(host_name); + } + else if (strcasecmp(test_name,"XTI_TCP_RR") == 0) { + send_xti_tcp_rr(host_name); + } + else if (strcasecmp(test_name,"XTI_UDP_STREAM") == 0) { + send_xti_udp_stream(host_name); + } + else if (strcasecmp(test_name,"XTI_UDP_RR") == 0) { + send_xti_udp_rr(host_name); + } +#endif /* WANT_XTI */ + +#ifdef WANT_SCTP + else if (strcasecmp(test_name, "SCTP_STREAM") == 0) { + send_sctp_stream(host_name); + } + else if (strcasecmp(test_name, "SCTP_RR") == 0) { + send_sctp_rr(host_name); + } + else if (strcasecmp(test_name, "SCTP_STREAM_MANY") == 0) { + send_sctp_stream_1toMany(host_name); + } + else if (strcasecmp(test_name, "SCTP_RR_MANY") == 0) { + send_sctp_stream_1toMany(host_name); + } +#endif + +#ifdef DO_DNS + else if (strcasecmp(test_name,"DNS_RR") == 0) { + fprintf(stderr, + "DNS tests can now be found in netperf4.\n"); + fflush(stderr); + exit(-1); + } +#endif /* DO_DNS */ +#ifdef WANT_SDP + else if (strcasecmp(test_name,"SDP_STREAM") == 0) { + send_sdp_stream(host_name); + } + else if (strcasecmp(test_name,"SDP_MAERTS") == 0) { + send_sdp_maerts(host_name); + } + else if (strcasecmp(test_name,"SDP_RR") == 0) { + send_sdp_rr(host_name); + } +#endif /* WANT_SDP */ + else { + printf("The test you requested is unknown to this netperf.\n"); + printf("Please verify that you have the correct test name, \n"); + printf("and that test family has been compiled into this netperf.\n"); + exit(1); + } + + if (!no_control) { + shutdown_control(); + } + +#ifdef WIN32 + /* Cleanup the winsock lib */ + WSACleanup(); +#endif + + return(0); +} + + diff --git a/netperf_version.h b/netperf_version.h new file mode 100644 index 0000000..b2b9fdc --- /dev/null +++ b/netperf_version.h @@ -0,0 +1 @@ +#define NETPERF_VERSION "2.4.4" diff --git a/netserver.c b/netserver.c new file mode 100644 index 0000000..02be3ce --- /dev/null +++ b/netserver.c @@ -0,0 +1,1022 @@ +/* + + Copyright (C) 1993-2007 Hewlett-Packard Company + ALL RIGHTS RESERVED. + + The enclosed software and documentation includes copyrighted works + of Hewlett-Packard Co. For as long as you comply with the following + limitations, you are hereby authorized to (i) use, reproduce, and + modify the software and documentation, and to (ii) distribute the + software and documentation, including modifications, for + non-commercial purposes only. + + 1. The enclosed software and documentation is made available at no + charge in order to advance the general development of + high-performance networking products. + + 2. You may not delete any copyright notices contained in the + software or documentation. All hard copies, and copies in + source code or object code form, of the software or + documentation (including modifications) must contain at least + one of the copyright notices. + + 3. The enclosed software and documentation has not been subjected + to testing and quality control and is not a Hewlett-Packard Co. + product. At a future time, Hewlett-Packard Co. may or may not + offer a version of the software and documentation as a product. + + 4. THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS". + HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE, + REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR + DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL + PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR + DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES, + EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE + DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + 5. HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY + DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES + (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION, + MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION. + +*/ + +#include "netperf_version.h" + +char netserver_id[]="\ +@(#)netserver.c (c) Copyright 1993-2007 Hewlett-Packard Co. Version 2.4.3"; + + /***********************************************************************/ + /* */ + /* netserver.c */ + /* */ + /* This is the server side code for the netperf test package. It */ + /* will operate either stand-alone, or as a child of inetd. In this */ + /* way, we insure that it can be installed on systems with or without */ + /* root permissions (editing inetd.conf). Essentially, this code is */ + /* the analog to the netsh.c code. */ + /* */ + /***********************************************************************/ + + +/************************************************************************/ +/* */ +/* Global include files */ +/* */ +/************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_LIMITS_H +# include <limits.h> +#endif +#include <sys/types.h> +#include <stdio.h> +#ifndef WIN32 +#include <errno.h> +#include <signal.h> +#endif +#if !defined(WIN32) && !defined(__VMS) +#include <sys/ipc.h> +#endif /* !defined(WIN32) && !defined(__VMS) */ +#include <fcntl.h> +#ifdef WIN32 +#include <time.h> +#include <winsock2.h> +#define netperf_socklen_t socklen_t +/* we need to find some other way to decide to include ws2 */ +/* if you are trying to compile on Windows 2000 or NT 4 you will */ +/* probably have to define DONT_IPV6 */ +#ifndef DONT_IPV6 +#include <ws2tcpip.h> +#endif /* DONT_IPV6 */ +#include <windows.h> +#define sleep(x) Sleep((x)*1000) +#else +#ifndef MPE +#include <sys/time.h> +#endif /* MPE */ +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <netdb.h> +#include <unistd.h> +#ifndef DONT_WAIT +#include <sys/wait.h> +#endif /* DONT_WAIT */ +#endif /* WIN32 */ +#include <stdlib.h> +#ifdef __VMS +#include <tcpip$inetdef.h> +#include <unixio.h> +#endif /* __VMS */ +#include "netlib.h" +#include "nettest_bsd.h" + +#ifdef WANT_UNIX +#include "nettest_unix.h" +#endif /* WANT_UNIX */ + +#ifdef WANT_DLPI +#include "nettest_dlpi.h" +#endif /* WANT_DLPI */ + +#ifdef WANT_SCTP +#include "nettest_sctp.h" +#endif + +#include "netsh.h" + +#ifndef DEBUG_LOG_FILE +#ifndef WIN32 +#define DEBUG_LOG_FILE "/tmp/netperf.debug" +#else +#define DEBUG_LOG_FILE "c:\\temp\\netperf.debug" +#endif /* WIN32 */ +#endif /* DEBUG_LOG_FILE */ + + /* some global variables */ + +FILE *afp; +char listen_port[10]; +extern char *optarg; +extern int optind, opterr; + +#ifndef WIN32 +#define SERVER_ARGS "dL:n:p:v:V46" +#else +#define SERVER_ARGS "dL:n:p:v:V46I:i:" +#endif + +/* perhaps one day we will use this as part of only opening a debug + log file if debug is set, of course we have to be wary of the base + use of "where" and so probably always need "where" pointing + "somewhere" or other. */ +void +open_debug_file() +{ +#ifndef WIN32 +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif + char FileName[PATH_MAX]; /* for opening the debug log file */ + strcpy(FileName, DEBUG_LOG_FILE); + + if (where != NULL) fflush(where); + + snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid()); + if ((where = fopen(FileName, "w")) == NULL) { + perror("netserver: debug file"); + exit(1); + } + + chmod(FileName,0644); +#endif + +} + /* This routine implements the "main event loop" of the netperf */ + /* server code. Code above it will have set-up the control connection */ + /* so it can just merrily go about its business, which is to */ + /* "schedule" performance tests on the server. */ + +void +process_requests() +{ + + float temp_rate; + + if (debug) open_debug_file(); + + + while (1) { + recv_request(); + + switch (netperf_request.content.request_type) { + + case DEBUG_ON: + netperf_response.content.response_type = DEBUG_OK; + /* dump_request already present in recv_request; redundant? */ + if (!debug) { + debug++; + open_debug_file(); + dump_request(); + } + send_response(); + break; + + case DEBUG_OFF: + if (debug) + debug--; + netperf_response.content.response_type = DEBUG_OK; + send_response(); + /* +SAF why??? */ + if (!debug) + { + fclose(where); +#if !defined(WIN32) && !defined(MPE) && !defined(__VMS) + /* For Unix: reopen the debug write file descriptor to "/dev/null" */ + /* and redirect stdout to it. */ + fflush (stdout); + where = fopen ("/dev/null", "w"); + if (where == NULL) + { + perror ("netserver: reopening debug fp for writing: /dev/null"); + exit (1); + } + if (close (STDOUT_FILENO) == -1) + { + perror ("netserver: closing stdout file descriptor"); + exit (1); + } + if (dup (fileno (where)) == -1) + { + perror ("netserver: duplicate /dev/null write file descr. to stdout"); + exit (1); + } +#endif /* !WIN32 !MPE !__VMS */ + } + break; + + case CPU_CALIBRATE: + netperf_response.content.response_type = CPU_CALIBRATE; + temp_rate = calibrate_local_cpu(0.0); + bcopy((char *)&temp_rate, + (char *)netperf_response.content.test_specific_data, + sizeof(temp_rate)); + bcopy((char *)&lib_num_loc_cpus, + (char *)netperf_response.content.test_specific_data + sizeof(temp_rate), + sizeof(lib_num_loc_cpus)); + if (debug) { + fprintf(where,"netserver: sending CPU information:"); + fprintf(where,"rate is %g num cpu %d\n",temp_rate,lib_num_loc_cpus); + fflush(where); + } + + /* we need the cpu_start, cpu_stop in the looper case to kill the */ + /* child proceses raj 7/95 */ + +#ifdef USE_LOOPER + cpu_start(1); + cpu_stop(1,&temp_rate); +#endif /* USE_LOOPER */ + + send_response(); + break; + + case DO_TCP_STREAM: + recv_tcp_stream(); + break; + + case DO_TCP_MAERTS: + recv_tcp_maerts(); + break; + + case DO_TCP_RR: + recv_tcp_rr(); + break; + + case DO_TCP_CRR: + recv_tcp_conn_rr(); + break; + + case DO_TCP_CC: + recv_tcp_cc(); + break; + +#ifdef DO_1644 + case DO_TCP_TRR: + recv_tcp_tran_rr(); + break; +#endif /* DO_1644 */ + +#ifdef DO_NBRR + case DO_TCP_NBRR: + recv_tcp_nbrr(); + break; +#endif /* DO_NBRR */ + + case DO_UDP_STREAM: + recv_udp_stream(); + break; + + case DO_UDP_RR: + recv_udp_rr(); + break; + +#ifdef WANT_DLPI + + case DO_DLPI_CO_RR: + recv_dlpi_co_rr(); + break; + + case DO_DLPI_CL_RR: + recv_dlpi_cl_rr(); + break; + + case DO_DLPI_CO_STREAM: + recv_dlpi_co_stream(); + break; + + case DO_DLPI_CL_STREAM: + recv_dlpi_cl_stream(); + break; + +#endif /* WANT_DLPI */ + +#ifdef WANT_UNIX + + case DO_STREAM_STREAM: + recv_stream_stream(); + break; + + case DO_STREAM_RR: + recv_stream_rr(); + break; + + case DO_DG_STREAM: + recv_dg_stream(); + break; + + case DO_DG_RR: + recv_dg_rr(); + break; + +#endif /* WANT_UNIX */ + +#ifdef WANT_XTI + case DO_XTI_TCP_STREAM: + recv_xti_tcp_stream(); + break; + + case DO_XTI_TCP_RR: + recv_xti_tcp_rr(); + break; + + case DO_XTI_UDP_STREAM: + recv_xti_udp_stream(); + break; + + case DO_XTI_UDP_RR: + recv_xti_udp_rr(); + break; + +#endif /* WANT_XTI */ + +#ifdef WANT_SCTP + case DO_SCTP_STREAM: + recv_sctp_stream(); + break; + + case DO_SCTP_STREAM_MANY: + recv_sctp_stream_1toMany(); + break; + + case DO_SCTP_RR: + recv_sctp_rr(); + break; + + case DO_SCTP_RR_MANY: + recv_sctp_rr_1toMany(); + break; +#endif + +#ifdef WANT_SDP + case DO_SDP_STREAM: + recv_sdp_stream(); + break; + + case DO_SDP_MAERTS: + recv_sdp_maerts(); + break; + + case DO_SDP_RR: + recv_sdp_rr(); + break; +#endif + + default: + fprintf(where,"unknown test number %d\n", + netperf_request.content.request_type); + fflush(where); + netperf_response.content.serv_errno=998; + send_response(); + break; + + } + } +} + +/* + set_up_server() + + set-up the server listen socket. we only call this routine if the + user has specified a port number on the command line or we believe we + are not a child of inetd or its platform-specific equivalent */ + +/*KC*/ + +void +set_up_server(char hostname[], char port[], int af) +{ + + struct addrinfo hints; + struct addrinfo *local_res; + struct addrinfo *local_res_temp; + + struct sockaddr_storage peeraddr; + netperf_socklen_t peeraddr_len = sizeof(peeraddr); + + SOCKET server_control; + int on=1; + int count; + int error; + int not_listening; + +#if !defined(WIN32) && !defined(MPE) && !defined(__VMS) + FILE *rd_null_fp; /* Used to redirect from "/dev/null". */ + FILE *wr_null_fp; /* Used to redirect to "/dev/null". */ +#endif /* !WIN32 !MPE !__VMS */ + + if (debug) { + fprintf(stderr, + "set_up_server called with host '%s' port '%s' remfam %d\n", + hostname, + port, + af); + fflush(stderr); + } + + memset(&hints,0,sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + count = 0; + do { + error = getaddrinfo((char *)hostname, + (char *)port, + &hints, + &local_res); + count += 1; + if (error == EAI_AGAIN) { + if (debug) { + fprintf(stderr,"Sleeping on getaddrinfo EAI_AGAIN\n"); + fflush(stderr); + } + sleep(1); + } + } while ((error == EAI_AGAIN) && (count <= 5)); + + if (error) { + fprintf(stderr, + "set_up_server: could not resolve remote '%s' port '%s' af %d", + hostname, + port, + af); + fprintf(stderr,"\n\tgetaddrinfo returned %d %s\n", + error, + gai_strerror(error)); + exit(-1); + } + + if (debug) { + dump_addrinfo(stderr, local_res, hostname, port, af); + } + + not_listening = 1; + local_res_temp = local_res; + + while((local_res_temp != NULL) && (not_listening)) { + + fprintf(stderr, + "Starting netserver at port %s\n", + port); + + server_control = socket(local_res_temp->ai_family,SOCK_STREAM,0); + + if (server_control == INVALID_SOCKET) { + perror("set_up_server could not allocate a socket"); + exit(-1); + } + + /* happiness and joy, keep going */ + if (setsockopt(server_control, + SOL_SOCKET, + SO_REUSEADDR, + (char *)&on , + sizeof(on)) == SOCKET_ERROR) { + if (debug) { + perror("warning: set_up_server could not set SO_REUSEADDR"); + } + } + /* still happy and joyful */ + + if ((bind (server_control, + local_res_temp->ai_addr, + local_res_temp->ai_addrlen) != SOCKET_ERROR) && + (listen (server_control,5) != SOCKET_ERROR)) { + not_listening = 0; + break; + } + else { + /* we consider a bind() or listen() failure a transient and try + the next address */ + if (debug) { + perror("warning: set_up_server failed a bind or listen call\n"); + } + local_res_temp = local_res_temp->ai_next; + continue; + } + } + + if (not_listening) { + fprintf(stderr, + "set_up_server could not establish a listen endpoint for %s port %s with family %s\n", + host_name, + port, + inet_ftos(af)); + fflush(stderr); + exit(-1); + } + else { + printf("Starting netserver at hostname %s port %s and family %s\n", + hostname, + port, + inet_ftos(af)); + } + + /* + setpgrp(); + */ + +#if !defined(WIN32) && !defined(MPE) && !defined(__VMS) + /* Flush the standard I/O file descriptors before forking. */ + fflush (stdin); + fflush (stdout); + fflush (stderr); + switch (fork()) + { + case -1: + perror("netperf server error"); + exit(1); + + case 0: + /* Redirect stdin from "/dev/null". */ + rd_null_fp = fopen ("/dev/null", "r"); + if (rd_null_fp == NULL) + { + perror ("netserver: opening for reading: /dev/null"); + exit (1); + } + if (close (STDIN_FILENO) == -1) + { + perror ("netserver: closing stdin file descriptor"); + exit (1); + } + if (dup (fileno (rd_null_fp)) == -1) + { + perror ("netserver: duplicate /dev/null read file descr. to stdin"); + exit (1); + } + + /* Redirect stdout to the debug write file descriptor. */ + if (close (STDOUT_FILENO) == -1) + { + perror ("netserver: closing stdout file descriptor"); + exit (1); + } + if (dup (fileno (where)) == -1) + { + perror ("netserver: duplicate the debug write file descr. to stdout"); + exit (1); + } + + /* Redirect stderr to "/dev/null". */ + wr_null_fp = fopen ("/dev/null", "w"); + if (wr_null_fp == NULL) + { + perror ("netserver: opening for writing: /dev/null"); + exit (1); + } + if (close (STDERR_FILENO) == -1) + { + perror ("netserver: closing stderr file descriptor"); + exit (1); + } + if (dup (fileno (wr_null_fp)) == -1) + { + perror ("netserver: dupicate /dev/null write file descr. to stderr"); + exit (1); + } + +#ifndef NO_SETSID + setsid(); +#else + setpgrp(); +#endif /* NO_SETSID */ + + /* some OS's have SIGCLD defined as SIGCHLD */ +#ifndef SIGCLD +#define SIGCLD SIGCHLD +#endif /* SIGCLD */ + + signal(SIGCLD, SIG_IGN); + +#endif /* !WIN32 !MPE !__VMS */ + + for (;;) + { + if ((server_sock=accept(server_control, + (struct sockaddr *)&peeraddr, + &peeraddr_len)) == INVALID_SOCKET) + { + printf("server_control: accept failed errno %d\n",errno); + exit(1); + } +#if defined(MPE) || defined(__VMS) + /* + * Since we cannot fork this process , we cant fire any threads + * as they all share the same global data . So we better allow + * one request at at time + */ + process_requests() ; +#elif WIN32 + { + BOOL b; + char cmdline[80]; + PROCESS_INFORMATION pi; + STARTUPINFO si; + int i; + + memset(&si, 0 , sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + + /* Pass the server_sock as stdin for the new process. */ + /* Hopefully this will continue to be created with the OBJ_INHERIT attribute. */ + si.hStdInput = (HANDLE)server_sock; + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + + /* Build cmdline for child process */ + strcpy(cmdline, program); + if (verbosity > 1) { + snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -v %d", verbosity); + } + for (i=0; i < debug; i++) { + snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -d"); + } + snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -I %x", (int)(UINT_PTR)server_sock); + snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)server_control); + snprintf(&cmdline[strlen(cmdline)], sizeof(cmdline) - strlen(cmdline), " -i %x", (int)(UINT_PTR)where); + + b = CreateProcess(NULL, /* Application Name */ + cmdline, + NULL, /* Process security attributes */ + NULL, /* Thread security attributes */ + TRUE, /* Inherit handles */ + 0, /* Creation flags PROCESS_QUERY_INFORMATION, */ + NULL, /* Enviornment */ + NULL, /* Current directory */ + &si, /* StartupInfo */ + &pi); + if (!b) + { + perror("CreateProcessfailure: "); + exit(1); + } + + /* We don't need the thread or process handles any more; let them */ + /* go away on their own timeframe. */ + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + /* And close the server_sock since the child will own it. */ + + close(server_sock); + } +#else + signal(SIGCLD, SIG_IGN); + + switch (fork()) + { + case -1: + /* something went wrong */ + exit(1); + case 0: + /* we are the child process */ + close(server_control); + process_requests(); + exit(0); + break; + default: + /* we are the parent process */ + close(server_sock); + /* we should try to "reap" some of our children. on some */ + /* systems they are being left as defunct processes. we */ + /* will call waitpid, looking for any child process, */ + /* with the WNOHANG feature. when waitpid return a zero, */ + /* we have reaped all the children there are to reap at */ + /* the moment, so it is time to move on. raj 12/94 */ +#ifndef DONT_WAIT +#ifdef NO_SETSID + /* Only call "waitpid()" if "setsid()" is not used. */ + while(waitpid(-1, NULL, WNOHANG) > 0) { } +#endif /* NO_SETSID */ +#endif /* DONT_WAIT */ + break; + } +#endif /* !WIN32 !MPE !__VMS */ + } /*for*/ +#if !defined(WIN32) && !defined(MPE) && !defined(__VMS) + break; /*case 0*/ + + default: + exit (0); + + } +#endif /* !WIN32 !MPE !__VMS */ +} + +#ifdef WIN32 + + /* With Win2003, WinNT's POSIX subsystem is gone and hence so is */ + /* fork. */ + + /* But hopefully the kernel support will continue to exist for some */ + /* time. */ + + /* We are not counting on the child address space copy_on_write */ + /* support, since it isn't exposed except through the NT native APIs */ + /* (which is not public). */ + + /* We will try to use the InheritHandles flag in CreateProcess. It */ + /* is in the public API, though it is documented as "must be FALSE". */ + + /* So where we would have forked, we will now create a new process. */ + /* I have added a set of command line switches to specify a list of */ + /* handles that the child should close since they shouldn't have */ + /* been inherited ("-i#"), and a single switch to specify the handle */ + /* for the server_sock ("I#"). */ + + /* A better alternative would be to re-write NetPerf to be */ + /* multi-threaded; i.e., move all of the various NetPerf global */ + /* variables in to thread specific structures. But this is a bigger */ + /* effort than I want to tackle at this time. (And I doubt that the */ + /* HP-UX author sees value in this effort). */ + +#endif + +int _cdecl +main(int argc, char *argv[]) +{ + + int c; + int not_inetd = 0; +#ifdef WIN32 + BOOL child = FALSE; +#endif + char arg1[BUFSIZ], arg2[BUFSIZ]; +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif + char FileName[PATH_MAX]; /* for opening the debug log file */ + + struct sockaddr name; + netperf_socklen_t namelen = sizeof(name); + + +#ifdef WIN32 + WSADATA wsa_data ; + + /* Initialize the winsock lib ( version 2.2 ) */ + if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){ + printf("WSAStartup() failed : %d\n", GetLastError()) ; + return 1 ; + } +#endif /* WIN32 */ + + /* Save away the program name */ + program = (char *)malloc(strlen(argv[0]) + 1); + if (program == NULL) { + printf("malloc(%d) failed!\n", strlen(argv[0]) + 1); + return 1 ; + } + strcpy(program, argv[0]); + + netlib_init(); + + /* Scan the command line to see if we are supposed to set-up our own */ + /* listen socket instead of relying on inetd. */ + + /* first set a copy of initial values */ + strncpy(local_host_name,"0.0.0.0",sizeof(local_host_name)); + local_address_family = AF_UNSPEC; + strncpy(listen_port,TEST_PORT,sizeof(listen_port)); + + while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_netserver_usage(); + exit(1); + case 'd': + /* we want to set the debug file name sometime */ + debug++; + break; + case 'L': + not_inetd = 1; + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + strncpy(local_host_name,arg1,sizeof(local_host_name)); + } + if (arg2[0]) { + local_address_family = parse_address_family(arg2); + /* if only the address family was set, we may need to set the + local_host_name accordingly. since our defaults are IPv4 + this should only be necessary if we have IPv6 support raj + 2005-02-07 */ +#if defined (AF_INET6) + if (!arg1[0]) { + strncpy(local_host_name,"::0",sizeof(local_host_name)); + } +#endif + } + break; + case 'n': + shell_num_cpus = atoi(optarg); + if (shell_num_cpus > MAXCPUS) { + fprintf(stderr, + "netserver: This version can only support %d CPUs. Please", + MAXCPUS); + fprintf(stderr, + " increase MAXCPUS in netlib.h and recompile.\n"); + fflush(stderr); + exit(1); + } + break; + case 'p': + /* we want to open a listen socket at a */ + /* specified port number */ + strncpy(listen_port,optarg,sizeof(listen_port)); + not_inetd = 1; + break; + case '4': + local_address_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + local_address_family = AF_INET6; + strncpy(local_host_name,"::0",sizeof(local_host_name)); +#else + local_address_family = AF_UNSPEC; +#endif + break; + case 'v': + /* say how much to say */ + verbosity = atoi(optarg); + break; + case 'V': + printf("Netperf version %s\n",NETPERF_VERSION); + exit(0); + break; +#ifdef WIN32 +/*+*+SAF */ + case 'I': + child = TRUE; + /* This is the handle we expect to inherit. */ + /*+*+SAF server_sock = (HANDLE)atoi(optarg); */ + break; + case 'i': + /* This is a handle we should NOT inherit. */ + /*+*+SAF CloseHandle((HANDLE)atoi(optarg)); */ + break; +#endif + + } + } + + /* +*+SAF I need a better way to find inherited handles I should close! */ + /* +*+SAF Use DuplicateHandle to force inheritable attribute (or reset it)? */ + +/* unlink(DEBUG_LOG_FILE); */ + + strcpy(FileName, DEBUG_LOG_FILE); + +#ifndef WIN32 + snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%d", getpid()); + if ((where = fopen(FileName, "w")) == NULL) { + perror("netserver: debug file"); + exit(1); + } +#else + { + + if (child) { + snprintf(&FileName[strlen(FileName)], sizeof(FileName) - strlen(FileName), "_%x", getpid()); + } + + /* Hopefully, by closing stdout & stderr, the subsequent + fopen calls will get mapped to the correct std handles. */ + fclose(stdout); + + if ((where = fopen(FileName, "w")) == NULL) { + perror("netserver: fopen of debug file as new stdout failed!"); + exit(1); + } + + fclose(stderr); + + if ((where = fopen(FileName, "w")) == NULL) { + fprintf(stdout, "fopen of debug file as new stderr failed!\n"); + exit(1); + } + } +#endif + +#ifndef WIN32 + chmod(DEBUG_LOG_FILE,0644); +#endif + +#if WIN32 + if (child) { + server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE); + } +#endif + + /* if we are not a child of an inetd or the like, then we should + open a socket and hang listens off of it. otherwise, we should go + straight into processing requests. the do_listen() routine will sit + in an infinite loop accepting connections and forking child + processes. the child processes will call process_requests */ + + /* If fd 0 is not a socket then assume we're not being called */ + /* from inetd and start server socket on the default port. */ + /* this enhancement comes from vwelch@ncsa.uiuc.edu (Von Welch) */ + if (not_inetd) { + /* the user specified a port number on the command line */ + set_up_server(local_host_name,listen_port,local_address_family); + } +#ifdef WIN32 + /* OK, with Win2003 WinNT's POSIX subsystem is gone, and hence so is */ + /* fork. But hopefully the kernel support will continue to exist */ + /* for some time. We are not counting on the address space */ + /* copy_on_write support, since it isn't exposed except through the */ + /* NT native APIs (which are not public). We will try to use the */ + /* InheritHandles flag in CreateProcess though since this is public */ + /* and is used for more than just POSIX so hopefully it won't go */ + /* away. */ + else if (TRUE) { + if (child) { + process_requests(); + } else { + strncpy(listen_port,TEST_PORT,sizeof(listen_port)); + set_up_server(local_host_name,listen_port,local_address_family); + } + } +#endif +#if !defined(__VMS) + else if (getsockname(0, &name, &namelen) == SOCKET_ERROR) { + /* we may not be a child of inetd */ + if (errno == ENOTSOCK) { + strncpy(listen_port,TEST_PORT,sizeof(listen_port)); + set_up_server(local_host_name,listen_port,local_address_family); + } + } +#endif /* !defined(__VMS) */ + else { + /* we are probably a child of inetd, or are being invoked via the + VMS auxilliarly server mechanism */ +#if !defined(__VMS) + server_sock = 0; +#else + if ( (server_sock = socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET ) + { + perror("Failed to grab aux server socket" ); + exit(1); + } + +#endif /* !defined(__VMS) */ + process_requests(); + } +#ifdef WIN32 + /* Cleanup the winsock lib */ + WSACleanup(); +#endif + + return(0); +} @@ -0,0 +1,1002 @@ +#include "netperf_version.h" + +char netsh_id[]="\ +@(#)netsh.c (c) Copyright 1993-2007 Hewlett-Packard Company. Version 2.4.3pre"; + + +/****************************************************************/ +/* */ +/* Global include files */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#ifndef WIN32 +#include <unistd.h> +#ifndef __VMS +#include <sys/ipc.h> +#endif /* __VMS */ +#endif /* WIN32 */ +#include <fcntl.h> +#ifndef WIN32 +#include <errno.h> +#include <signal.h> +#endif /* !WIN32 */ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + /* the following four includes should not be needed ?*/ +#ifndef WIN32 +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#else +#include <time.h> +#include <winsock2.h> +#define netperf_socklen_t socklen_t +#endif + +#ifndef STRINGS +#include <string.h> +#else /* STRINGS */ +#include <strings.h> +#endif /* STRINGS */ + +#ifdef WIN32 +extern int getopt(int , char **, char *) ; +#else +double atof(const char *); +#endif /* WIN32 */ + +/**********************************************************************/ +/* */ +/* Local Include Files */ +/* */ +/**********************************************************************/ + +#define NETSH +#include "netsh.h" +#include "netlib.h" +#include "nettest_bsd.h" + +#ifdef WANT_UNIX +#include "nettest_unix.h" +#ifndef WIN32 +#include "sys/socket.h" +#endif /* !WIN32 */ +#endif /* WANT_UNIX */ + +#ifdef WANT_XTI +#include "nettest_xti.h" +#endif /* WANT_XTI */ + +#ifdef WANT_DLPI +#include "nettest_dlpi.h" +#endif /* WANT_DLPI */ + +#ifdef WANT_SCTP +#include "nettest_sctp.h" +#endif + + +/************************************************************************/ +/* */ +/* Global constants and macros */ +/* */ +/************************************************************************/ + + /* Some of the args take optional parameters. Since we are using */ + /* getopt to parse the command line, we will tell getopt that they do */ + /* not take parms, and then look for them ourselves */ +#define GLOBAL_CMD_LINE_ARGS "A:a:b:B:CcdDf:F:H:hi:I:k:K:l:L:n:NO:o:P:p:rt:T:v:VW:w:46" + +/************************************************************************/ +/* */ +/* Extern variables */ +/* */ +/************************************************************************/ + +/* +extern int errno; +extern char *sys_errlist[ ]; +extern int sys_nerr; +*/ + +/************************************************************************/ +/* */ +/* Global variables */ +/* */ +/************************************************************************/ + +/* some names and such */ +char *program; /* program invocation name */ +char username[BUFSIZ]; /* login name of user */ +char cmd_file[BUFSIZ]; /* name of the commands file */ + +/* stuff to say where this test is going */ +char host_name[HOSTNAMESIZE]; /* remote host name or ip addr */ +char local_host_name[HOSTNAMESIZE]; /* local hostname or ip */ +char test_name[BUFSIZ]; /* which test to run */ +char test_port[PORTBUFSIZE]; /* where is the test waiting */ +char local_test_port[PORTBUFSIZE]; /* from whence we should start */ +int address_family; /* which address family remote */ +int local_address_family; /* which address family local */ + +/* the source of data for filling the buffers */ +char fill_file[BUFSIZ]; + +/* output controlling variables */ +int + debug, /* debugging level */ + print_headers, /* do/don't display headers */ + verbosity; /* verbosity level */ + +/* When specified with -B, this will be displayed at the end of the line + for output that does not include the test header. mostly this is + to help identify a specific netperf result when concurrent netperfs + are run. raj 2006-02-01 */ +char *result_brand = NULL; + +/* cpu variables */ +int + local_cpu_usage, /* you guessed it */ + remote_cpu_usage; /* still right ! */ + +float + local_cpu_rate, + remote_cpu_rate; + +int + shell_num_cpus=1; + +/* the end-test conditions for the tests - either transactions, bytes, */ +/* or time. different vars used for clarity - space is cheap ;-) */ +int + test_time, /* test ends by time */ + test_len_ticks, /* how many times will the timer go off before */ + /* the test is over? */ + test_bytes, /* test ends on byte count */ + test_trans; /* test ends on tran count */ + +/* the alignment conditions for the tests */ +int + local_recv_align, /* alignment for local receives */ + local_send_align, /* alignment for local sends */ + local_send_offset = 0, + local_recv_offset = 0, + remote_recv_align, /* alignment for remote receives */ + remote_send_align, /* alignment for remote sends */ + remote_send_offset = 0, + remote_recv_offset = 0; + +#if defined(WANT_INTERVALS) || defined(WANT_DEMO) +int + interval_usecs, + interval_wate, + interval_burst; + +int demo_mode; /* are we actually in demo mode? */ +double demo_interval = 1000000.0; /* what is the desired interval to + display interval results. default + is one second in units of + microseconds */ +double demo_units = 0.0; /* what is our current best guess as + to how many work units must be + done to be near the desired + reporting interval? */ + +double units_this_tick; +#endif + +#ifdef DIRTY +int loc_dirty_count; +int loc_clean_count; +int rem_dirty_count; +int rem_clean_count; +#endif /* DIRTY */ + + /* some of the vairables for confidence intervals... */ + +int confidence_level; +int iteration_min; +int iteration_max; +int result_confidence_only = 0; + +double interval; + + /* stuff to control the "width" of the buffer rings for sending and */ + /* receiving data */ +int send_width; +int recv_width; + +/* address family */ +int af = AF_INET; + +/* did someone request processor affinity? */ +int cpu_binding_requested = 0; + +/* are we not establishing a control connection? */ +int no_control = 0; + +char netserver_usage[] = "\n\ +Usage: netserver [options] \n\ +\n\ +Options:\n\ + -h Display this text\n\ + -d Increase debugging output\n\ + -L name,family Use name to pick listen address and family for family\n\ + -p portnum Listen for connect requests on portnum.\n\ + -4 Do IPv4\n\ + -6 Do IPv6\n\ + -v verbosity Specify the verbosity level\n\ + -V Display version information and exit\n\ +\n"; + +/* netperf_usage done as two concatenated strings to make the MS + compiler happy when compiling for x86_32. fix from Spencer + Frink. */ + +char netperf_usage1[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +Global options:\n\ + -a send,recv Set the local send,recv buffer alignment\n\ + -A send,recv Set the remote send,recv buffer alignment\n\ + -B brandstr Specify a string to be emitted with brief output\n\ + -c [cpu_rate] Report local CPU usage\n\ + -C [cpu_rate] Report remote CPU usage\n\ + -d Increase debugging output\n\ + -D [secs,units] * Display interim results at least every secs seconds\n\ + using units as the initial guess for units per second\n\ + -f G|M|K|g|m|k Set the output units\n\ + -F fill_file Pre-fill buffers with data from fill_file\n\ + -h Display this text\n\ + -H name|ip,fam * Specify the target machine and/or local ip and family\n\ + -i max,min Specify the max and min number of iterations (15,1)\n\ + -I lvl[,intvl] Specify confidence level (95 or 99) (99) \n\ + and confidence interval in percentage (10)\n\ + -l testlen Specify test duration (>0 secs) (<0 bytes|trans)\n\ + -L name|ip,fam * Specify the local ip|name and address family\n\ + -o send,recv Set the local send,recv buffer offsets\n\ + -O send,recv Set the remote send,recv buffer offset\n\ + -n numcpu Set the number of processors for CPU util\n\ + -N Establish no control connection, do 'send' side only\n\ + -p port,lport* Specify netserver port number and/or local port\n\ + -P 0|1 Don't/Do display test headers\n\ + -r Allow confidence to be hit on result only\n\ + -t testname Specify test to perform\n\ + -T lcpu,rcpu Request netperf/netserver be bound to local/remote cpu\n\ + -v verbosity Specify the verbosity level\n\ + -W send,recv Set the number of send,recv buffers\n\ + -v level Set the verbosity level (default 1, min 0)\n\ + -V Display the netperf version and exit\n"; + +char netperf_usage2[] = "\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n\ +\n" +"* For these options taking two parms, specifying one value with no comma\n\ +will only set the first parms and will leave the second at the default\n\ +value. To set the second value it must be preceded with a comma or be a\n\ +comma-separated pair. This is to retain previous netperf behaviour.\n"; + + +/* This routine will return the two arguments to the calling routine. */ +/* If the second argument is not specified, and there is no comma, */ +/* then the value of the second argument will be the same as the */ +/* value of the first. If there is a comma, then the value of the */ +/* second argument will be the value of the second argument ;-) */ +void +break_args(char *s, char *arg1, char *arg2) + +{ + char *ns; + ns = strchr(s,','); + if (ns) { + /* there was a comma arg2 should be the second arg*/ + *ns++ = '\0'; + while ((*arg2++ = *ns++) != '\0'); + } + else { + /* there was not a comma, we can use ns as a temp s */ + /* and arg2 should be the same value as arg1 */ + ns = s; + while ((*arg2++ = *ns++) != '\0'); + }; + while ((*arg1++ = *s++) != '\0'); +} + +/* break_args_explicit + + this routine is somewhat like break_args in that it will separate a + pair of comma-separated values. however, if there is no comma, + this version will not ass-u-me that arg2 should be the same as + arg1. raj 2005-02-04 */ +void +break_args_explicit(char *s, char *arg1, char *arg2) + +{ + char *ns; + ns = strchr(s,','); + if (ns) { + /* there was a comma arg2 should be the second arg*/ + *ns++ = '\0'; + while ((*arg2++ = *ns++) != '\0'); + } + else { + /* there was not a comma, so we should make sure that arg2 is \0 + lest something become confused. raj 2005-02-04 */ + *arg2 = '\0'; + }; + while ((*arg1++ = *s++) != '\0'); + +} + +/* given a string with possible values for setting an address family, + convert that into one of the AF_mumble values - AF_INET, AF_INET6, + AF_UNSPEC as apropriate. the family_string is compared in a + case-insensitive manner */ + +int +parse_address_family(char family_string[]) +{ + + char temp[10]; /* gotta love magic constants :) */ + + strncpy(temp,family_string,10); + + if (debug) { + fprintf(where, + "Attempting to parse address family from %s derived from %s\n", + temp, + family_string); + } +#if defined(AF_INET6) + if (strstr(temp,"6")) { + return(AF_INET6); + } +#endif + if (strstr(temp,"inet") || + strstr(temp,"4")) { + return(AF_INET); + } + if (strstr(temp,"unspec") || + strstr(temp,"0")) { + return(AF_UNSPEC); + } + fprintf(where, + "WARNING! %s not recognized as an address family, using AF_UNPSEC\n", + family_string); + fprintf(where, + "Are you sure netperf was configured for that address family?\n"); + fflush(where); + return(AF_UNSPEC); +} + + +void +set_defaults() +{ + + /* stuff to say where this test is going */ + strcpy(host_name,""); /* remote host name or ip addr */ + strcpy(local_host_name,""); /* we want it to be INADDR_ANY */ + strcpy(test_name,"TCP_STREAM"); /* which test to run */ + strncpy(test_port,"12865",PORTBUFSIZE); /* where is the test waiting */ + strncpy(local_test_port,"0",PORTBUFSIZE);/* INPORT_ANY as it were */ + address_family = AF_UNSPEC; + local_address_family = AF_UNSPEC; + + /* output controlling variables */ + debug = 0;/* debugging level */ + print_headers = 1;/* do print test headers */ + verbosity = 1;/* verbosity level */ + /* cpu variables */ + local_cpu_usage = 0;/* measure local cpu */ + remote_cpu_usage = 0;/* what do you think ;-) */ + + local_cpu_rate = (float)0.0; + remote_cpu_rate = (float)0.0; + + /* the end-test conditions for the tests - either transactions, bytes, */ + /* or time. different vars used for clarity - space is cheap ;-) */ + test_time = 10; /* test ends by time */ + test_bytes = 0; /* test ends on byte count */ + test_trans = 0; /* test ends on tran count */ + + /* the alignment conditions for the tests */ + local_recv_align = 8; /* alignment for local receives */ + local_send_align = 8; /* alignment for local sends */ + remote_recv_align = 8; /* alignment for remote receives*/ + remote_send_align = 8; /* alignment for remote sends */ + +#ifdef WANT_INTERVALS + /* rate controlling stuff */ + interval_usecs = 0; + interval_wate = 1; + interval_burst = 0; +#endif /* WANT_INTERVALS */ + +#ifdef DIRTY + /* dirty and clean cache stuff */ + loc_dirty_count = 0; + loc_clean_count = 0; + rem_dirty_count = 0; + rem_clean_count = 0; +#endif /* DIRTY */ + + /* some of the vairables for confidence intervals... */ + + confidence_level = 99; + iteration_min = 1; + iteration_max = 1; + interval = 0.05; /* five percent? */ + + no_control = 0; + strcpy(fill_file,""); +} + + +void +print_netserver_usage() +{ + fwrite(netserver_usage, sizeof(char), strlen(netserver_usage), stderr); +} + + +void +print_netperf_usage() +{ + fwrite(netperf_usage1, sizeof(char), strlen(netperf_usage1), stderr); + fwrite(netperf_usage2, sizeof(char), strlen(netperf_usage2), stderr); +} + +void +scan_cmd_line(int argc, char *argv[]) +{ + extern int optind; /* index of first unused arg */ + extern char *optarg; /* pointer to option string */ + + int c; + + char arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + program = (char *)malloc(strlen(argv[0]) + 1); + if (program == NULL) { + printf("malloc(%d) failed!\n", strlen(argv[0]) + 1); + exit(1); + } + strcpy(program, argv[0]); + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form first, (see the routine break_args.. */ + + while ((c= getopt(argc, argv, GLOBAL_CMD_LINE_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_netperf_usage(); + exit(1); + case 'a': + /* set local alignments */ + break_args(optarg,arg1,arg2); + if (arg1[0]) { + local_send_align = convert(arg1); + } + if (arg2[0]) + local_recv_align = convert(arg2); + break; + case 'A': + /* set remote alignments */ + break_args(optarg,arg1,arg2); + if (arg1[0]) { + remote_send_align = convert(arg1); + } + if (arg2[0]) + remote_recv_align = convert(arg2); + break; + case 'c': + /* measure local cpu usage please. the user */ + /* may have specified the cpu rate as an */ + /* optional parm */ + if (argv[optind] && isdigit((unsigned char)argv[optind][0])){ + /* there was an optional parm */ + local_cpu_rate = (float)atof(argv[optind]); + optind++; + } + local_cpu_usage++; + break; + case 'C': + /* measure remote cpu usage please */ + if (argv[optind] && isdigit((unsigned char)argv[optind][0])){ + /* there was an optional parm */ + remote_cpu_rate = (float)atof(argv[optind]); + optind++; + } + remote_cpu_usage++; + break; + case 'd': + debug++; + break; + case 'D': +#if (defined WANT_DEMO) + demo_mode++; + if (argv[optind] && isdigit((unsigned char)argv[optind][0])){ + /* there was an optional parm */ + break_args_explicit(argv[optind],arg1,arg2); + optind++; + if (arg1[0]) { + demo_interval = atof(arg1) * 1000000.0; + } + if (arg2[0]) { + demo_units = convert(arg2); + } + } +#else + printf("Sorry, Demo Mode not configured into this netperf.\n"); + printf("please consider reconfiguring netperf with\n"); + printf("--enable-demo=yes and recompiling\n"); +#endif + break; + case 'f': + /* set the thruput formatting */ + libfmt = *optarg; + break; + case 'F': + /* set the fill_file variable for pre-filling buffers */ + strcpy(fill_file,optarg); + break; + case 'i': + /* set the iterations min and max for confidence intervals */ + break_args(optarg,arg1,arg2); + if (arg1[0]) { + iteration_max = convert(arg1); + } + if (arg2[0] ) { + iteration_min = convert(arg2); + } + /* if the iteration_max is < iteration_min make iteration_max + equal iteration_min */ + if (iteration_max < iteration_min) iteration_max = iteration_min; + /* limit minimum to 3 iterations */ + if (iteration_max < 3) iteration_max = 3; + if (iteration_min < 3) iteration_min = 3; + /* limit maximum to 30 iterations */ + if (iteration_max > 30) iteration_max = 30; + if (iteration_min > 30) iteration_min = 30; + break; + case 'I': + /* set the confidence level (95 or 99) and width */ + break_args(optarg,arg1,arg2); + if (arg1[0]) { + confidence_level = convert(arg1); + } + if((confidence_level != 95) && (confidence_level != 99)){ + printf("Only 95%% and 99%% confidence level is supported\n"); + exit(1); + } + if (arg2[0] ) { + interval = (double) convert(arg2)/100; + } + /* make sure that iteration_min and iteration_max are at least + at a reasonable default value. if a -i option has previously + been parsed, these will no longer be 1, so we can check + against 1 */ + if (iteration_min == 1) iteration_min = 3; + if (iteration_max == 1) iteration_max = 10; + + break; + case 'k': + /* local dirty and clean counts */ +#ifdef DIRTY + break_args(optarg,arg1,arg2); + if (arg1[0]) { + loc_dirty_count = convert(arg1); + } + if (arg2[0] ) { + loc_clean_count = convert(arg2); + } +#else + printf("I don't know how to get dirty.\n"); +#endif /* DIRTY */ + break; + case 'K': + /* remote dirty and clean counts */ +#ifdef DIRTY + break_args(optarg,arg1,arg2); + if (arg1[0]) { + rem_dirty_count = convert(arg1); + } + if (arg2[0] ) { + rem_clean_count = convert(arg2); + } +#else + printf("I don't know how to get dirty.\n"); +#endif /* DIRTY */ + break; + case 'n': + shell_num_cpus = atoi(optarg); + break; + case 'N': + no_control = 1; + break; + case 'o': + /* set the local offsets */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + local_send_offset = convert(arg1); + if (arg2[0]) + local_recv_offset = convert(arg2); + break; + case 'O': + /* set the remote offsets */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + remote_send_offset = convert(arg1); + if (arg2[0]) + remote_recv_offset = convert(arg2); + break; + case 'P': + /* to print or not to print, that is */ + /* the header question */ + print_headers = convert(optarg); + break; + case 'r': + /* the user wishes that we declare confidence when hit on the + result even if not yet reached on CPU utilization. only + meaningful if cpu util is enabled */ + result_confidence_only = 1; + break; + case 't': + /* set the test name */ + strcpy(test_name,optarg); + break; + case 'T': + /* We want to set the processor on which netserver or netperf */ + /* will run */ + break_args(optarg,arg1,arg2); + if (arg1[0]) { + local_proc_affinity = convert(arg1); + bind_to_specific_processor(local_proc_affinity,0); + } + if (arg2[0]) { + remote_proc_affinity = convert(arg2); + } + cpu_binding_requested = 1; + break; + case 'W': + /* set the "width" of the user space data buffer ring. This will */ + /* be the number of send_size buffers malloc'd in the tests */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + send_width = convert(arg1); + if (arg2[0]) + recv_width = convert(arg2); + break; + case 'l': + /* determine test end conditions */ + /* assume a timed test */ + test_time = convert(optarg); + test_bytes = test_trans = 0; + if (test_time < 0) { + test_bytes = -1 * test_time; + test_trans = test_bytes; + test_time = 0; + } + break; + case 'v': + /* say how much to say */ + verbosity = convert(optarg); + break; + case 'p': + /* specify an alternate port number we use break_args_explicit + here to maintain backwards compatibility with previous + generations of netperf where having a single value did not + set both remote _and_ local port number. raj 2005-02-04 */ + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) + strncpy(test_port,arg1,PORTBUFSIZE); + if (arg2[0]) + strncpy(local_test_port,arg2,PORTBUFSIZE); + break; + case 'H': + /* save-off the host identifying information, use + break_args_explicit since passing just one value should not + set both */ + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) + strncpy(host_name,arg1,sizeof(host_name)); + if (arg2[0]) + address_family = parse_address_family(arg2); + break; + case 'L': + /* save-off the local control socket addressing information. use + break_args_explicit since passing just one value should not + set both */ + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_host_name,arg1,sizeof(local_host_name)); + if (arg2[0]) + local_address_family = parse_address_family(arg2); + break; + case 'w': + /* We want to send requests at a certain wate. */ + /* Remember that there are 1000000 usecs in a */ + /* second, and that the packet rate is */ + /* expressed in packets per millisecond. */ +#ifdef WANT_INTERVALS + interval_usecs = convert_timespec(optarg); + interval_wate = interval_usecs / 1000; +#else + fprintf(where, + "Packet rate control is not compiled in.\n"); +#endif + break; + case 'b': + /* we want to have a burst so many packets per */ + /* interval. */ +#ifdef WANT_INTERVALS + interval_burst = convert(optarg); +#else + fprintf(where, + "Packet burst size is not compiled in. \n"); +#endif /* WANT_INTERVALS */ + break; + case 'B': + result_brand = malloc(strlen(optarg)+1); + if (NULL != result_brand) { + strcpy(result_brand,optarg); + } + else { + fprintf(where, + "Unable to malloc space for result brand\n"); + } + break; + case '4': + address_family = AF_INET; + local_address_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + address_family = AF_INET6; + local_address_family = AF_INET6; +#else + printf("This netperf was not compiled on an IPv6 capable system!\n"); + exit(-1); +#endif + break; + case 'V': + printf("Netperf version %s\n",NETPERF_VERSION); + exit(0); + break; + }; + } + /* ok, what should our default hostname and local binding info be? + */ + if ('\0' == host_name[0]) { + /* host_name was not set */ + switch (address_family) { + case AF_INET: + strcpy(host_name,"localhost"); + break; + case AF_UNSPEC: + /* what to do here? case it off the local_address_family I + suppose */ + switch (local_address_family) { + case AF_INET: + case AF_UNSPEC: + strcpy(host_name,"localhost"); + break; +#if defined(AF_INET6) + case AF_INET6: + strcpy(host_name,"::1"); + break; +#endif + default: + printf("Netperf does not understand %d as an address family\n", + address_family); + exit(-1); + } + break; +#if defined(AF_INET6) + case AF_INET6: + strcpy(host_name,"::1"); + break; +#endif + default: + printf("Netperf does not understand %d as an address family\n", + address_family); + exit(-1); + } + } + + /* now, having established the name to which the control will + connect, from what should it come? */ + if ('\0' == local_host_name[0]) { + switch (local_address_family) { + case AF_INET: + strcpy(local_host_name,"0.0.0.0"); + break; + case AF_UNSPEC: + switch (address_family) { + case AF_INET: + case AF_UNSPEC: + strcpy(local_host_name,"0.0.0.0"); + break; +#if defined(AF_INET6) + case AF_INET6: + strcpy(local_host_name,"::0"); + break; +#endif + default: + printf("Netperf does not understand %d as an address family\n", + address_family); + exit(-1); + } + break; +#if defined(AF_INET6) + case AF_INET6: + strcpy(local_host_name,"::0"); + break; +#endif + default: + printf("Netperf does not understand %d as an address family\n", + address_family); + exit(-1); + } + } + + /* so, if we aren't even going to establish a control connection we + should set certain "remote" settings to reflect this, regardless + of what else may have been set on the command line */ + if (no_control) { + remote_recv_align = -1; + remote_send_align = -1; + remote_send_offset = -1; + remote_recv_offset = -1; + remote_cpu_rate = (float)-1.0; + remote_cpu_usage = 0; + } + + /* parsing test-specific options used to be conditional on there + being a "--" in the option stream. however, some of the tests + have other initialization happening in their "scan" routines so we + want to call them regardless. raj 2005-02-08 */ + if ((strcasecmp(test_name,"TCP_STREAM") == 0) || +#ifdef HAVE_ICSC_EXS + (strcasecmp(test_name,"EXS_TCP_STREAM") == 0) || +#endif /* HAVE_ICSC_EXS */ +#ifdef HAVE_SENDFILE + (strcasecmp(test_name,"TCP_SENDFILE") == 0) || +#endif /* HAVE_SENDFILE */ + (strcasecmp(test_name,"TCP_MAERTS") == 0) || + (strcasecmp(test_name,"TCP_RR") == 0) || + (strcasecmp(test_name,"TCP_CRR") == 0) || + (strcasecmp(test_name,"TCP_CC") == 0) || +#ifdef DO_1644 + (strcasecmp(test_name,"TCP_TRR") == 0) || +#endif /* DO_1644 */ +#ifdef DO_NBRR + (strcasecmp(test_name,"TCP_TRR") == 0) || +#endif /* DO_NBRR */ + (strcasecmp(test_name,"UDP_STREAM") == 0) || + (strcasecmp(test_name,"UDP_RR") == 0)) + { + scan_sockets_args(argc, argv); + } + +#ifdef WANT_DLPI + else if ((strcasecmp(test_name,"DLCO_RR") == 0) || + (strcasecmp(test_name,"DLCL_RR") == 0) || + (strcasecmp(test_name,"DLCO_STREAM") == 0) || + (strcasecmp(test_name,"DLCL_STREAM") == 0)) + { + scan_dlpi_args(argc, argv); + } +#endif /* WANT_DLPI */ + +#ifdef WANT_UNIX + else if ((strcasecmp(test_name,"STREAM_RR") == 0) || + (strcasecmp(test_name,"DG_RR") == 0) || + (strcasecmp(test_name,"STREAM_STREAM") == 0) || + (strcasecmp(test_name,"DG_STREAM") == 0)) + { + scan_unix_args(argc, argv); + } +#endif /* WANT_UNIX */ + +#ifdef WANT_XTI + else if ((strcasecmp(test_name,"XTI_TCP_RR") == 0) || + (strcasecmp(test_name,"XTI_TCP_STREAM") == 0) || + (strcasecmp(test_name,"XTI_UDP_RR") == 0) || + (strcasecmp(test_name,"XTI_UDP_STREAM") == 0)) + { + scan_xti_args(argc, argv); + } +#endif /* WANT_XTI */ + +#ifdef WANT_SCTP + else if ((strcasecmp(test_name,"SCTP_STREAM") == 0) || + (strcasecmp(test_name,"SCTP_RR") == 0) || + (strcasecmp(test_name,"SCTP_STREAM_MANY") == 0) || + (strcasecmp(test_name,"SCTP_RR_MANY") == 0)) + { + scan_sctp_args(argc, argv); + } +#endif + +#ifdef WANT_SDP + else if((strcasecmp(test_name,"SDP_STREAM") == 0) || + (strcasecmp(test_name,"SDP_MAERTS") == 0) || + (strcasecmp(test_name,"SDP_RR") == 0)) + { + scan_sdp_args(argc, argv); + } +#endif + + /* what is our default value for the output units? if the test + name contains "RR" or "rr" or "Rr" or "rR" then the default is + 'x' for transactions. otherwise it is 'm' for megabits + (10^6) */ + + if ('?' == libfmt) { + /* we use a series of strstr's here because not everyone has + strcasestr and I don't feel like up or downshifting text */ + if ((strstr(test_name,"RR")) || + (strstr(test_name,"rr")) || + (strstr(test_name,"Rr")) || + (strstr(test_name,"rR"))) { + libfmt = 'x'; + } + else { + libfmt = 'm'; + } + } + else if ('x' == libfmt) { + /* now, a format of 'x' makes no sense for anything other than + an RR test. if someone has been silly enough to try to set + that, we will reset it silently to default - namely 'm' */ + if ((strstr(test_name,"RR") == NULL) && + (strstr(test_name,"rr") == NULL) && + (strstr(test_name,"Rr") == NULL) && + (strstr(test_name,"rR") == NULL)) { + libfmt = 'm'; + } + } +} + + +void +dump_globals() +{ + printf("Program name: %s\n", program); + printf("Local send alignment: %d\n",local_send_align); + printf("Local recv alignment: %d\n",local_recv_align); + printf("Remote send alignment: %d\n",remote_send_align); + printf("Remote recv alignment: %d\n",remote_recv_align); + printf("Report local CPU %d\n",local_cpu_usage); + printf("Report remote CPU %d\n",remote_cpu_usage); + printf("Verbosity: %d\n",verbosity); + printf("Debug: %d\n",debug); + printf("Port: %s\n",test_port); + printf("Test name: %s\n",test_name); + printf("Test bytes: %d Test time: %d Test trans: %d\n", + test_bytes, + test_time, + test_trans); + printf("Host name: %s\n",host_name); + printf("\n"); +} @@ -0,0 +1,149 @@ +/* + Copyright (C) 1993,1995 Hewlett-Packard Company +*/ + +/* libraried performance include file */ +/* the define NOPERFEXTERN tels us not to re-define all the */ + +/* defines and defaults */ +#define HOSTNAMESIZE 255 +#define PORTBUFSIZE 10 +#define DEFAULT_SIZE 32768 +#define HOST_NAME "127.0.0.1" +#define TEST_PORT "12865" + +/* output controlling variables */ +#define DEBUG 0 /* debugging level */ +#define VERBOSITY 0 /* verbosity level */ + +/* the end-test conditions for the tests - either transactions, bytes, */ +/* or time. different vars used for clarity - space is cheap ;-) */ +#define TEST_TIME 10 /* test ends by time */ +#define TEST_BYTES 0 /* test ends on byte count */ +#define TEST_TRANS 0 /* test ends on tran count */ + +/* the alignment conditions for the tests */ +#define LOC_RECV_ALIGN 4 /* alignment for local receives */ +#define LOC_SEND_ALIGN 4 /* alignment for local sends */ +#define REM_RECV_ALIGN 4 /* alignment for remote receive */ +#define REM_SEND_ALIGN 4 /* alignment for remote sends */ + +/* misc defines for the hell of it */ +#ifndef MAXLONG +#define MAXLONG 4294967295UL +#endif /* MAXLONG */ + +#ifndef NETSH +extern char *program; /* program invocation name */ + +/* stuff to say where this test is going */ +extern char host_name[HOSTNAMESIZE];/* remote host name or ip addr */ +extern char local_host_name[HOSTNAMESIZE]; +extern char test_port[PORTBUFSIZE]; /* where is the test waiting */ +extern char local_test_port[PORTBUFSIZE]; +extern int address_family; +extern int local_address_family; +extern int parse_address_family(char family_string[]); +extern void set_defaults(); +extern void scan_cmd_line(int argc, char *argv[]); +extern void dump_globals(); +extern void break_args(char *s, char *arg1, char *arg2); +extern void break_args_explicit(char *s, char *arg1, char *arg2); +extern void print_netserver_usage(); + +/* output controlling variables */ +extern int + debug, /* debugging level */ + print_headers, /* do/don't print test headers */ + verbosity; /* verbosity level */ + +/* the end-test conditions for the tests - either transactions, bytes, */ +/* or time. different vars used for clarity - space is cheap ;-) */ +extern int + test_time, /* test ends by time */ + test_len_ticks, + test_bytes, /* test ends on byte count */ + test_trans; /* test ends on tran count */ + +/* the alignment conditions for the tests */ +extern int + local_recv_align, /* alignment for local receives */ + local_send_align, /* alignment for local sends */ + remote_recv_align, /* alignment for remote receives */ + remote_send_align, /* alignment for remote sends */ + local_send_offset, + local_recv_offset, + remote_send_offset, + remote_recv_offset; + +#if defined(WANT_INTERVALS) || defined(WANT_DEMO) +extern int interval_usecs; +extern int interval_wate; +extern int interval_burst; + +extern int demo_mode; +extern double demo_interval; +extern double demo_units; +extern double units_this_tick; +#endif + +#ifdef DIRTY +extern int rem_dirty_count; +extern int rem_clean_count; +extern int loc_dirty_count; +extern int loc_clean_count; +#endif /* DIRTY */ + +/* stuff for confidence intervals */ + +extern int confidence_level; +extern int iteration_min; +extern int iteration_max; +extern int result_confidence_only; +extern double interval; + +extern int cpu_binding_requested; + +/* stuff to controll the bufferspace "width" */ +extern int send_width; +extern int recv_width; + +/* address family */ +extern int af; + +/* different options for other things */ +extern int + local_cpu_usage, + remote_cpu_usage; + +extern float + local_cpu_rate, + remote_cpu_rate; + +extern int + shell_num_cpus; + +extern char + test_name[BUFSIZ]; + +extern char + fill_file[BUFSIZ]; + +extern char * + result_brand; + +extern int + no_control; + +#ifdef WANT_DLPI + +extern int + loc_ppa, + rem_ppa; + +extern int + dlpi_sap; + +#endif /* WANT_DLPI */ + +#endif diff --git a/nettest_bsd.c b/nettest_bsd.c new file mode 100644 index 0000000..27092f3 --- /dev/null +++ b/nettest_bsd.c @@ -0,0 +1,12333 @@ +#ifndef lint +char nettest_id[]="\ +@(#)nettest_bsd.c (c) Copyright 1993-2004 Hewlett-Packard Co. Version 2.4.3"; +#endif /* lint */ + + +/****************************************************************/ +/* */ +/* nettest_bsd.c */ +/* */ +/* the BSD sockets parsing routine... */ +/* ...with the addition of Windows NT, this is now also */ +/* a Winsock test... sigh :) */ +/* */ +/* scan_sockets_args() */ +/* */ +/* the actual test routines... */ +/* */ +/* send_tcp_stream() perform a tcp stream test */ +/* recv_tcp_stream() */ +/* send_tcp_maerts() perform a tcp stream test */ +/* recv_tcp_maerts() in the other direction */ +/* send_tcp_rr() perform a tcp request/response */ +/* recv_tcp_rr() */ +/* send_tcp_conn_rr() an RR test including connect */ +/* recv_tcp_conn_rr() */ +/* send_tcp_cc() a connect/disconnect test with */ +/* recv_tcp_cc() no RR */ +/* send_udp_stream() perform a udp stream test */ +/* recv_udp_stream() */ +/* send_udp_rr() perform a udp request/response */ +/* recv_udp_rr() */ +/* loc_cpu_rate() determine the local cpu maxrate */ +/* rem_cpu_rate() find the remote cpu maxrate */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <fcntl.h> +#ifndef WIN32 +#include <errno.h> +#include <signal.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifdef NOSTDLIBH +#include <malloc.h> +#endif /* NOSTDLIBH */ + +#ifndef WIN32 +#if !defined(__VMS) +#include <sys/ipc.h> +#endif /* !defined(__VMS) */ +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#else /* WIN32 */ +#include <process.h> +#define netperf_socklen_t socklen_t +#include <winsock2.h> + +/* while it is unlikely that anyone running Windows 2000 or NT 4 is + going to be trying to compile this, if they are they will want to + define DONT_IPV6 in the sources file */ +#ifndef DONT_IPV6 +#include <ws2tcpip.h> +#endif +#include <windows.h> + +#define sleep(x) Sleep((x)*1000) + +#define __func__ __FUNCTION__ +#endif /* WIN32 */ + +/* We don't want to use bare constants in the shutdown() call. In the + extremely unlikely event that SHUT_WR isn't defined, we will define + it to the value we used to be passing to shutdown() anyway. raj + 2007-02-08 */ +#if !defined(SHUT_WR) +#define SHUT_WR 1 +#endif + +#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) +# include "missing/getaddrinfo.h" +#endif + +#include "netlib.h" +#include "netsh.h" +#include "nettest_bsd.h" + +#if defined(WANT_HISTOGRAM) || defined(WANT_DEMO) +#include "hist.h" +#endif /* WANT_HISTOGRAM */ + +/* make first_burst_size unconditional so we can use it easily enough + when calculating transaction latency for the TCP_RR test. raj + 2007-06-08 */ +int first_burst_size=0; + +#if defined(HAVE_SENDFILE) && (defined(__linux) || defined(__sun__)) +#include <sys/sendfile.h> +#endif /* HAVE_SENDFILE && (__linux || __sun__) */ + + + +/* these variables are specific to the BSD sockets tests, but can + * be used elsewhere if needed. They are externed through nettest_bsd.h + */ + +int + rss_size_req = -1, /* requested remote socket send buffer size */ + rsr_size_req = -1, /* requested remote socket recv buffer size */ + rss_size, /* remote socket send buffer size */ + rsr_size, /* remote socket recv buffer size */ + lss_size_req = -1, /* requested local socket send buffer size */ + lsr_size_req = -1, /* requested local socket recv buffer size */ + lss_size, /* local socket send buffer size */ + lsr_size, /* local socket recv buffer size */ + req_size = 1, /* request size */ + rsp_size = 1, /* response size */ + send_size, /* how big are individual sends */ + recv_size; /* how big are individual receives */ + +static int confidence_iteration; +static char local_cpu_method; +static char remote_cpu_method; + +/* these will control the width of port numbers we try to use in the */ +/* TCP_CRR and/or TCP_TRR tests. raj 3/95 */ +static int client_port_min = 5000; +static int client_port_max = 65535; + + /* different options for the sockets */ + +int + loc_nodelay, /* don't/do use NODELAY locally */ + rem_nodelay, /* don't/do use NODELAY remotely */ +#ifdef TCP_CORK + loc_tcpcork=0, /* don't/do use TCP_CORK locally */ + rem_tcpcork=0, /* don't/do use TCP_CORK remotely */ +#endif /* TCP_CORK */ + loc_sndavoid, /* avoid send copies locally */ + loc_rcvavoid, /* avoid recv copies locally */ + rem_sndavoid, /* avoid send copies remotely */ + rem_rcvavoid, /* avoid recv_copies remotely */ + local_connected = 0, /* local socket type, connected/non-connected */ + remote_connected = 0; /* remote socket type, connected/non-connected */ + +#ifdef WANT_HISTOGRAM +#ifdef HAVE_GETHRTIME +static hrtime_t time_one; +static hrtime_t time_two; +#elif HAVE_GET_HRT +#include "hrt.h" +static hrt_t time_one; +static hrt_t time_two; +#elif defined(WIN32) +static LARGE_INTEGER time_one; +static LARGE_INTEGER time_two; +#else +static struct timeval time_one; +static struct timeval time_two; +#endif /* HAVE_GETHRTIME */ +static HIST time_hist; +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_INTERVALS +int interval_count; +#ifndef WANT_SPIN +sigset_t signal_set; +#define INTERVALS_INIT() \ + if (interval_burst) { \ + /* zero means that we never pause, so we never should need the \ + interval timer. we used to use it for demo mode, but we deal \ + with that with a variant on watching the clock rather than \ + waiting for a timer. raj 2006-02-06 */ \ + start_itimer(interval_wate); \ + } \ + interval_count = interval_burst; \ + /* get the signal set for the call to sigsuspend */ \ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { \ + fprintf(where, \ + "%s: unable to get sigmask errno %d\n", \ + __func__, \ + errno); \ + fflush(where); \ + exit(1); \ + } + +#define INTERVALS_WAIT() \ + /* in this case, the interval count is the count-down couter \ + to decide to sleep for a little bit */ \ + if ((interval_burst) && (--interval_count == 0)) { \ + /* call sigsuspend and wait for the interval timer to get us \ + out */ \ + if (debug > 1) { \ + fprintf(where,"about to suspend\n"); \ + fflush(where); \ + } \ + if (sigsuspend(&signal_set) == EFAULT) { \ + fprintf(where, \ + "%s: fault with sigsuspend.\n", \ + __func__); \ + fflush(where); \ + exit(1); \ + } \ + interval_count = interval_burst; \ + } +#else +/* first out timestamp */ +#ifdef HAVE_GETHRTIME +static hrtime_t intvl_one; +static hrtime_t intvl_two; +static hrtime_t *intvl_one_ptr = &intvl_one; +static hrtime_t *intvl_two_ptr = &intvl_two; +static hrtime_t *temp_intvl_ptr = &intvl_one; +#elif defined(WIN32) +static LARGE_INTEGER intvl_one; +static LARGE_INTEGER intvl_two; +static LARGE_INTEGER *intvl_one_ptr = &intvl_one; +static LARGE_INTEGER *intvl_two_ptr = &intvl_two; +static LARGE_INTEGER *temp_intvl_ptr = &intvl_one; +#else +static struct timeval intvl_one; +static struct timeval intvl_two; +static struct timeval *intvl_one_ptr = &intvl_one; +static struct timeval *intvl_two_ptr = &intvl_two; +static struct timeval *temp_intvl_ptr = &intvl_one; +#endif + +#define INTERVALS_INIT() \ + if (interval_burst) { \ + HIST_timestamp(intvl_one_ptr); \ + } \ + interval_count = interval_burst; \ + +#define INTERVALS_WAIT() \ + /* in this case, the interval count is the count-down couter \ + to decide to sleep for a little bit */ \ + if ((interval_burst) && (--interval_count == 0)) { \ + /* call sigsuspend and wait for the interval timer to get us \ + out */ \ + if (debug > 1) { \ + fprintf(where,"about to spin suspend\n"); \ + fflush(where); \ + } \ + HIST_timestamp(intvl_two_ptr); \ + while(delta_micro(intvl_one_ptr,intvl_two_ptr) < interval_usecs) { \ + HIST_timestamp(intvl_two_ptr); \ + } \ + temp_intvl_ptr = intvl_one_ptr; \ + intvl_one_ptr = intvl_two_ptr; \ + intvl_two_ptr = temp_intvl_ptr; \ + interval_count = interval_burst; \ + } +#endif +#endif + +#ifdef WANT_DEMO +#ifdef HAVE_GETHRTIME +static hrtime_t demo_one; +static hrtime_t demo_two; +static hrtime_t *demo_one_ptr = &demo_one; +static hrtime_t *demo_two_ptr = &demo_two; +static hrtime_t *temp_demo_ptr = &demo_one; +#elif defined(WIN32) +static LARGE_INTEGER demo_one; +static LARGE_INTEGER demo_two; +static LARGE_INTEGER *demo_one_ptr = &demo_one; +static LARGE_INTEGER *demo_two_ptr = &demo_two; +static LARGE_INTEGER *temp_demo_ptr = &demo_one; +#else +static struct timeval demo_one; +static struct timeval demo_two; +static struct timeval *demo_one_ptr = &demo_one; +static struct timeval *demo_two_ptr = &demo_two; +static struct timeval *temp_demo_ptr = &demo_one; +#endif + +/* for a _STREAM test, "a" should be lss_size and "b" should be + rsr_size. for a _MAERTS test, "a" should be lsr_size and "b" should + be rss_size. raj 2005-04-06 */ +#define DEMO_STREAM_SETUP(a,b) \ + if ((demo_mode) && (demo_units == 0)) { \ + /* take our default value of demo_units to be the larger of \ + twice the remote's SO_RCVBUF or twice our SO_SNDBUF */ \ + if (a > b) { \ + demo_units = 2*a; \ + } \ + else { \ + demo_units = 2*b; \ + } \ + } + +#define DEMO_STREAM_INTERVAL(units) \ + if (demo_mode) { \ + double actual_interval; \ + units_this_tick += units; \ + if (units_this_tick >= demo_units) { \ + /* time to possibly update demo_units and maybe output an \ + interim result */ \ + HIST_timestamp(demo_two_ptr); \ + actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); \ + /* we always want to fine-tune demo_units here whether we \ + emit an interim result or not. if we are short, this \ + will lengthen demo_units. if we are long, this will \ + shorten it */ \ + demo_units = demo_units * (demo_interval / actual_interval); \ + if (actual_interval >= demo_interval) { \ + /* time to emit an interim result */ \ + fprintf(where, \ + "Interim result: %7.2f %s/s over %.2f seconds\n", \ + calc_thruput_interval(units_this_tick, \ + actual_interval/1000000.0), \ + format_units(), \ + actual_interval/1000000.0); \ + fflush(where); \ + units_this_tick = 0.0; \ + /* now get a new starting timestamp. we could be clever \ + and swap pointers - the math we do probably does not \ + take all that long, but for now this will suffice */ \ + temp_demo_ptr = demo_one_ptr; \ + demo_one_ptr = demo_two_ptr; \ + demo_two_ptr = temp_demo_ptr; \ + } \ + } \ + } + +#define DEMO_RR_SETUP(a) \ + if ((demo_mode) && (demo_units == 0)) { \ + /* take whatever we are given */ \ + demo_units = a; \ + } + +#define DEMO_RR_INTERVAL(units) \ + if (demo_mode) { \ + double actual_interval; \ + units_this_tick += units; \ + if (units_this_tick >= demo_units) { \ + /* time to possibly update demo_units and maybe output an \ + interim result */ \ + HIST_timestamp(demo_two_ptr); \ + actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); \ + /* we always want to fine-tune demo_units here whether we \ + emit an interim result or not. if we are short, this \ + will lengthen demo_units. if we are long, this will \ + shorten it */ \ + demo_units = demo_units * (demo_interval / actual_interval); \ + if (actual_interval >= demo_interval) { \ + /* time to emit an interim result */ \ + fprintf(where, \ + "Interim result: %.2f %s/s over %.2f seconds\n", \ + units_this_tick / (actual_interval/1000000.0), \ + "Trans", \ + actual_interval/1000000.0); \ + units_this_tick = 0.0; \ + /* now get a new starting timestamp. we could be clever \ + and swap pointers - the math we do probably does not \ + take all that long, but for now this will suffice */ \ + temp_demo_ptr = demo_one_ptr; \ + demo_one_ptr = demo_two_ptr; \ + demo_two_ptr = temp_demo_ptr; \ + } \ + } \ + } +#endif + +char sockets_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +TCP/UDP BSD Sockets Test Options:\n\ + -b number Send number requests at start of _RR tests\n\ + -C Set TCP_CORK when available\n\ + -D [L][,R] Set TCP_NODELAY locally and/or remotely (TCP_*)\n\ + -h Display this text\n\ + -H name,fam Use name (or IP) and family as target of data connection\n\ + -L name,fam Use name (or IP) and family as source of data connection\n\ + -m bytes Set the send size (TCP_STREAM, UDP_STREAM)\n\ + -M bytes Set the recv size (TCP_STREAM, UDP_STREAM)\n\ + -n Use the connected socket for UDP locally\n\ + -N Use the connected socket for UDP remotely\n\ + -p min[,max] Set the min/max port numbers for TCP_CRR, TCP_TRR\n\ + -P local[,remote] Set the local/remote port for the data socket\n\ + -r req,[rsp] Set request/response sizes (TCP_RR, UDP_RR)\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ + -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\ + -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + + +/* these routines convert between the AF address space and the NF + address space since the numeric values of AF_mumble are not the + same across the platforms. raj 2005-02-08 */ + +int +nf_to_af(int nf) { + switch(nf) { + case NF_INET: + return AF_INET; + break; + case NF_UNSPEC: + return AF_UNSPEC; + break; + case NF_INET6: +#if defined(AF_INET6) + return AF_INET6; +#else + return AF_UNSPEC; +#endif + break; + default: + return AF_UNSPEC; + break; + } +} + +int +af_to_nf(int af) { + + switch(af) { + case AF_INET: + return NF_INET; + break; + case AF_UNSPEC: + return NF_UNSPEC; + break; +#if defined(AF_INET6) + case AF_INET6: + return NF_INET6; + break; +#endif + default: + return NF_UNSPEC; + break; + } +} + + + /* This routine is intended to retrieve interesting aspects of tcp */ + /* for the data connection. at first, it attempts to retrieve the */ + /* maximum segment size. later, it might be modified to retrieve */ + /* other information, but it must be information that can be */ + /* retrieved quickly as it is called during the timing of the test. */ + /* for that reason, a second routine may be created that can be */ + /* called outside of the timing loop */ +static +void +get_tcp_info(SOCKET socket, int *mss) +{ + +#ifdef TCP_MAXSEG + netperf_socklen_t sock_opt_len; + + sock_opt_len = sizeof(netperf_socklen_t); + if (getsockopt(socket, + getprotobyname("tcp")->p_proto, + TCP_MAXSEG, + (char *)mss, + &sock_opt_len) == SOCKET_ERROR) { + fprintf(where, + "netperf: get_tcp_info: getsockopt TCP_MAXSEG: errno %d\n", + errno); + fflush(where); + *mss = -1; + } +#else + *mss = -1; +#endif /* TCP_MAXSEG */ +} + + +/* return a pointer to a completed addrinfo chain - prefer + data_address to controlhost and utilize the specified address + family */ + +struct addrinfo * +complete_addrinfo(char *controlhost, char *data_address, char *port, int family, int type, int protocol, int flags) +{ + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *temp_res; + +#define CHANGED_SOCK_TYPE 0x1 +#define CHANGED_PROTOCOL 0x2 +#define CHANGED_SCTP 0x4 + int change_info = 0; + static int change_warning_displayed = 0; + + int count = 0; + int error = 0; + + char *hostname; + + /* take data-address over controlhost */ + if (data_address) + hostname = data_address; + else + hostname = controlhost; + + if (debug) { + fprintf(where, + "complete_addrinfo using hostname %s port %s family %s type %s prot %s flags 0x%x\n", + hostname, + port, + inet_ftos(family), + inet_ttos(type), + inet_ptos(protocol), + flags); + fflush(where); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = type; + hints.ai_protocol = protocol; + hints.ai_flags = flags|AI_CANONNAME; + + count = 0; + do { + error = getaddrinfo((char *)hostname, + (char *)port, + &hints, + &res); + count += 1; + if (error == EAI_AGAIN) { + if (debug) { + fprintf(where,"Sleeping on getaddrinfo EAI_AGAIN\n"); + fflush(where); + } + sleep(1); + } + /* while you see this kludge first, it is actually the second, the + first being the one for Solaris below. The need for this kludge + came after implementing the Solaris broken getaddrinfo kludge - + now we see a kludge in Linux getaddrinfo where if it is given + SOCK_STREAM and IPPROTO_SCTP it barfs with a -7 + EAI_SOCKTYPE. so, we check if the error was EAI_SOCKTYPE and if + we were asking for IPPROTO_SCTP and if so, kludge, again... raj + 2008-10-13 */ +#ifdef WANT_SCTP + if (EAI_SOCKTYPE == error +#ifdef EAI_BADHINTS + || EAI_BADHINTS == error +#endif + ) { + /* we ass-u-me this is the Linux getaddrinfo bug, clear the + hints.ai_protocol field, and set some state "remembering" + that we did this so the code for the Solaris kludge can do + the fix-up for us. also flip error over to EAI_AGAIN and + make sure we don't "count" this time around the loop. */ + hints.ai_protocol = 0; + error = EAI_AGAIN; + count -= 1; + change_info |= CHANGED_SCTP; + } +#endif + } while ((error == EAI_AGAIN) && (count <= 5)); + + if (error) { + fprintf(where, + "complete_addrinfo: could not resolve '%s' port '%s' af %d", + hostname, + port, + family); + fprintf(where, + "\n\tgetaddrinfo returned %d %s\n", + error, + gai_strerror(error)); + fflush(where); + exit(-1); + } + + /* there exists at least one platform - Solaris 10 - that does not + seem to completely honor the ai_protocol and/or ai_socktype one + sets in the hints parm to the getaddrinfo call. so, we need to + walk the list of entries returned and if either of those do not + match what we asked for, we need to go ahead and set them + "correctly" this is based in part on some earlier SCTP-only code + from previous revisions. raj 2006-10-09 */ + + temp_res = res; + + while (temp_res) { + + if ((type) && + (temp_res->ai_socktype != type)) { + change_info |= CHANGED_SOCK_TYPE; + if (debug) { + fprintf(where, + "WARNING! Changed bogus getaddrinfo socket type %d to %d\n", + temp_res->ai_socktype, + type); + fflush(where); + } + temp_res->ai_socktype = type; + } + + if ((protocol) && + (temp_res->ai_protocol != protocol)) { + change_info |= CHANGED_PROTOCOL; + if (debug) { + fprintf(where, + "WARNING! Changed bogus getaddrinfo protocol %d to %d\n", + temp_res->ai_protocol, + protocol); + fflush(where); + } + temp_res->ai_protocol = protocol; + } + temp_res = temp_res->ai_next; + } + + if ((change_info & CHANGED_SOCK_TYPE) && + !(change_warning_displayed & CHANGED_SOCK_TYPE)) { + change_warning_displayed |= CHANGED_SOCK_TYPE; + fprintf(where, + "WARNING! getaddrinfo returned a socket type which did not\n"); + fprintf(where, + "match the requested type. Please contact your vendor for\n"); + fprintf(where, + "a fix to this bug in getaddrinfo()\n"); + fflush(where); + } + + /* if we dropped the protocol hint, it would be for a protocol that + getaddrinfo() wasn't supporting yet, not for the bug that it took + our hint and still returned zero. raj 2006-10-16 */ + if ((change_info & CHANGED_PROTOCOL) && + !(change_warning_displayed & CHANGED_PROTOCOL) && + (hints.ai_protocol != 0)) { + change_warning_displayed |= CHANGED_PROTOCOL; + fprintf(where, + "WARNING! getaddrinfo returned a protocol other than the\n"); + fprintf(where, + "requested protocol. Please contact your vendor for\n"); + fprintf(where, + "a fix to this bug in getaddrinfo()\n"); + fflush(where); + } + + if ((change_info & CHANGED_SCTP) && + !(change_warning_displayed & CHANGED_SCTP)) { + change_warning_displayed |= CHANGED_SCTP; + fprintf(where, + "WARNING! getaddrinfo on this platform does not accept IPPROTO_SCTP!\n"); + fprintf(where, + "Please contact your vendor for a fix to this bug in getaddrinfo().\n"); + fflush(where); + } + + + if (debug) { + dump_addrinfo(where, res, hostname, port, family); + } + + return(res); +} + +void +complete_addrinfos(struct addrinfo **remote,struct addrinfo **local, char remote_host[], int type, int protocol, int flags) { + + *remote = complete_addrinfo(remote_host, + remote_data_address, + remote_data_port, + remote_data_family, + type, + protocol, + flags); + + /* OK, if the user has not specified a local data endpoint address + (test-specific -L), pick the local data endpoint address based on + the remote data family info (test-specific -H or -4 or -6 + option). if the user has not specified remote data addressing + info (test-specific -H, -4 -6) pick something based on the local + control connection address (ie the global -L option). */ + + if (NULL == local_data_address) { + local_data_address = malloc(HOSTNAMESIZE); + if (NULL == remote_data_address) { + if (debug) { + fprintf(where, + "local_data_address not set, using local_host_name of '%s'\n", + local_host_name); + fflush(where); + } + strcpy(local_data_address,local_host_name); + } + else { + if (debug) { + fprintf(where, + "local_data_address not set, using address family info\n"); + fflush(where); + } + /* by default, use 0.0.0.0 - assume IPv4 */ + strcpy(local_data_address,"0.0.0.0"); +#if defined(AF_INET6) + if ((AF_INET6 == local_data_family) || + ((AF_UNSPEC == local_data_family) && + (AF_INET6 == remote_data_family)) || + ((AF_UNSPEC == local_data_family) && + (AF_INET6 == (*remote)->ai_family))) { + strcpy(local_data_address,"::0"); + } +#endif + } + } + + *local = complete_addrinfo("what to put here?", + local_data_address, + local_data_port, + local_data_family, + type, + protocol, + flags|AI_PASSIVE); + +} + +void +set_hostname_and_port(char *hostname, char *portstr, int family, int port) +{ + strcpy(hostname,"0.0.0.0"); +#if defined AF_INET6 + if (AF_INET6 == family) { + strcpy(hostname,"::0"); + } +#endif + + sprintf(portstr, "%u", port); + +} + +static unsigned short +get_port_number(struct addrinfo *res) +{ + switch(res->ai_family) { + case AF_INET: { + struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; + return(ntohs(foo->sin_port)); + break; + } +#if defined(AF_INET6) + case AF_INET6: { + struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; + return(ntohs(foo->sin6_port)); + break; + } +#endif + default: + fprintf(where, + "Unexpected Address Family of %u\n",res->ai_family); + fflush(where); + exit(-1); + } +} + +/* this routine will set the port number of the sockaddr in the + addrinfo to the specified value, based on the address family */ +void +set_port_number(struct addrinfo *res, unsigned short port) +{ + switch(res->ai_family) { + case AF_INET: { + struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; + foo->sin_port = htons(port); + break; + } +#if defined(AF_INET6) + case AF_INET6: { + struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; + foo->sin6_port = htons(port); + break; + } +#endif + default: + fprintf(where, + "Unexpected Address Family of %u\n",res->ai_family); + fflush(where); + exit(-1); + } +} + + + + /* This routine will create a data (listen) socket with the + apropriate options set and return it to the caller. this replaces + all the duplicate code in each of the test routines and should help + make things a little easier to understand. since this routine can be + called by either the netperf or netserver programs, all output + should be directed towards "where." family is generally AF_INET and + type will be either SOCK_STREAM or SOCK_DGRAM. This routine will + also be used by the "SCTP" tests, hence the slightly strange-looking + SCTP stuff in the classic bsd sockets test file... vlad/raj + 2005-03-15 */ + +SOCKET +create_data_socket(struct addrinfo *res) +{ + + SOCKET temp_socket; + int one; + int on = 1; + + + /*set up the data socket */ + temp_socket = socket(res->ai_family, + res->ai_socktype, + res->ai_protocol); + + if (temp_socket == INVALID_SOCKET){ + fprintf(where, + "netperf: create_data_socket: socket: errno %d fam %s type %s prot %s errmsg %s\n", + errno, + inet_ftos(res->ai_family), + inet_ttos(res->ai_socktype), + inet_ptos(res->ai_protocol), + strerror(errno)); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where,"create_data_socket: socket %d obtained...\n",temp_socket); + fflush(where); + } + + /* Modify the local socket size. The reason we alter the send buffer + size here rather than when the connection is made is to take care + of decreases in buffer size. Decreasing the window size after + connection establishment is a TCP no-no. Also, by setting the + buffer (window) size before the connection is established, we can + control the TCP MSS (segment size). The MSS is never (well, should + never be) more that 1/2 the minimum receive buffer size at each + half of the connection. This is why we are altering the receive + buffer size on the sending size of a unidirectional transfer. If + the user has not requested that the socket buffers be altered, we + will try to find-out what their values are. If we cannot touch the + socket buffer in any way, we will set the values to -1 to indicate + that. */ + + /* all the oogy nitty gritty stuff moved from here into the routine + being called below, per patches from davidm to workaround the bug + in Linux getsockopt(). raj 2004-06-15 */ + set_sock_buffer (temp_socket, SEND_BUFFER, lss_size_req, &lss_size); + set_sock_buffer (temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size); + + /* now, we may wish to enable the copy avoidance features on the */ + /* local system. of course, this may not be possible... */ + +#ifdef SO_RCV_COPYAVOID + if (loc_rcvavoid) { + if (setsockopt(temp_socket, + SOL_SOCKET, + SO_RCV_COPYAVOID, + (const char *)&loc_rcvavoid, + sizeof(int)) == SOCKET_ERROR) { + fprintf(where, + "netperf: create_data_socket: Could not enable receive copy avoidance"); + fflush(where); + loc_rcvavoid = 0; + } + } +#else + /* it wasn't compiled in... */ + loc_rcvavoid = 0; +#endif /* SO_RCV_COPYAVOID */ + +#ifdef SO_SND_COPYAVOID + if (loc_sndavoid) { + if (setsockopt(temp_socket, + SOL_SOCKET, + SO_SND_COPYAVOID, + (const char *)&loc_sndavoid, + sizeof(int)) == SOCKET_ERROR) { + fprintf(where, + "netperf: create_data_socket: Could not enable send copy avoidance"); + fflush(where); + loc_sndavoid = 0; + } + } +#else + /* it was not compiled in... */ + loc_sndavoid = 0; +#endif + + /* Now, we will see about setting the TCP_NODELAY flag on the local */ + /* socket. We will only do this for those systems that actually */ + /* support the option. If it fails, note the fact, but keep going. */ + /* If the user tries to enable TCP_NODELAY on a UDP socket, this */ + /* will cause an error to be displayed */ + + /* well..... long ago and far away that would have happened, in + particular because we would always use IPPROTO_TCP here. + however, now we are using res->ai_protocol, which will be + IPPROT_UDP, and while HP-UX, and I suspect no-one else on the + planet has a UDP_mumble option that overlaps with TCP_NODELAY, + sure as knuth made little green programs, linux has a UDP_CORK + option that is defined as a value of 1, which is the same a + TCP_NODELAY under Linux. So, when asking for -D and + "TCP_NODELAY" under Linux, we are actually setting UDP_CORK + instead of getting an error like every other OS on the + planet. joy and rupture. this stops a UDP_RR test cold sooo we + have to make sure that res->ai_protocol actually makes sense for + a _NODELAY setsockopt() or a UDP_RR test on Linux where someone + mistakenly sets -D will hang. raj 2005-04-21 */ + +#if defined(TCP_NODELAY) || defined(SCTP_NODELAY) + if ((loc_nodelay) && (res->ai_protocol != IPPROTO_UDP)) { + + /* strictly speaking, since the if defined above is an OR, we + should probably check against TCP_NODELAY being defined here. + however, the likelihood of SCTP_NODELAY being defined and + TCP_NODELAY _NOT_ being defined is, probably :), epsilon. raj + 2005-03-15 */ + + int option = TCP_NODELAY; + + /* I suspect that WANT_SCTP would suffice here since that is the + only time we would have called getaddrinfo with a hints asking + for SCTP, but just in case there is an SCTP implementation out + there _without_ SCTP_NODELAY... raj 2005-03-15 */ + +#if defined(WANT_SCTP) && defined(SCTP_NODELAY) + if (IPPROTO_SCTP == res->ai_protocol) { + option = SCTP_NODELAY; + } +#endif + + one = 1; + if(setsockopt(temp_socket, + res->ai_protocol, + option, + (char *)&one, + sizeof(one)) == SOCKET_ERROR) { + fprintf(where, + "netperf: create_data_socket: nodelay: errno %d\n", + errno); + fflush(where); + } + + if (debug > 1) { + fprintf(where, + "netperf: create_data_socket: [TCP|SCTP]_NODELAY requested...\n"); + fflush(where); + } + } +#else /* TCP_NODELAY */ + + loc_nodelay = 0; + +#endif /* TCP_NODELAY */ + +#if defined(TCP_CORK) + + if (loc_tcpcork != 0) { + /* the user wishes for us to set TCP_CORK on the socket */ + int one = 1; + if (setsockopt(temp_socket, + getprotobyname("tcp")->p_proto, + TCP_CORK, + (char *)&one, + sizeof(one)) == SOCKET_ERROR) { + perror("netperf: sendfile_tcp_stream: tcp_cork"); + exit(1); + } + if (debug) { + fprintf(where,"sendfile_tcp_stream: tcp_cork...\n"); + } + } + +#endif /* TCP_CORK */ + + /* since some of the UDP tests do not do anything to cause an + implicit bind() call, we need to be rather explicit about our + bind() call here. even if the address and/or the port are zero + (INADDR_ANY etc). raj 2004-07-20 */ + + if (setsockopt(temp_socket, + SOL_SOCKET, + SO_REUSEADDR, + (const char *)&on, + sizeof(on)) < 0) { + fprintf(where, + "netperf: create_data_socket: SO_REUSEADDR failed %d\n", + errno); + fflush(where); + } + + if (bind(temp_socket, + res->ai_addr, + res->ai_addrlen) < 0) { + if (debug) { + fprintf(where, + "netperf: create_data_socket: data socket bind failed errno %d\n", + errno); + fprintf(where," port: %d\n",get_port_number(res)); + fflush(where); + } + } + + + return(temp_socket); + +} + +#ifdef KLUDGE_SOCKET_OPTIONS + + + /* This routine is for those BROKEN systems which do not correctly */ + /* pass socket attributes through calls such as accept(). It should */ + /* only be called for those broken systems. I *really* don't want to */ + /* have this, but even broken systems must be measured. raj 11/95 */ +void +kludge_socket_options(int temp_socket) +{ + + set_sock_buffer(temp_socket, SEND_BUFFER, lss_size_req, &lss_size); + set_sock_buffer(temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size); + + /* now, we may wish to enable the copy avoidance features on the */ + /* local system. of course, this may not be possible... */ + /* those calls were only valid for HP-UX, and I know that HP-UX is */ + /* written correctly, and so we do not need to include those calls */ + /* in this kludgy routine. raj 11/95 */ + + + /* Now, we will see about setting the TCP_NODELAY flag on the local */ + /* socket. We will only do this for those systems that actually */ + /* support the option. If it fails, note the fact, but keep going. */ + /* If the user tries to enable TCP_NODELAY on a UDP socket, this */ + /* will cause an error to be displayed */ + +#ifdef TCP_NODELAY + if (loc_nodelay) { + one = 1; + if(setsockopt(temp_socket, + getprotobyname("tcp")->p_proto, + TCP_NODELAY, + (char *)&one, + sizeof(one)) == SOCKET_ERROR) { + fprintf(where,"netperf: kludge_socket_options: nodelay: errno %d\n", + errno); + fflush(where); + } + + if (debug > 1) { + fprintf(where, + "netperf: kludge_socket_options: TCP_NODELAY requested...\n"); + fflush(where); + } + } +#else /* TCP_NODELAY */ + + loc_nodelay = 0; + +#endif /* TCP_NODELAY */ + + } + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + +static void * +get_address_address(struct addrinfo *info) +{ + struct sockaddr_in *sin; +#if defined(AF_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch(info->ai_family) { + case AF_INET: + sin = (struct sockaddr_in *)info->ai_addr; + return(&(sin->sin_addr)); + break; +#if defined(AF_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *)info->ai_addr; + return(&(sin6->sin6_addr)); + break; +#endif + default: + fprintf(stderr,"we never expected to get here in get_address_address\n"); + fflush(stderr); + exit(-1); + } +} + +#if defined(WIN32) +/* +*+ Why isn't this in the winsock headers yet? */ +const char * +inet_ntop(int af, const void *src, char *dst, size_t size); +#endif + +/* This routine is a generic test header printer for the topmost header */ +void +print_top_test_header(char test_name[], struct addrinfo *source, struct addrinfo *destination) +{ + +#if defined(AF_INET6) + char address_buf[INET6_ADDRSTRLEN]; +#else + char address_buf[16]; /* magic constant */ +#endif + + /* we want to have some additional, interesting information in */ + /* the headers. we know some of it here, but not all, so we will */ + /* only print the test title here and will print the results */ + /* titles after the test is finished */ + fprintf(where,test_name); + address_buf[0] = '\0'; + inet_ntop(source->ai_family,get_address_address(source),address_buf,sizeof(address_buf)); + fprintf(where, + " from %s (%s) port %u %s", + source->ai_canonname, + address_buf, + get_port_number(source), + inet_ftos(source->ai_family)); + address_buf[0] = '\0'; + inet_ntop(destination->ai_family,get_address_address(destination),address_buf,sizeof(address_buf)); + fprintf(where, + " to %s (%s) port %u %s", + destination->ai_canonname, + address_buf, + get_port_number(destination), + inet_ftos(destination->ai_family)); + + if (iteration_max > 1) { + fprintf(where, + " : +/-%3.1f%% @ %2d%% conf. %s", + interval/0.02, + confidence_level, + result_confidence_only ? " on result only" : ""); + } + if ((loc_nodelay > 0) || (rem_nodelay > 0)) { + fprintf(where," : nodelay"); + } + if ((loc_sndavoid > 0) || + (loc_rcvavoid > 0) || + (rem_sndavoid > 0) || + (rem_rcvavoid > 0)) { + fprintf(where," : copy avoidance"); + } + + if (no_control) { + fprintf(where," : no control"); + } + +#ifdef WANT_HISTOGRAM + fprintf(where," : histogram"); +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_INTERVALS +#ifndef WANT_SPIN + fprintf(where," : interval"); +#else + fprintf(where," : spin interval"); +#endif +#endif /* WANT_INTERVALS */ + +#ifdef DIRTY + fprintf(where," : dirty data"); +#endif /* DIRTY */ +#ifdef WANT_DEMO + fprintf(where," : demo"); +#endif +#ifdef WANT_FIRST_BURST + /* a little hokey perhaps, but we really only want this to be + emitted for tests where it actually is used, which means a + "REQUEST/RESPONSE" test. raj 2005-11-10 */ + if (strstr(test_name,"REQUEST/RESPONSE")) { + fprintf(where," : first burst %d",first_burst_size); + } +#endif + if (cpu_binding_requested) { + fprintf(where," : cpu bind"); + } + fprintf(where,"\n"); + +} + + +/* This routine implements the TCP unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_tcp_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %s\n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + SOCKET send_socket; + int bytes_remaining; + int tcp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + + unsigned long long local_bytes_sent = 0; + double bytes_sent = 0.0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct tcp_stream_request_struct *tcp_stream_request; + struct tcp_stream_response_struct *tcp_stream_response; + struct tcp_stream_results_struct *tcp_stream_result; + + tcp_stream_request = + (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; + tcp_stream_response = + (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; + tcp_stream_result = + (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + /* complete_addrinfos will either succede or exit the process */ + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP STREAM TEST",local_res,remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_tcp_stream: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_tcp_stream: send_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 1, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_STREAM; + tcp_stream_request->send_buf_size = rss_size_req; + tcp_stream_request->recv_buf_size = rsr_size_req; + tcp_stream_request->receive_size = recv_size; + tcp_stream_request->no_delay = rem_nodelay; + tcp_stream_request->recv_alignment = remote_recv_align; + tcp_stream_request->recv_offset = remote_recv_offset; + tcp_stream_request->measure_cpu = remote_cpu_usage; + tcp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + tcp_stream_request->test_length = test_time; + } + else { + tcp_stream_request->test_length = test_bytes; + } + tcp_stream_request->so_rcvavoid = rem_rcvavoid; + tcp_stream_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + tcp_stream_request->dirty_count = rem_dirty_count; + tcp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + tcp_stream_request->port = atoi(remote_data_port); + tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where, + "netperf: send_tcp_stream: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the TCP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_stream_response->recv_buf_size; + rss_size = tcp_stream_response->send_buf_size; + rem_nodelay = tcp_stream_response->no_delay; + remote_cpu_usage= tcp_stream_response->measure_cpu; + remote_cpu_rate = tcp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in + network order */ + set_port_number(remote_res, + (short)tcp_stream_response->data_port_number); + + rem_rcvavoid = tcp_stream_response->so_rcvavoid; + rem_sndavoid = tcp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lss_size,rsr_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_tcp_stream: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* we only start the interval timer if we are using the + timer-timed intervals rather than the sit and spin ones. raj + 2006-02-06 */ +#if defined(WANT_INTERVALS) + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before we go into send and then again just + after we come out raj 8/94 */ + /* but lets only do this if there is going to be a histogram + displayed */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + if((len=send(send_socket, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + break; + } + perror("netperf: data send error"); + printf("len was %d\n",len); + exit(1); + } + + local_bytes_sent += send_size; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp the exit from the send call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(send_size) +#endif + +#if defined(WANT_INTERVALS) + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the TCP maximum segment_size was (if possible) */ + if (verbosity > 1) { + tcp_mss = -1; + get_tcp_info(send_socket,&tcp_mss); + } + + if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown tcp stream socket"); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has */ + /* brought all the data up into the application. it will do a */ + /* shutdown to cause a FIN to be sent our way. We will assume that */ + /* any exit from the recv() call is good... raj 4/93 */ + + recv(send_socket, send_ring->buffer_ptr, send_size, 0); + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(send_socket); + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting + things. If it wasn't supposed to care, it will return obvious + values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the + future, we may want to include a calculation of the thruput + measured by the remote, but it should be the case that for a + TCP stream test, that the two numbers should be *very* + close... We calculate bytes_sent regardless of the way the + test length was controlled. If it was time, we needed to, + and if it was by bytes, the user may have specified a number + of bytes that wasn't a multiple of the send_size, so we + really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(tcp_stream_result->bytes_received); + } + else { + bytes_sent = (double)local_bytes_sent; + } + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = tcp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + tcp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)tcp_stream_result->recv_calls, + tcp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + tcp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + + +/* This routine implements the netperf-side TCP unidirectional data + transfer test (a.k.a. stream) for the sockets interface where the + data flow is from the netserver to the netperf. It receives its + parameters via global variables from the shell and writes its + output to the standard output. */ + + +void +send_tcp_maerts(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %s \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Recvs %-8.8s Sends\n\ +Local Remote Local Remote Xfered Per Per\n\ +Recv Send Recv Send Recv (avg) Send (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* recv-size greater than our recv window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *recv_ring; + + int len; + unsigned int nummessages = 0; + SOCKET recv_socket; + int bytes_remaining; + int tcp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can recv > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + double bytes_sent = 0.0; + unsigned long long local_bytes_recvd = 0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct tcp_maerts_request_struct *tcp_maerts_request; + struct tcp_maerts_response_struct *tcp_maerts_response; + struct tcp_maerts_results_struct *tcp_maerts_result; + + tcp_maerts_request = + (struct tcp_maerts_request_struct *)netperf_request.content.test_specific_data; + tcp_maerts_response = + (struct tcp_maerts_response_struct *)netperf_response.content.test_specific_data; + tcp_maerts_result = + (struct tcp_maerts_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP MAERTS TEST",local_res,remote_res); + } + + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + recv_socket = create_data_socket(local_res); + + if (recv_socket == INVALID_SOCKET){ + perror("netperf: send_tcp_maerts: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_tcp_maerts: recv_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the recv */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the recv size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (recv_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one recv-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* recv_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our recv */ + /* buffers, we should respect that wish... */ + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + if (recv_ring == NULL) { + /* only allocate the recv ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + local_recv_align, + local_recv_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 1, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_MAERTS; + tcp_maerts_request->send_buf_size = rss_size_req; + tcp_maerts_request->recv_buf_size = rsr_size_req; + tcp_maerts_request->send_size = send_size; + tcp_maerts_request->no_delay = rem_nodelay; + tcp_maerts_request->send_alignment = remote_send_align; + tcp_maerts_request->send_offset = remote_send_offset; + tcp_maerts_request->measure_cpu = remote_cpu_usage; + tcp_maerts_request->cpu_rate = remote_cpu_rate; + if (test_time) { + tcp_maerts_request->test_length = test_time; + } + else { + tcp_maerts_request->test_length = test_bytes; + } + tcp_maerts_request->so_rcvavoid = rem_rcvavoid; + tcp_maerts_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + tcp_maerts_request->dirty_count = rem_dirty_count; + tcp_maerts_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + tcp_maerts_request->port = atoi(remote_data_port); + tcp_maerts_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where, + "netperf: send_tcp_maerts: requesting TCP maerts test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the TCP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_maerts_response->recv_buf_size; + rss_size = tcp_maerts_response->send_buf_size; + rem_nodelay = tcp_maerts_response->no_delay; + remote_cpu_usage= tcp_maerts_response->measure_cpu; + remote_cpu_rate = tcp_maerts_response->cpu_rate; + send_size = tcp_maerts_response->send_size; + + /* we have to make sure that the server port number is in + network order */ + set_port_number(remote_res, + (short)tcp_maerts_response->data_port_number); + rem_rcvavoid = tcp_maerts_response->so_rcvavoid; + rem_sndavoid = tcp_maerts_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lsr_size,rss_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(recv_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_tcp_maerts: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a maerts test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + if (!no_control) { + /* this is a netperf to netserver test, netserver will close + to tell us the test is over, so use PAD_TIME to avoid + causing the netserver fits. */ + start_timer(test_time + PAD_TIME); + } + else { + /* this is a netperf to data source test, no PAD_TIME */ + start_timer(test_time); + } + } + else { + /* The tester wanted to recv a number of bytes. we don't do that + in a TCP_MAERTS test. sorry. raj 2002-06-21 */ + printf("netperf: send_tcp_maerts: test must be timed\n"); + exit(1); + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* the test will continue until we either get a zero-byte recv() + on the socket or our failsafe timer expires. most of the time + we trust that we get a zero-byte recieve from the socket. raj + 2002-06-21 */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before we go into recv and then again just + after we come out raj 8/94 */ + /* but only if we are actually going to display a histogram. raj + 2006-02-07 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + while ((!times_up) && (len=recv(recv_socket, + recv_ring->buffer_ptr, + recv_size, + 0)) > 0 ) { + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp the exit from the recv call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef DIRTY + access_buffer(recv_ring->buffer_ptr, + recv_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(len); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the recv width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + recv_ring = recv_ring->next; + if (bytes_remaining) { + bytes_remaining -= len; + } + + local_bytes_recvd += len; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* make sure we timestamp just before we go into recv */ + /* raj 2004-06-15 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + } + + /* an EINTR is to be expected when this is a no_control test */ + if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) { + perror("send_tcp_maerts: data recv error"); + printf("len was %d\n",len); + exit(1); + } + + /* if we get here, it must mean we had a recv return of 0 before + the watchdog timer expired, or the watchdog timer expired and + this was a no_control test */ + + /* The test is over. Flush the buffers to the remote end. We do a + graceful release to tell the remote we have all the data. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the TCP maximum segment_size was (if possible) */ + if (verbosity > 1) { + tcp_mss = -1; + get_tcp_info(recv_socket,&tcp_mss); + } + + if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown tcp maerts socket"); + exit(1); + } + + stop_timer(); + + /* this call will always give us the local elapsed time for the + test, and will also store-away the necessaries for cpu + utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(recv_socket); + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting + things. If it wasn't supposed to care, it will return obvious + values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the + future, we may want to include a calculation of the thruput + measured by the remote, but it should be the case that for a + TCP maerts test, that the two numbers should be *very* + close... We calculate bytes_sent regardless of the way the + test length was controlled. If it was time, we needed to, + and if it was by bytes, the user may have specified a number + of bytes that wasn't a multiple of the recv_size, so we + really didn't recv what he asked for ;-) */ + + bytes_sent = ntohd(tcp_maerts_result->bytes_sent); + } + else { + bytes_sent = (double)local_bytes_recvd; + } + + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = tcp_maerts_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + tcp_maerts_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_maerts_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the recvs */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lsr_size, /* local recvbuf size */ + rss_size, /* remot sendbuf size */ + send_size, /* how large were the recvs */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_recv_align, + remote_recv_align, + local_recv_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)tcp_maerts_result->send_calls, + tcp_maerts_result->send_calls); + fprintf(where, + ksink_fmt2, + tcp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in recv() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + + +#ifdef HAVE_ICSC_EXS + +#include <sys/exs.h> + + +/* This routine implements the TCP unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + +void +send_exs_tcp_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + SOCKET send_socket; + int bytes_remaining; + int tcp_mss = -1; /* possibly uninitialized on printf far below */ + + exs_mhandle_t exs_mhandle; + exs_qhandle_t exs_qhandle; +#define NETPERF_EXS_PENDING 16 + int exs_aio_pending; + int exs_aio_eagain; + int exs_aio_dequeued; + int exs_aio_dequeuecnt; + int exs_evtcnt; +#define NETPERF_EXS_QSIZE 128 + exs_event_t exs_evtvec[NETPERF_EXS_QSIZE]; + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + + double bytes_sent = 0.0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct tcp_stream_request_struct *tcp_stream_request; + struct tcp_stream_response_struct *tcp_stream_response; + struct tcp_stream_results_struct *tcp_stream_result; + + tcp_stream_request = + (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; + tcp_stream_response = + (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; + tcp_stream_result = + (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; + +#if 0 /* def WANT_HISTOGRAM */ + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + /* complete_addrinfos will either succede or exit the process */ + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("EXS TCP STREAM TEST",local_res,remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* initialize EXS API and create event queue */ + if (exs_init (EXS_VERSION) == -1) { + perror ("netperf: send_exs_tcp_stream: exs_init failed"); + exit (1); + } + + if ((exs_qhandle = exs_qcreate (NETPERF_EXS_QSIZE)) == EXS_QHANDLE_INVALID) { + perror ("netperf: send_exs_tcp_stream: exs_qcreate failed"); + exit (1); + } + if (debug) { + fprintf (where, "send_exs_tcp_stream: qhandle=%d\n", exs_qhandle); + } + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_tcp_stream: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_tcp_stream: send_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_exs_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset, + &exs_mhandle); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 1, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_STREAM; + tcp_stream_request->send_buf_size = rss_size_req; + tcp_stream_request->recv_buf_size = rsr_size_req; + tcp_stream_request->receive_size = recv_size; + tcp_stream_request->no_delay = rem_nodelay; + tcp_stream_request->recv_alignment = remote_recv_align; + tcp_stream_request->recv_offset = remote_recv_offset; + tcp_stream_request->measure_cpu = remote_cpu_usage; + tcp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + tcp_stream_request->test_length = test_time; + } + else { + tcp_stream_request->test_length = test_bytes; + } + tcp_stream_request->so_rcvavoid = rem_rcvavoid; + tcp_stream_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + tcp_stream_request->dirty_count = rem_dirty_count; + tcp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + tcp_stream_request->port = atoi(remote_data_port); + tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where, + "netperf: send_tcp_stream: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_stream_response->recv_buf_size; + rss_size = tcp_stream_response->send_buf_size; + rem_nodelay = tcp_stream_response->no_delay; + remote_cpu_usage= tcp_stream_response->measure_cpu; + remote_cpu_rate = tcp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in */ + /* network order */ + set_port_number(remote_res,(short)tcp_stream_response->data_port_number); + + rem_rcvavoid = tcp_stream_response->so_rcvavoid; + rem_sndavoid = tcp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + +#if 0 /* def WANT_DEMO */ + DEMO_STREAM_SETUP(lss_size,rsr_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_tcp_stream: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#if 0 /* def WANT_INTERVALS */ + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + +#if 0 /* def WANT_DEMO */ + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + exs_aio_pending = 0; + exs_aio_eagain = 0; + exs_aio_dequeuecnt = 0; + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#if 0 /* def WANT_HISTOGRAM */ + /* timestamp just before we go into send and then again just after */ + /* we come out raj 8/94 */ + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + + /* post up to NETPERF_EXS_PENDING I/Os */ + while ((exs_aio_pending < NETPERF_EXS_PENDING) && + (exs_send (send_socket, send_ring->buffer_ptr, send_size, + 0, exs_qhandle, (exs_ahandle_t)-1, exs_mhandle) == 0)) { + exs_aio_pending++; + + /* now we want to move our pointer to the next + position in the data buffer...we may also want to + wrap back to the "beginning" of the bufferspace, so + we will mod the number of messages sent by the send + width, and use that to calculate the offset to add + to the base pointer. */ + + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* check exs_send result */ + if (exs_aio_pending < NETPERF_EXS_PENDING) { + /* standard flow control case */ + if (errno == EAGAIN) + exs_aio_eagain++; + /* case of times_up */ + else if (errno == EINTR) + break; + /* strange, let's stop */ + else { + perror ("netperf: exs_send error"); + exit (1); + } + } + + /* dequeue events with "threshold" on 1/2 posted */ + exs_aio_dequeued = + exs_qdequeue (exs_qhandle, exs_evtvec, + -(exs_aio_pending>>1), NULL); + exs_aio_dequeuecnt++; + + /* check exs_dequeue result */ + if (exs_aio_dequeued < 0) { + /* case of times_up */ + if (errno == EINTR) + break; + /* strange, let's stop */ + else { + perror ("netperf: exs_send error"); + exit (1); + } + } + /* update number of pending I/Os */ + else { + exs_aio_pending -= exs_aio_dequeued; + } + + +#if 0 /* def WANT_HISTOGRAM */ + /* timestamp the exit from the send call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ + +#if 0 /* def WANT_DEMO */ + DEMO_STREAM_INTERVAL(send_size); +#endif + +#if 0 /* def WANT_INTERVALS */ + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + } + + /* Collect the last completion events */ + exs_aio_dequeued = + exs_qdequeue (exs_qhandle, exs_evtvec, -exs_aio_pending, NULL); + exs_aio_dequeuecnt++; + /* check exs_dequeue result and update number of pending I/Os */ + if (exs_aio_dequeued < 0) { + perror ("netperf: exs_send error"); + exit (1); + } + exs_aio_pending -= exs_aio_dequeued; + + /* Display some async I/O debug info */ + if (debug) { + fprintf (where, "send_exs_tcp_stream: " + "aio sent=%d eagain=%d dequeue=%d pending=%d\n", + nummessages, exs_aio_eagain, exs_aio_dequeuecnt, exs_aio_pending); + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the TCP maximum segment_size was (if possible) */ + if (verbosity > 1) { + tcp_mss = -1; + get_tcp_info(send_socket,&tcp_mss); + } + + if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown tcp stream socket"); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has */ + /* brought all the data up into the application. it will do a */ + /* shutdown to cause a FIN to be sent our way. We will assume that */ + /* any exit from the recv() call is good... raj 4/93 */ + + recv(send_socket, send_ring->buffer_ptr, send_size, 0); + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(send_socket); + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(tcp_stream_result->bytes_received); + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = tcp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + tcp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)tcp_stream_result->recv_calls, + tcp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + tcp_mss); + fflush(where); +#if 0 /* def WANT_HISTOGRAM */ + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + +#endif /* HAVE_ICSC_EXS */ + + + +#if defined(HAVE_SENDFILE) + +#if defined(QUICK_SENDPATH) + +/* + * a temporary stub for the sendpath() system call + * which is defined & implemented in the kernel + * but which has no libc stub. + */ +#include <sys/types.h> +#include <sys/scall_define.h> +#include <sys/uio.h> + +ssize_t +sendpath(int s, char *path, off_t offset, size_t nbytes, + const struct iovec *hdtrl, int flags) + { + return syscall(SYS_sendpath, s, path, offset, nbytes, hdtrl, flags); + } +#endif /* QUICK_SENDPATH */ + +/* This routine implements the TCP unidirectional data transfer test + (a.k.a. stream) for the sockets interface using the sendfile() + system call - TCP_SENDFILE. It receives its parameters via global + variables from the shell and writes its output to the standard + output. Basically, this is the same test as the send_tcp_stream() + logic and we even tell the remote to do a TCP_STREAM test since for + all it knows, nothig is different. */ + +void +sendfile_tcp_stream(remote_host) + char remote_host[]; +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + +char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct sendfile_ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + SOCKET send_socket; + int bytes_remaining; + int tcp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + double bytes_sent = 0.0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + struct sockaddr_in server; + +#if defined(__linux) || defined(__sun__) + off_t scratch_offset; /* the linux sendfile() call will update + the offset variable, which is + something we do _not_ want to happen + to the value in the send_ring! so, we + have to use a scratch variable. */ +#endif /* __linux || defined(__sun__) */ +#if defined (USE_OSX) + off_t scratch_len; /* Darwin 9.x need a value-result parameter */ +#endif +#if defined (__sun__) + size_t scratch_len; /* the sun sendfilev() needs a place to + tell us how many bytes were written, + even though it also returns the value */ + sendfilevec_t sv; +#endif /* __sun__ */ + + struct tcp_stream_request_struct *tcp_stream_request; + struct tcp_stream_response_struct *tcp_stream_response; + struct tcp_stream_results_struct *tcp_stream_result; + + tcp_stream_request = + (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; + tcp_stream_response = + (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; + tcp_stream_result = + (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + /* we want to have some additional, interesting information in */ + /* the headers. we know some of it here, but not all, so we will */ + /* only print the test title here and will print the results */ + /* titles after the test is finished */ +#ifdef QUICK_SENDPATH + print_top_test_header("TCP SENDPATH TEST",local_res,remote_res); +#else + print_top_test_header("TCP SENDFILE TEST",local_res,remote_res); +#endif /* QUICK_SENDPATH */ + } + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /* set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: sendfile_tcp_stream: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"sendfile_tcp_stream: send_socket obtained...\n"); + } + +#if defined(TCP_CORK) + /* should this even be here?!? */ + if (loc_tcpcork != 0) { + /* the user wishes for us to set TCP_CORK on the socket */ + int one = 1; + if (setsockopt(send_socket, + getprotobyname("tcp")->p_proto, + TCP_CORK, + (char *)&one, + sizeof(one)) == SOCKET_ERROR) { + perror("netperf: sendfile_tcp_stream: tcp_cork"); + exit(1); + } + if (debug) { + fprintf(where,"sendfile_tcp_stream: tcp_cork...\n"); + } + } + +#endif /* TCP_CORK */ + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + + /*check for file size/ min file size here? create file here/ back out???*/ + + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and + offset. note also that we have allocated a quantity of memory + that is at least one send-size greater than our socket buffer + size. We want to be sure that there are at least two buffers + allocated - this can be a bit of a problem when the send_size + is bigger than the socket size, so we must check... the user + may have wanted to explicitly set the "width" of our send + buffers, we should respect that wish... */ + + /*sendring -> an offset index that will shift the starting point of the*/ + /*section of the file sent throughout the file*/ + + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + + /* only allocate the send ring once. this is a networking test, + not a memory allocation test. this way, we do not need a + deallocate_buffer_ring() routine, and I don't feel like + writing one anyway :) raj 11/94 */ + + send_ring = alloc_sendfile_buf_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must + calibrate the cpu(s). We will perform this task within the + tests themselves. If the user has specified the cpu rate, then + calibrate_local_cpu will return rather quickly as it will have + nothing to do. If local_cpu_rate is zero, then we will go + through all the "normal" calibration stuff and return the rate + back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 1, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_STREAM; + tcp_stream_request->send_buf_size = rss_size_req; + tcp_stream_request->recv_buf_size = rsr_size_req; + tcp_stream_request->receive_size = recv_size; + tcp_stream_request->no_delay = rem_nodelay; + tcp_stream_request->recv_alignment = remote_recv_align; + tcp_stream_request->recv_offset = remote_recv_offset; + tcp_stream_request->measure_cpu = remote_cpu_usage; + tcp_stream_request->cpu_rate = remote_cpu_rate; + + if (test_time) { + tcp_stream_request->test_length = test_time; + } + else { + tcp_stream_request->test_length = test_bytes; + } + + tcp_stream_request->so_rcvavoid = rem_rcvavoid; + tcp_stream_request->so_sndavoid = rem_sndavoid; + +#ifdef DIRTY + tcp_stream_request->dirty_count = rem_dirty_count; + tcp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + tcp_stream_request->port = atoi(remote_data_port); + tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where, + "netperf: send_tcp_stream: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will have + done all the needed set-up we will have calibrated the cpu + locally before sending the request, and will grab the counter + value right after the connect returns. The remote will grab the + counter right after the accept call. This saves the hassle of + extra messages being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_stream_response->recv_buf_size; + rss_size = tcp_stream_response->send_buf_size; + rem_nodelay = tcp_stream_response->no_delay; + remote_cpu_usage= tcp_stream_response->measure_cpu; + remote_cpu_rate = tcp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in */ + /* network order */ + set_port_number(remote_res,(short)tcp_stream_response->data_port_number); + rem_rcvavoid = tcp_stream_response->so_rcvavoid; + rem_sndavoid = tcp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lss_size,rsr_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_tcp_stream: data socket connect failed"); + printf(" port: %d\n",ntohs(server.sin_port)); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either + the connect would have failed, or the previous response would + have indicated a problem. I failed to see the value of the + extra message after the accept on the remote. If it failed, + we'll see it here. If it didn't, we might as well start pumping + data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + + /* in previous revisions, we had the same code repeated throught + all the test suites. this was unnecessary, and meant more + work for me when I wanted to switch to POSIX signals, so I + have abstracted this out into a routine in netlib.c. if you + are experiencing signal problems, you might want to look + there. raj 11/94 */ + + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + + /* before we start, initialize a few variables */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* We use an "OR" to control test execution. When the test is + controlled by time, the byte count check will always return + false. When the test is controlled by byte count, the time test + will always return false. When the test is finished, the whole + expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + + /* the sendfile_tcp_stream test does not support making the buffers + dirty. 08/2000 */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before we go into sendfile() and then again + just after we come out raj 08/2000 */ + /* but only if we are actually going to display a histogram */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + /* you can look at netlib.h for a description of the fields we + are passing to sendfile(). 08/2000 */ +#ifdef QUICK_SENDPATH + if ((len=sendpath(send_socket, + fill_file, + send_ring->offset, + send_ring->length, + send_ring->hdtrl, + send_ring->flags)) != send_size) +#elif defined(__linux) + scratch_offset = send_ring->offset; + if ((len=sendfile(send_socket, + send_ring->fildes, + &scratch_offset, /* modified after the call! */ + send_ring->length)) != send_size) +#elif defined (__sun__) + /* We must call with SFV_NOWAIT and a large file size (>= 16MB) to + get zero-copy, as well as compiling with -D_LARGEFILE_SOURCE + -D_FILE_OFFSET_BITS=64 */ + sv.sfv_fd = send_ring->fildes; + sv.sfv_flag = SFV_NOWAIT; + sv.sfv_off = send_ring->offset; + sv.sfv_len = send_ring->length; + if ((len = sendfilev(send_socket, &sv, 1, &scratch_len)) != send_size) +#elif defined(__FreeBSD__) + /* so close to HP-UX and yet so far away... :) */ + if ((sendfile(send_ring->fildes, + send_socket, + send_ring->offset, + send_ring->length, + NULL, + (off_t *)&len, + send_ring->flags) != 0) || + (len != send_size)) +#elif defined(USE_OSX) + scratch_len = send_ring->length; + if ((sendfile(send_ring->fildes, + send_socket, + send_ring->offset, + (off_t *)&scratch_len, + NULL, + send_ring->flags) != 0) || + (scratch_len != send_size)) +#else /* original sendile HP-UX */ + if ((len=sendfile(send_socket, + send_ring->fildes, + send_ring->offset, + send_ring->length, + send_ring->hdtrl, + send_ring->flags)) != send_size) +#endif /* QUICK_SENDPATH */ + { + /* the test was interrupted, must be the end of test. the + send_tcp_stream code has some WIN32 ifdefs that we do not + need here. */ + if ((len >=0) || SOCKET_EINTR(len)) { + break; + } + perror("netperf: data send error: sendfile"); + fprintf(stderr, + "len was %d send_size was %d\n", + len, + send_size); + fflush(stderr); + exit(1); + } + + /* offset += len;*/ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp the exit from the send call and update the + histogram */ + + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(send_size); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a + graceful release to insure that all data has been taken by the + remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the TCP maximum segment_size was (if possible) */ + if (verbosity > 1) { + tcp_mss = -1; + get_tcp_info(send_socket,&tcp_mss); + } + + if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown tcp stream socket"); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has */ + /* brought all the data up into the application. it will do a */ + /* shutdown to cause a FIN to be sent our way. We will assume that */ + /* any exit from the recv() call is good... raj 4/93 */ + + /* since we are using sendfile() instead of send, we have no + scratch buffer from the send_ring to use for the + receive. however, since we "know" that the recv should be + returning zero bytes (not that we are making the checks we + should) we can pass the address of the flags field. raj 08/2000 + */ + + recv(send_socket, + &(send_ring->flags), + sizeof(send_ring->flags), + 0); + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(send_socket); + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(tcp_stream_result->bytes_received); + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = tcp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + tcp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + + break; + + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + + } + + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + + case 0: + + fprintf(where, + tput_fmt_0, + thruput); + break; + + case 1: + case 2: + + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)tcp_stream_result->recv_calls, + tcp_stream_result->recv_calls); + + fprintf(where, + ksink_fmt2, + tcp_mss); + + fflush(where); + +#ifdef WANT_HISTOGRAM + + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } +} + +#endif /* HAVE_SENDFILE */ + +/* This is the server-side routine for the tcp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_tcp_stream() +{ + + struct sockaddr_storage myaddr_in, peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + int len; + unsigned int receive_calls; + float elapsed_time; + double bytes_received; + + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + +#ifdef DO_SELECT + fd_set readfds; + struct timeval timeout; +#endif /* DO_SELECT */ + + struct tcp_stream_request_struct *tcp_stream_request; + struct tcp_stream_response_struct *tcp_stream_response; + struct tcp_stream_results_struct *tcp_stream_results; + +#ifdef DO_SELECT + FD_ZERO(&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; +#endif /* DO_SELECT */ + + tcp_stream_request = + (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; + tcp_stream_response = + (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; + tcp_stream_results = + (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_STREAM_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_tcp_stream: requested alignment of %d\n", + tcp_stream_request->recv_alignment); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_stream_request->send_buf_size; + lsr_size_req = tcp_stream_request->recv_buf_size; + loc_nodelay = tcp_stream_request->no_delay; + loc_rcvavoid = tcp_stream_request->so_rcvavoid; + loc_sndavoid = tcp_stream_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_stream_request->ipfamily), + tcp_stream_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_stream_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + /* what sort of sizes did we end-up with? */ + if (tcp_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = tcp_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + tcp_stream_request->recv_alignment, + tcp_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_tcp_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_stream_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + tcp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (tcp_stream_request->measure_cpu) { + tcp_stream_response->measure_cpu = 1; + tcp_stream_response->cpu_rate = + calibrate_local_cpu(tcp_stream_request->cpu_rate); + } + else { + tcp_stream_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_stream_response->send_buf_size = lss_size; + tcp_stream_response->recv_buf_size = lsr_size; + tcp_stream_response->no_delay = loc_nodelay; + tcp_stream_response->so_rcvavoid = loc_rcvavoid; + tcp_stream_response->so_sndavoid = loc_sndavoid; + tcp_stream_response->receive_size = recv_size; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_stream_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + /* there used to be an #ifdef DIRTY call to access_buffer() here, + but we have switched from accessing the buffer before the recv() + call to accessing the buffer after the recv() call. The + accessing before was, IIRC, related to having dirty data when + doing page-flipping copy avoidance. */ + + bytes_received = 0; + receive_calls = 0; + + while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) { + if (len == SOCKET_ERROR ) + { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + bytes_received += len; + receive_calls++; + +#ifdef DIRTY + /* we access the buffer after the recv() call now, rather than before */ + access_buffer(recv_ring->buffer_ptr, + recv_size, + tcp_stream_request->dirty_count, + tcp_stream_request->clean_count); +#endif /* DIRTY */ + + + /* move to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef PAUSE + sleep(1); +#endif /* PAUSE */ + +#ifdef DO_SELECT + FD_SET(s_data,&readfds); + select(s_data+1,&readfds,NULL,NULL,&timeout); +#endif /* DO_SELECT */ + + } + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(tcp_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_stream: got %g bytes\n", + bytes_received); + fprintf(where, + "recv_tcp_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + tcp_stream_results->bytes_received = htond(bytes_received); + tcp_stream_results->elapsed_time = elapsed_time; + tcp_stream_results->recv_calls = receive_calls; + + tcp_stream_results->cpu_method = cpu_method; + tcp_stream_results->num_cpus = lib_num_loc_cpus; + + if (tcp_stream_request->measure_cpu) { + tcp_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_tcp_stream: test complete, sending results.\n"); + fprintf(where, + " bytes_received %g receive_calls %d\n", + bytes_received, + receive_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + send_response(); + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + } + +/* This is the server-side routine for the tcp maerts test. It is + implemented as one routine. I could break things-out somewhat, but + didn't feel it was necessary. */ + +void +recv_tcp_maerts() +{ + + struct sockaddr_storage myaddr_in, peeraddr_in; + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + int len; + unsigned int send_calls; + float elapsed_time; + double bytes_sent = 0.0 ; + + struct ring_elt *send_ring; + + struct tcp_maerts_request_struct *tcp_maerts_request; + struct tcp_maerts_response_struct *tcp_maerts_response; + struct tcp_maerts_results_struct *tcp_maerts_results; + + tcp_maerts_request = + (struct tcp_maerts_request_struct *)netperf_request.content.test_specific_data; + tcp_maerts_response = + (struct tcp_maerts_response_struct *)netperf_response.content.test_specific_data; + tcp_maerts_results = + (struct tcp_maerts_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_maerts: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired + parameters and then let the initiator know that all is ready. If + socket size defaults are to be used, then the initiator will have + sent us 0's. If the socket sizes cannot be changed, then we will + send-back what they are. If that information cannot be + determined, then we send-back -1's for the sizes. If things go + wrong for any reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It + would be best if the error that the remote reports to the user is + the actual error we encountered, rather than some bogus + unexpected response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_maerts: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_MAERTS_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_maerts: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_tcp_maerts: requested alignment of %d\n", + tcp_maerts_request->send_alignment); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_maerts: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_maerts_request->send_buf_size; + lsr_size_req = tcp_maerts_request->recv_buf_size; + loc_nodelay = tcp_maerts_request->no_delay; + loc_rcvavoid = tcp_maerts_request->so_rcvavoid; + loc_sndavoid = tcp_maerts_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_maerts_request->ipfamily), + tcp_maerts_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_maerts_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* what sort of sizes did we end-up with? */ + if (tcp_maerts_request->send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + else { + send_size = tcp_maerts_request->send_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the recving side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (send_width == 0) { + send_width = (lsr_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + send_ring = allocate_buffer_ring(send_width, + send_size, + tcp_maerts_request->send_alignment, + tcp_maerts_request->send_offset); + + if (debug) { + fprintf(where,"recv_tcp_maerts: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_maerts_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + tcp_maerts_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (tcp_maerts_request->measure_cpu) { + tcp_maerts_response->measure_cpu = 1; + tcp_maerts_response->cpu_rate = + calibrate_local_cpu(tcp_maerts_request->cpu_rate); + } + else { + tcp_maerts_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_maerts_response->send_buf_size = lss_size; + tcp_maerts_response->recv_buf_size = lsr_size; + tcp_maerts_response->no_delay = loc_nodelay; + tcp_maerts_response->so_rcvavoid = loc_rcvavoid; + tcp_maerts_response->so_sndavoid = loc_sndavoid; + tcp_maerts_response->send_size = send_size; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* we will start the timer before the accept() to be somewhat + analagous to the starting of the timer before the connect() call + in the TCP_STREAM test. raj 2002-06-21 */ + + start_timer(tcp_maerts_request->test_length); + + /* Now it's time to start receiving data on the connection. We will + first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_maerts_request->measure_cpu); + + + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + + /* this is for those systems which *INCORRECTLY* fail to pass + attributes across an accept() call. Including this goes against + my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + bytes_sent = 0.0; + send_calls = 0; + + len = 0; /* nt-lint; len is not initialized (printf far below) if + times_up initially true.*/ + times_up = 0; /* must remember to initialize this little beauty */ + while (!times_up) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + access_buffer(send_ring->buffer_ptr, + send_size, + tcp_maerts_request->dirty_count, + tcp_maerts_request->clean_count); + +#endif /* DIRTY */ + + if((len=send(s_data, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + bytes_sent += len; + send_calls++; + + /* more to the next buffer in the send_ring */ + send_ring = send_ring->next; + + } + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has + brought all the data up into the application. it will do a + shutdown to cause a FIN to be sent our way. We will assume that + any exit from the recv() call is good... raj 4/93 */ + + recv(s_data, send_ring->buffer_ptr, send_size, 0); + + + cpu_stop(tcp_maerts_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_maerts: got %g bytes\n", + bytes_sent); + fprintf(where, + "recv_tcp_maerts: got %d sends\n", + send_calls); + fflush(where); + } + + tcp_maerts_results->bytes_sent = htond(bytes_sent); + tcp_maerts_results->elapsed_time = elapsed_time; + tcp_maerts_results->send_calls = send_calls; + + if (tcp_maerts_request->measure_cpu) { + tcp_maerts_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_tcp_maerts: test complete, sending results.\n"); + fprintf(where, + " bytes_sent %g send_calls %d\n", + bytes_sent, + send_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + tcp_maerts_results->cpu_method = cpu_method; + tcp_maerts_results->num_cpus = lib_num_loc_cpus; + send_response(); + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + } + + + /* this routine implements the sending (netperf) side of the TCP_RR */ + /* test. */ + +void +send_tcp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_title_band = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed \n\ +Send Recv Size Size Time Throughput \n\ +bytes Bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f %s\n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_title_tput = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Tput CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time %-8.8s local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset RoundTrip Trans Throughput\n\ +Local Remote Local Remote Latency Rate %-8.8s/s\n\ +Send Recv Send Recv usec/Tran per sec Outbound Inbound\n\ +%5d %5d %5d %5d %-6.3f %-6.3f %-6.3f %-6.3f\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct tcp_rr_request_struct *tcp_rr_request; + struct tcp_rr_response_struct *tcp_rr_response; + struct tcp_rr_results_struct *tcp_rr_result; + +#ifdef WANT_FIRST_BURST +#define REQUEST_CWND_INITIAL 2 + /* "in the beginning..." the WANT_FIRST_BURST stuff was like both + Unix and the state of New Jersey - both were simple an unspoiled. + then it was realized that some stacks are quite picky about + initial congestion windows and a non-trivial initial burst of + requests would not be individual segments even with TCP_NODELAY + set. so, we have to start tracking a poor-man's congestion window + up here in window space because we want to try to make something + happen that frankly, we cannot guarantee with the specification + of TCP. ain't that grand?-) raj 2006-01-30 */ + int requests_outstanding = 0; + int request_cwnd = REQUEST_CWND_INITIAL; /* we ass-u-me that having + three requests + outstanding at the + beginning of the test + is ok with TCP stacks + of interest. the first + two will come from our + first_burst loop, and + the third from our + regularly scheduled + send */ +#endif + + tcp_rr_request = + (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_rr_response= + (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_rr_result = + (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP REQUEST/RESPONSE TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + +#ifdef WANT_FIRST_BURST + /* we have to remember to reset the number of transactions + outstanding and the "congestion window for each new + iteration. raj 2006-01-31 */ + requests_outstanding = 0; + request_cwnd = REQUEST_CWND_INITIAL; +#endif + + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_tcp_rr: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_tcp_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 8, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_RR; + tcp_rr_request->recv_buf_size = rsr_size_req; + tcp_rr_request->send_buf_size = rss_size_req; + tcp_rr_request->recv_alignment = remote_recv_align; + tcp_rr_request->recv_offset = remote_recv_offset; + tcp_rr_request->send_alignment = remote_send_align; + tcp_rr_request->send_offset = remote_send_offset; + tcp_rr_request->request_size = req_size; + tcp_rr_request->response_size = rsp_size; + tcp_rr_request->no_delay = rem_nodelay; + tcp_rr_request->measure_cpu = remote_cpu_usage; + tcp_rr_request->cpu_rate = remote_cpu_rate; + tcp_rr_request->so_rcvavoid = rem_rcvavoid; + tcp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + tcp_rr_request->test_length = test_time; + } + else { + tcp_rr_request->test_length = test_trans * -1; + } + tcp_rr_request->port = atoi(remote_data_port); + tcp_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_tcp_rr: requesting TCP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the TCP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_rr_response->recv_buf_size; + rss_size = tcp_rr_response->send_buf_size; + rem_nodelay = tcp_rr_response->no_delay; + remote_cpu_usage = tcp_rr_response->measure_cpu; + remote_cpu_rate = tcp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res,(short)tcp_rr_response->data_port_number); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_RR_SETUP(1000) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: data socket connect failed"); + + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + +#ifdef WANT_FIRST_BURST + /* we can inject no more than request_cwnd, which will grow with + time, and no more than first_burst_size. we don't use <= to + account for the "regularly scheduled" send call. of course + that makes it more a "max_outstanding_ than a + "first_burst_size" but for now we won't fix the names. also, + I suspect the extra check against < first_burst_size is + redundant since later I expect to make sure that request_cwnd + can never get larger than first_burst_size, but just at the + moment I'm feeling like a belt and suspenders kind of + programmer. raj 2006-01-30 */ + while ((first_burst_size > 0) && + (requests_outstanding < request_cwnd) && + (requests_outstanding < first_burst_size)) { + if (debug) { + fprintf(where, + "injecting, req_outstndng %d req_cwnd %d burst %d\n", + requests_outstanding, + request_cwnd, + first_burst_size); + } + if ((len = send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + /* we should never hit the end of the test in the first burst */ + perror("send_tcp_rr: initial burst data send error"); + exit(-1); + } + requests_outstanding += 1; + } + +#endif /* WANT_FIRST_BURST */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to send, and then again just + after the receive raj 8/94 */ + /* but only if we are actually going to display one. raj + 2007-02-07 */ + + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + if ((len = send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (SOCKET_EINTR(len) || (errno == 0)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + +#ifdef WANT_FIRST_BURST + requests_outstanding += 1; +#endif + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if ( SOCKET_EINTR(rsp_bytes_recvd) ) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + +#ifdef WANT_FIRST_BURST + /* so, since we've gotten a response back, update the + bookkeeping accordingly. there is one less request + outstanding and we can put one more out there than before. */ + requests_outstanding -= 1; + if (request_cwnd < first_burst_size) { + request_cwnd += 1; + if (debug) { + fprintf(where, + "incr req_cwnd to %d first_burst %d reqs_outstndng %d\n", + request_cwnd, + first_burst_size, + requests_outstanding); + } + } +#endif + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_RR_INTERVAL(1); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + + /* At this point we used to call shutdown on the data socket to be + sure all the data was delivered, but this was not germane in a + request/response test, and it was causing the tests to "hang" + when they were being controlled by time. So, I have replaced + this shutdown call with a call to close that can be found later + in the procedure. */ + + /* this call will always give us the elapsed time for the test, + and will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated CPU utilization. If it wasn't supposed to care, it + will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where,"netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + } + + /* We now calculate what our "throughput" was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu + utilization for the system(s) Of course, some of the + information might be bogus because there was no idle counter in + the kernel(s). We need to make a note of this for the user's + benefit... */ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will + multiply the number of transaction by 1024 to get "good" + numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = tcp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will + multiply the number of transaction by 1024 to get "good" + numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + tcp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. + if debugging is on, calculate_confidence will print-out the + parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + close(send_socket); + + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user has + specified zero-level verbosity, we will just print the local + service demand, or the remote service demand. If the user has + requested verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than 1, + we will display a veritable plethora of background information + from outside of this block as it it not cpu_measurement + specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + if ('x' == libfmt) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + else { + fprintf(where, + cpu_title_tput, + format_units(), + local_cpu_method, + remote_cpu_method); + } + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + ('x' == libfmt) ? thruput : + calc_thruput_interval_omni(thruput * (req_size+rsp_size), + 1.0), + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + ('x' == libfmt) ? thruput : + calc_thruput_interval_omni(thruput * (req_size+rsp_size), + 1.0), + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + ('x' == libfmt) ? tput_title : tput_title_band, + format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + /* are we trans or do we need to convert to bytes then + bits? at this point, thruput is in our "confident" + transactions per second. we can convert to a + bidirectional bitrate by multiplying that by the sum + of the req_size and rsp_size. we pass that to + calc_thruput_interval_omni with an elapsed time of + 1.0 s to get it converted to [kmg]bits/s or + [KMG]Bytes/s */ + ('x' == libfmt) ? thruput : + calc_thruput_interval_omni(thruput * (req_size+rsp_size), + 1.0), + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* normally, you might think that if we were messing about with + the value of libfmt we would need to put it back again, but + since this is basically the last thing we are going to do with + it, it does not matter. so there :) raj 2007-06-08 */ + /* if the user was asking for transactions, then we report + megabits per sedcond for the unidirectional throughput, + otherwise we use the desired units. */ + if ('x' == libfmt) { + libfmt = 'm'; + } + + fprintf(where, + ksink_fmt, + format_units(), + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset, + /* if the user has enable burst mode, we have to remember + to account for that in the number of transactions + outstanding at any one time. otherwise we will + underreport the latency of individual + transactions. learned from saf by raj 2007-06-08 */ + (((double)1.0/thruput)*(double)1000000.0) * + (double) (1+first_burst_size), + thruput, + calc_thruput_interval_omni(thruput * (double)req_size,1.0), + calc_thruput_interval_omni(thruput * (double)rsp_size,1.0)); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + +void +send_udp_stream(char remote_host[]) +{ + /**********************************************************************/ + /* */ + /* UDP Unidirectional Send Test */ + /* */ + /**********************************************************************/ + +#define UDP_LENGTH_MAX 0XFFFF - 28 + + char *tput_title = "\ +Socket Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +bytes bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = "\ +%6d %6d %-7.2f %7d %6d %7.2f\n\ +%6d %-7.2f %7d %7.2f\n\n"; + + + char *cpu_title = "\ +Socket Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +bytes bytes secs # # %s/sec %% %c%c us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f %c\n"; + + char *cpu_fmt_1 = "\ +%6d %6d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ +%6d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; + + unsigned int messages_recvd; + unsigned int messages_sent; + unsigned int failed_sends; + + float elapsed_time, + local_cpu_utilization, + remote_cpu_utilization; + + float local_service_demand, remote_service_demand; + double local_thruput, remote_thruput; + double bytes_sent; + double bytes_recvd; + + + int len; + struct ring_elt *send_ring; + SOCKET data_socket; + + unsigned int sum_messages_sent; + unsigned int sum_messages_recvd; + unsigned int sum_failed_sends; + double sum_local_thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct udp_stream_request_struct *udp_stream_request; + struct udp_stream_response_struct *udp_stream_response; + struct udp_stream_results_struct *udp_stream_results; + + udp_stream_request = + (struct udp_stream_request_struct *)netperf_request.content.test_specific_data; + udp_stream_response = + (struct udp_stream_response_struct *)netperf_response.content.test_specific_data; + udp_stream_results = + (struct udp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_DGRAM, + IPPROTO_UDP, + 0); + + if ( print_headers ) { + print_top_test_header("UDP UNIDIRECTIONAL SEND TEST",local_res,remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + sum_messages_sent = 0; + sum_messages_recvd = 0; + sum_failed_sends = 0; + sum_local_thruput = 0.0; + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + messages_sent = 0; + messages_recvd = 0; + failed_sends = 0; + times_up = 0; + + /*set up the data socket */ + data_socket = create_data_socket(local_res); + + if (data_socket == INVALID_SOCKET){ + perror("udp_send: data socket"); + exit(1); + } + + /* now, we want to see if we need to set the send_size */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = (lss_size < UDP_LENGTH_MAX ? lss_size : UDP_LENGTH_MAX); + } + else { + send_size = 4096; + } + } + + + /* set-up the data buffer with the requested alignment and offset, */ + /* most of the numbers here are just a hack to pick something nice */ + /* and big in an attempt to never try to send a buffer a second time */ + /* before it leaves the node...unless the user set the width */ + /* explicitly. */ + if (send_width == 0) send_width = 32; + + if (send_ring == NULL ) { + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + + /* if the user supplied a cpu rate, this call will complete rather */ + /* quickly, otherwise, the cpu rate will be retured to us for */ + /* possible display. The Library will keep it's own copy of this data */ + /* for use elsewhere. We will only display it. (Does that make it */ + /* "opaque" to us?) */ + + if (local_cpu_usage) + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + + if (!no_control) { + /* Tell the remote end to set up the data connection. The server + sends back the port number and alters the socket parameters + there. Of course this is a datagram service so no connection + is actually set up, the server just sets up the socket and + binds it. */ + + netperf_request.content.request_type = DO_UDP_STREAM; + udp_stream_request->recv_buf_size = rsr_size_req; + udp_stream_request->message_size = send_size; + udp_stream_request->recv_connected = remote_connected; + udp_stream_request->recv_alignment = remote_recv_align; + udp_stream_request->recv_offset = remote_recv_offset; + udp_stream_request->measure_cpu = remote_cpu_usage; + udp_stream_request->cpu_rate = remote_cpu_rate; + udp_stream_request->test_length = test_time; + udp_stream_request->so_rcvavoid = rem_rcvavoid; + udp_stream_request->so_sndavoid = rem_sndavoid; + udp_stream_request->port = atoi(remote_data_port); + udp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + + send_request(); + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_udp_stream: remote data connection done.\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_udp_stream: error on remote"); + exit(1); + } + + /* Place the port number returned by the remote into the sockaddr */ + /* structure so our sends can be sent to the correct place. Also get */ + /* some of the returned socket buffer information for user display. */ + + /* make sure that port numbers are in the proper order */ + set_port_number(remote_res,(short)udp_stream_response->data_port_number); + + rsr_size = udp_stream_response->recv_buf_size; + rss_size = udp_stream_response->send_buf_size; + remote_cpu_rate = udp_stream_response->cpu_rate; + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lss_size,rsr_size) +#endif + + /* We "connect" up to the remote post to allow is to use the send */ + /* call instead of the sendto call. Presumeably, this is a little */ + /* simpler, and a little more efficient. I think that it also means */ + /* that we can be informed of certain things, but am not sure */ + /* yet...also, this is the way I would expect a client to behave */ + /* when talking to a server */ + if (local_connected) { + if (connect(data_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("send_udp_stream: data socket connect failed"); + exit(1); + } else if (debug) { + fprintf(where,"send_udp_stream: connected data socket.\n"); + fflush(where); + } + } + + /* set up the timer to call us after test_time. one of these days, */ + /* it might be nice to figure-out a nice reliable way to have the */ + /* test controlled by a byte count as well, but since UDP is not */ + /* reliable, that could prove difficult. so, in the meantime, we */ + /* only allow a UDP_STREAM test to be a timed test. */ + + if (test_time) { + times_up = 0; + start_timer(test_time); + } + else { + fprintf(where,"Sorry, UDP_STREAM tests must be timed.\n"); + fflush(where); + } + + /* Get the start count for the idle counter and the start time */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* Send datagrams like there was no tomorrow. at somepoint it might */ + /* be nice to set this up so that a quantity of bytes could be sent, */ + /* but we still need some sort of end of test trigger on the receive */ + /* side. that could be a select with a one second timeout, but then */ + /* if there is a test where none of the data arrives for awile and */ + /* then starts again, we would end the test too soon. something to */ + /* think about... */ + while (!times_up) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + if (local_connected) { + len = send(data_socket, + send_ring->buffer_ptr, + send_size, + 0); + } else { + len = sendto(data_socket, + send_ring->buffer_ptr, + send_size, + 0, + remote_res->ai_addr, + remote_res->ai_addrlen); + } + + if (len != send_size) { + if ((len >= 0) || + SOCKET_EINTR(len)) + break; + if (errno == ENOBUFS) { + failed_sends++; + continue; + } + perror("udp_send: data send error"); + exit(1); + } + messages_sent++; + + /* now we want to move our pointer to the next position in the */ + /* data buffer... */ + + send_ring = send_ring->next; + + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* get the second timestamp */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(send_size) +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + } + + /* This is a timed test, so the remote will be returning to us after */ + /* a time. We should not need to send any "strange" messages to tell */ + /* the remote that the test is completed, unless we decide to add a */ + /* number of messages to the test. */ + + /* the test is over, so get stats and stuff */ + cpu_stop(local_cpu_usage, + &elapsed_time); + + if (!no_control) { + /* Get the statistics from the remote end */ + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_udp_stream: remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_udp_stream: error on remote"); + exit(1); + } + messages_recvd = udp_stream_results->messages_recvd; + bytes_recvd = (double) send_size * (double) messages_recvd; + } + else { + /* since there was no control connection, we've no idea what was + actually received. raj 2007-02-08 */ + messages_recvd = -1; + bytes_recvd = -1.0; + } + + bytes_sent = (double) send_size * (double) messages_sent; + local_thruput = calc_thruput(bytes_sent); + + + /* we asume that the remote ran for as long as we did */ + + remote_thruput = calc_thruput(bytes_recvd); + + /* print the results for this socket and message size */ + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) We pass zeros for the local */ + /* cpu utilization and elapsed time to tell the routine to use */ + /* the libraries own values for those. */ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* shouldn't this really be based on bytes_recvd, since that is */ + /* the effective throughput of the test? I think that it should, */ + /* so will make the change raj 11/94 */ + local_service_demand = calc_service_demand(bytes_recvd, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + /* The local calculations could use variables being kept by */ + /* the local netlib routines. The remote calcuations need to */ + /* have a few things passed to them. */ + if (remote_cpu_usage) { + remote_cpu_utilization = udp_stream_results->cpu_util; + remote_service_demand = calc_service_demand(bytes_recvd, + 0.0, + remote_cpu_utilization, + udp_stream_results->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + remote_thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + /* since the routine calculate_confidence is rather generic, and */ + /* we have a few other parms of interest, we will do a little work */ + /* here to caclulate their average. */ + sum_messages_sent += messages_sent; + sum_messages_recvd += messages_recvd; + sum_failed_sends += failed_sends; + sum_local_thruput += local_thruput; + + confidence_iteration++; + + /* this datapoint is done, so we don't need the socket any longer */ + close(data_socket); + + } + + /* we should reach this point once the test is finished */ + + retrieve_confident_values(&elapsed_time, + &remote_thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* some of the interesting values aren't covered by the generic */ + /* confidence routine */ + messages_sent = sum_messages_sent / (confidence_iteration -1); + messages_recvd = sum_messages_recvd / (confidence_iteration -1); + failed_sends = sum_failed_sends / (confidence_iteration -1); + local_thruput = sum_local_thruput / (confidence_iteration -1); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(udp_stream_results->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + local_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + messages_sent, + failed_sends, + local_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rsr_size, + elapsed_time, + messages_recvd, + remote_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + messages_sent, + failed_sends, + local_thruput, + rsr_size, /* remote recvbuf size */ + elapsed_time, + messages_recvd, + remote_thruput); + break; + } + } + + fflush(where); +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + fprintf(where,"\nHistogram of time spent in send() call\n"); + fflush(where); + HIST_report(time_hist); + } +#endif /* WANT_HISTOGRAM */ + +} + + + /* this routine implements the receive side (netserver) of the */ + /* UDP_STREAM performance test. */ + +void +recv_udp_stream() +{ + struct ring_elt *recv_ring; + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in; + SOCKET s_data; + netperf_socklen_t addrlen; + struct sockaddr_storage remote_addr; + netperf_socklen_t remote_addrlen; + + int len = 0; + unsigned int bytes_received = 0; + float elapsed_time; + + int message_size; + unsigned int messages_recvd = 0; + + struct udp_stream_request_struct *udp_stream_request; + struct udp_stream_response_struct *udp_stream_response; + struct udp_stream_results_struct *udp_stream_results; + + udp_stream_request = + (struct udp_stream_request_struct *)netperf_request.content.test_specific_data; + udp_stream_response = + (struct udp_stream_response_struct *)netperf_response.content.test_specific_data; + udp_stream_results = + (struct udp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_udp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug > 1) { + fprintf(where,"recv_udp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = UDP_STREAM_RESPONSE; + + if (debug > 2) { + fprintf(where,"recv_udp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_udp_stream: requested alignment of %d\n", + udp_stream_request->recv_alignment); + fflush(where); + } + + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + udp_stream_request->message_size, + udp_stream_request->recv_alignment, + udp_stream_request->recv_offset); + + if (debug > 1) { + fprintf(where,"recv_udp_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug > 1) { + fprintf(where,"recv_udp_stream: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lsr_size_req = udp_stream_request->recv_buf_size; + loc_rcvavoid = udp_stream_request->so_rcvavoid; + loc_sndavoid = udp_stream_request->so_sndavoid; + local_connected = udp_stream_request->recv_connected; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(udp_stream_request->ipfamily), + udp_stream_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(udp_stream_request->ipfamily), + SOCK_DGRAM, + IPPROTO_UDP, + 0); + + s_data = create_data_socket(local_res); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + udp_stream_response->test_length = udp_stream_request->test_length; + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_data, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_data); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + udp_stream_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + udp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */ + udp_stream_response->measure_cpu = 0; + if (udp_stream_request->measure_cpu) { + /* We will pass the rate into the calibration routine. If the */ + /* user did not specify one, it will be 0.0, and we will do a */ + /* "real" calibration. Otherwise, all it will really do is */ + /* store it away... */ + udp_stream_response->measure_cpu = 1; + udp_stream_response->cpu_rate = + calibrate_local_cpu(udp_stream_request->cpu_rate); + } + + message_size = udp_stream_request->message_size; + test_time = udp_stream_request->test_length; + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + udp_stream_response->send_buf_size = lss_size; + udp_stream_response->recv_buf_size = lsr_size; + udp_stream_response->so_rcvavoid = loc_rcvavoid; + udp_stream_response->so_sndavoid = loc_sndavoid; + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(udp_stream_request->measure_cpu); + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + /* The loop will exit when the timer pops, or if we happen to recv a */ + /* message of less than send_size bytes... */ + + times_up = 0; + + start_timer(test_time + PAD_TIME); + + if (debug) { + fprintf(where,"recv_udp_stream: about to enter inner sanctum.\n"); + fflush(where); + } + + /* We "connect" up to the remote post to allow us to use the recv */ + /* call instead of the recvfrom call. Presumeably, this is a little */ + /* simpler, and a little more efficient. */ + + if (local_connected) { + + /* Receive the first message using recvfrom to find the remote address */ + remote_addrlen = sizeof(remote_addr); + len = recvfrom(s_data, recv_ring->buffer_ptr, + message_size, 0, + (struct sockaddr*)&remote_addr, &remote_addrlen); + if (len != message_size) { + if ((len == SOCKET_ERROR) && !SOCKET_EINTR(len)) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + } + messages_recvd++; + recv_ring = recv_ring->next; + + + /* Now connect with the remote socket address */ + if (connect(s_data, + (struct sockaddr*)&remote_addr, + remote_addrlen )== INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + close(s_data); + send_response(); + exit(1); + } + + if (debug) { + fprintf(where,"recv_udp_stream: connected data socket\n"); + fflush(where); + } + } + + while (!times_up) { + if(local_connected) { + len = recv(s_data, + recv_ring->buffer_ptr, + message_size, + 0); + } else { + len = recvfrom(s_data, + recv_ring->buffer_ptr, + message_size, + 0,0,0); + } + + if (len != message_size) { + if ((len == SOCKET_ERROR) && !SOCKET_EINTR(len)) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + break; + } + messages_recvd++; + recv_ring = recv_ring->next; + } + + if (debug) { + fprintf(where,"recv_udp_stream: got %d messages.\n",messages_recvd); + fflush(where); + } + + + /* The loop now exits due timer or < send_size bytes received. in */ + /* reality, we only really support a timed UDP_STREAM test. raj */ + /* 12/95 */ + + cpu_stop(udp_stream_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended on a timer, subtract the PAD_TIME */ + elapsed_time -= (float)PAD_TIME; + } + else { + stop_timer(); + } + + if (debug) { + fprintf(where,"recv_udp_stream: test ended in %f seconds.\n",elapsed_time); + fflush(where); + } + + + /* We will count the "off" message that got us out of the loop */ + bytes_received = (messages_recvd * message_size) + len; + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_udp_stream: got %d bytes\n", + bytes_received); + fflush(where); + } + + netperf_response.content.response_type = UDP_STREAM_RESULTS; + udp_stream_results->bytes_received = htonl(bytes_received); + udp_stream_results->messages_recvd = messages_recvd; + udp_stream_results->elapsed_time = elapsed_time; + udp_stream_results->cpu_method = cpu_method; + udp_stream_results->num_cpus = lib_num_loc_cpus; + if (udp_stream_request->measure_cpu) { + udp_stream_results->cpu_util = calc_cpu_util(elapsed_time); + } + else { + udp_stream_results->cpu_util = (float) -1.0; + } + + if (debug > 1) { + fprintf(where, + "recv_udp_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + + close(s_data); + +} + +void +send_udp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + float elapsed_time; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int len; + int nummessages; + SOCKET send_socket; + int trans_remaining; + int bytes_xferd; + + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct udp_rr_request_struct *udp_rr_request; + struct udp_rr_response_struct *udp_rr_response; + struct udp_rr_results_struct *udp_rr_result; + + udp_rr_request = + (struct udp_rr_request_struct *)netperf_request.content.test_specific_data; + udp_rr_response = + (struct udp_rr_response_struct *)netperf_response.content.test_specific_data; + udp_rr_result = + (struct udp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_DGRAM, + IPPROTO_UDP, + 0); + + if ( print_headers ) { + print_top_test_header("UDP REQUEST/RESPONSE TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_udp_rr: udp rr data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_udp_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. If */ + /* there is no idle counter in the kernel idle loop, the */ + /* local_cpu_rate will be set to -1. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 8, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_UDP_RR; + udp_rr_request->recv_buf_size = rsr_size_req; + udp_rr_request->send_buf_size = rss_size_req; + udp_rr_request->recv_alignment = remote_recv_align; + udp_rr_request->recv_offset = remote_recv_offset; + udp_rr_request->send_alignment = remote_send_align; + udp_rr_request->send_offset = remote_send_offset; + udp_rr_request->request_size = req_size; + udp_rr_request->response_size = rsp_size; + udp_rr_request->measure_cpu = remote_cpu_usage; + udp_rr_request->cpu_rate = remote_cpu_rate; + udp_rr_request->so_rcvavoid = rem_rcvavoid; + udp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + udp_rr_request->test_length = test_time; + } + else { + udp_rr_request->test_length = test_trans * -1; + } + udp_rr_request->port = atoi(remote_data_port); + udp_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_udp_rr: requesting UDP r/r test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the UDP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = udp_rr_response->recv_buf_size; + rss_size = udp_rr_response->send_buf_size; + remote_cpu_usage = udp_rr_response->measure_cpu; + remote_cpu_rate = udp_rr_response->cpu_rate; + /* port numbers in proper order */ + set_port_number(remote_res,(short)udp_rr_response->data_port_number); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_RR_SETUP(100) +#endif + + /* Connect up to the remote port on the data socket. This will set */ + /* the default destination address on this socket. With UDP, this */ + /* does make a performance difference as we may not have to do as */ + /* many routing lookups, however, I expect that a client would */ + /* behave this way. raj 1/94 */ + + if ( connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET ) { + perror("netperf: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return */ + /* false. When the test is controlled by byte count, the time test */ + /* will always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think */ + /* I just arbitrarily decrement trans_remaining for the timed */ + /* test, but will not do that just yet... One other question is */ + /* whether or not the send buffer and the receive buffer should be */ + /* the same buffer. */ + +#ifdef WANT_FIRST_BURST + { + int i; + for (i = 0; i < first_burst_size; i++) { + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + /* we should never hit the end of the test in the first burst */ + perror("send_udp_rr: initial burst data send error"); + exit(-1); + } + } + } +#endif /* WANT_FIRST_BURST */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_one); + } +#endif + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (SOCKET_EINTR(len)) { + /* We likely hit */ + /* test-end time. */ + break; + } + perror("send_udp_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response. with UDP we will get it all, or nothing */ + + if((rsp_bytes_recvd=recv(send_socket, + recv_ring->buffer_ptr, + rsp_size, + 0)) != rsp_size) { + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* Again, we have likely hit test-end time */ + break; + } + perror("send_udp_rr: data recv error"); + exit(1); + } + recv_ring = recv_ring->next; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } + +#endif + + /* at this point, we may wish to sleep for some period of */ + /* time, so we see how long that last transaction just took, */ + /* and sleep for the difference of that and the interval. We */ + /* will not sleep if the time would be less than a */ + /* millisecond. */ + +#ifdef WANT_DEMO + DEMO_RR_INTERVAL(1); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where,"Transaction %d completed\n",nummessages); + fflush(where); + } + } + + } + + /* for some strange reason, I used to call shutdown on the UDP */ + /* data socket here. I'm not sure why, because it would not have */ + /* any effect... raj 11/94 */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting + things. If it wasn't supposed to care, it will return obvious + values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + } + + /* We now calculate what our thruput was for the test. In the */ + /* future, we may want to include a calculation of the thruput */ + /* measured by the remote, but it should be the case that for a */ + /* UDP rr test, that the two numbers should be *very* close... */ + /* We calculate bytes_sent regardless of the way the test length */ + /* was controlled. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages / elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) Of course, some of the */ + /* information might be bogus because there was no idle counter */ + /* in the kernel(s). We need to make a note of this for the */ + /* user's benefit by placing a code for the metod used in the */ + /* test banner */ + + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = udp_rr_result->cpu_util; + + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + udp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are done with the socket */ + close(send_socket); + } + + /* at this point, we have made all the iterations we are going to */ + /* make. */ + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(udp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + fflush(where); + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* UDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/reponse times.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } +} + + /* this routine implements the receive side (netserver) of a UDP_RR */ + /* test. */ +void +recv_udp_rr() +{ + + struct ring_elt *recv_ring; + struct ring_elt *send_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in; + struct sockaddr_storage peeraddr; + SOCKET s_data; + netperf_socklen_t addrlen; + int trans_received; + int trans_remaining; + int request_bytes_recvd; + int response_bytes_sent; + float elapsed_time; + + struct udp_rr_request_struct *udp_rr_request; + struct udp_rr_response_struct *udp_rr_response; + struct udp_rr_results_struct *udp_rr_results; + + udp_rr_request = + (struct udp_rr_request_struct *)netperf_request.content.test_specific_data; + udp_rr_response = + (struct udp_rr_response_struct *)netperf_response.content.test_specific_data; + udp_rr_results = + (struct udp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_udp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_udp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = UDP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_udp_rr: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where,"recv_udp_rr: requested recv alignment of %d offset %d\n", + udp_rr_request->recv_alignment, + udp_rr_request->recv_offset); + fprintf(where,"recv_udp_rr: requested send alignment of %d offset %d\n", + udp_rr_request->send_alignment, + udp_rr_request->send_offset); + fflush(where); + } + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + udp_rr_request->request_size, + udp_rr_request->recv_alignment, + udp_rr_request->recv_offset); + + send_ring = allocate_buffer_ring(send_width, + udp_rr_request->response_size, + udp_rr_request->send_alignment, + udp_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_udp_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_udp_rr: grabbing a socket...\n"); + fflush(where); + } + + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = udp_rr_request->send_buf_size; + lsr_size_req = udp_rr_request->recv_buf_size; + loc_rcvavoid = udp_rr_request->so_rcvavoid; + loc_sndavoid = udp_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(udp_rr_request->ipfamily), + udp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(udp_rr_request->ipfamily), + SOCK_DGRAM, + IPPROTO_UDP, + 0); + + s_data = create_data_socket(local_res); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_data, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_data); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + udp_rr_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + netperf_response.content.serv_errno = 0; + + if (debug) { + fprintf(where, + "recv port number %d\n", + ((struct sockaddr_in *)&myaddr_in)->sin_port); + fflush(where); + } + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + udp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + udp_rr_response->measure_cpu = 0; + if (udp_rr_request->measure_cpu) { + udp_rr_response->measure_cpu = 1; + udp_rr_response->cpu_rate = calibrate_local_cpu(udp_rr_request->cpu_rate); + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + udp_rr_response->send_buf_size = lss_size; + udp_rr_response->recv_buf_size = lsr_size; + udp_rr_response->so_rcvavoid = loc_rcvavoid; + udp_rr_response->so_sndavoid = loc_sndavoid; + + send_response(); + + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(udp_rr_request->measure_cpu); + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + if (udp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(udp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = udp_rr_request->test_length * -1; + } + + addrlen = sizeof(peeraddr); + bzero((char *)&peeraddr, addrlen); + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + /* receive the request from the other side */ + if ((request_bytes_recvd = recvfrom(s_data, + recv_ring->buffer_ptr, + udp_rr_request->request_size, + 0, + (struct sockaddr *)&peeraddr, + &addrlen)) != udp_rr_request->request_size) { + if ( SOCKET_EINTR(request_bytes_recvd) ) + { + /* we must have hit the end of test time. */ + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + recv_ring = recv_ring->next; + + /* Now, send the response to the remote */ + if ((response_bytes_sent = sendto(s_data, + send_ring->buffer_ptr, + udp_rr_request->response_size, + 0, + (struct sockaddr *)&peeraddr, + addrlen)) != + udp_rr_request->response_size) { + if ( SOCKET_EINTR(response_bytes_sent) ) + { + /* we have hit end of test time. */ + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_udp_rr: Transaction %d complete.\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(udp_rr_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_udp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + udp_rr_results->bytes_received = (trans_received * + (udp_rr_request->request_size + + udp_rr_request->response_size)); + udp_rr_results->trans_received = trans_received; + udp_rr_results->elapsed_time = elapsed_time; + udp_rr_results->cpu_method = cpu_method; + udp_rr_results->num_cpus = lib_num_loc_cpus; + if (udp_rr_request->measure_cpu) { + udp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_udp_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + + /* we are done with the socket now */ + close(s_data); + + } + + + /* this routine implements the receive (netserver) side of a TCP_RR */ + /* test */ +void +recv_tcp_rr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in, + peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + int sock_closed = 0; + float elapsed_time; + + struct tcp_rr_request_struct *tcp_rr_request; + struct tcp_rr_response_struct *tcp_rr_response; + struct tcp_rr_results_struct *tcp_rr_results; + + tcp_rr_request = + (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_rr_response = + (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_rr_results = + (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_rr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_tcp_rr: requested recv alignment of %d offset %d\n", + tcp_rr_request->recv_alignment, + tcp_rr_request->recv_offset); + fprintf(where,"recv_tcp_rr: requested send alignment of %d offset %d\n", + tcp_rr_request->send_alignment, + tcp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + tcp_rr_request->response_size, + tcp_rr_request->send_alignment, + tcp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + tcp_rr_request->request_size, + tcp_rr_request->recv_alignment, + tcp_rr_request->recv_offset); + + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_rr_request->send_buf_size; + lsr_size_req = tcp_rr_request->recv_buf_size; + loc_nodelay = tcp_rr_request->no_delay; + loc_rcvavoid = tcp_rr_request->so_rcvavoid; + loc_sndavoid = tcp_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_rr_request->ipfamily), + tcp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_rr_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + tcp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + tcp_rr_response->measure_cpu = 0; + + if (tcp_rr_request->measure_cpu) { + tcp_rr_response->measure_cpu = 1; + tcp_rr_response->cpu_rate = calibrate_local_cpu(tcp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_rr_response->send_buf_size = lss_size; + tcp_rr_response->recv_buf_size = lsr_size; + tcp_rr_response->no_delay = loc_nodelay; + tcp_rr_response->so_rcvavoid = loc_rcvavoid; + tcp_rr_response->so_sndavoid = loc_sndavoid; + tcp_rr_response->test_length = tcp_rr_request->test_length; + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + if (debug) { + fprintf(where,"recv_tcp_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_rr_request->measure_cpu); + + /* The loop will exit when we hit the end of the test time, or when */ + /* we have exchanged the requested number of transactions. */ + + if (tcp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(tcp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = tcp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = tcp_rr_request->request_size; + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(request_bytes_recvd)) + { + timed_out = 1; + break; + } + + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else if( request_bytes_recvd == 0 ) { + if (debug) { + fprintf(where,"zero is my hero\n"); + fflush(where); + } + sock_closed = 1; + break; + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + recv_ring = recv_ring->next; + + if ((timed_out) || (sock_closed)) { + /* we hit the end of the test based on time - or the socket + closed on us along the way. bail out of here now... */ + if (debug) { + fprintf(where,"yo5\n"); + fflush(where); + } + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=send(s_data, + send_ring->buffer_ptr, + tcp_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_sent)) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 992; + send_response(); + exit(1); + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(tcp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + tcp_rr_results->bytes_received = (trans_received * + (tcp_rr_request->request_size + + tcp_rr_request->response_size)); + tcp_rr_results->trans_received = trans_received; + tcp_rr_results->elapsed_time = elapsed_time; + tcp_rr_results->cpu_method = cpu_method; + tcp_rr_results->num_cpus = lib_num_loc_cpus; + if (tcp_rr_request->measure_cpu) { + tcp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_tcp_rr: test complete, sending results.\n"); + fflush(where); + } + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + send_response(); + +} + + +void +loc_cpu_rate() +{ +#if defined(USE_LOOPER) + float dummy; +#endif + + /* a rather simple little test - it merely calibrates the local cpu */ + /* and prints the results. There are no headers to allow someone to */ + /* find a rate and use it in other tests automagically by setting a */ + /* variable equal to the output of this test. We ignore any rates */ + /* that may have been specified. In fact, we ignore all of the */ + /* command line args! */ + + fprintf(where, + "%g", + calibrate_local_cpu(0.0)); + + if (verbosity > 1) + fprintf(where, + "\nThere %s %d local %s\n", + (lib_num_loc_cpus > 1) ? "are" : "is", + lib_num_loc_cpus, + (lib_num_loc_cpus > 1) ? "cpus" : "cpu"); + + /* we need the cpu_start, cpu_stop in the looper case to kill the */ + /* child proceses raj 4/95 */ + +#ifdef USE_LOOPER + cpu_start(1); + cpu_stop(1,&dummy); +#endif /* USE_LOOPER */ + +} + +void +rem_cpu_rate() +{ + /* this test is much like the local variant, except that it works for */ + /* the remote system, so in this case, we do pay attention to the */ + /* value of the '-H' command line argument. */ + + fprintf(where, + "%g", + calibrate_remote_cpu()); + + if (verbosity > 1) + fprintf(where, + "\nThere %s %d remote %s\n", + (lib_num_rem_cpus > 1) ? "are" : "is", + lib_num_rem_cpus, + (lib_num_rem_cpus > 1) ? "cpus" : "cpu"); + +} + + + /* this test is intended to test the performance of establishing a + connection, exchanging a request/response pair, and repeating. it + is expected that this would be a good starting-point for + comparision of T/TCP with classic TCP for transactional workloads. + it will also look (can look) much like the communication pattern + of http for www access. */ + +void +send_tcp_conn_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\n\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + int myport; + int ret; + + struct tcp_conn_rr_request_struct *tcp_conn_rr_request; + struct tcp_conn_rr_response_struct *tcp_conn_rr_response; + struct tcp_conn_rr_results_struct *tcp_conn_rr_result; + + tcp_conn_rr_request = + (struct tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_conn_rr_response = + (struct tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_conn_rr_result = + (struct tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data; + + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP Connect/Request/Response TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + + + if (debug) { + fprintf(where,"send_tcp_conn_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup message. If + the user did not specify any of the parameters, they will be + passed as 0, which will indicate to the remote that no changes + beyond the system's default should be used. Alignment is the + exception, it will default to 8, which will be no alignment + alterations. */ + + netperf_request.content.request_type = DO_TCP_CRR; + tcp_conn_rr_request->recv_buf_size = rsr_size_req; + tcp_conn_rr_request->send_buf_size = rss_size_req; + tcp_conn_rr_request->recv_alignment = remote_recv_align; + tcp_conn_rr_request->recv_offset = remote_recv_offset; + tcp_conn_rr_request->send_alignment = remote_send_align; + tcp_conn_rr_request->send_offset = remote_send_offset; + tcp_conn_rr_request->request_size = req_size; + tcp_conn_rr_request->response_size = rsp_size; + tcp_conn_rr_request->no_delay = rem_nodelay; + tcp_conn_rr_request->measure_cpu = remote_cpu_usage; + tcp_conn_rr_request->cpu_rate = remote_cpu_rate; + tcp_conn_rr_request->so_rcvavoid = rem_rcvavoid; + tcp_conn_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + tcp_conn_rr_request->test_length = test_time; + } + else { + tcp_conn_rr_request->test_length = test_trans * -1; + } + tcp_conn_rr_request->port = atoi(remote_data_port); + tcp_conn_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_tcp_conn_rr: requesting TCP crr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will have + done all the needed set-up we will have calibrated the cpu + locally before sending the request, and will grab the counter + value right after the connect returns. The remote will grab the + counter right after the accept call. This saves the hassle of + extra messages being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + rsr_size = tcp_conn_rr_response->recv_buf_size; + rss_size = tcp_conn_rr_response->send_buf_size; + rem_nodelay = tcp_conn_rr_response->no_delay; + remote_cpu_usage = tcp_conn_rr_response->measure_cpu; + remote_cpu_rate = tcp_conn_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res, + (unsigned short)tcp_conn_rr_response->data_port_number); + + if (debug) { + fprintf(where,"remote listen done.\n"); + fprintf(where,"remote port is %u\n",get_port_number(remote_res)); + fflush(where); + } + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + } +#ifdef WANT_DEMO + DEMO_RR_SETUP(100) +#endif + + /* pick a nice random spot between client_port_min and */ + /* client_port_max for our initial port number */ + srand(getpid()); + if (client_port_max - client_port_min) { + myport = client_port_min + + (rand() % (client_port_max - client_port_min)); + } + else { + myport = client_port_min; + } + /* there will be a ++ before the first call to bind, so subtract one */ + myport--; + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + + cpu_start(local_cpu_usage); + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to create the socket, and then */ + /* again just after the receive raj 3/95 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + +newport: + /* pick a new port number */ + myport++; + + /* wrap the port number when we get to client_port_max. NOTE, some */ + /* broken TCP's might treat the port number as a signed 16 bit */ + /* quantity. we aren't interested in testing such broken */ + /* implementations :) so we won't make sure that it is below 32767 */ + /* raj 8/94 */ + if (myport >= client_port_max) { + myport = client_port_min; + } + + /* we do not want to use the port number that the server is */ + /* sitting at - this would cause us to fail in a loopback test. we */ + /* could just rely on the failure of the bind to get us past this, */ + /* but I'm guessing that in this one case at least, it is much */ + /* faster, given that we *know* that port number is already in use */ + /* (or rather would be in a loopback test) */ + + if (myport == get_port_number(remote_res)) myport++; + + if (debug) { + if ((nummessages % 100) == 0) { + printf("port %d\n",myport); + } + } + + /* set up the data socket */ + set_port_number(local_res, (unsigned short)myport); + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET) { + perror("netperf: send_tcp_conn_rr: tcp stream data socket"); + exit(1); + } + + + /* we used to call bind here, but that is now taken-care-of by the + create_data_socket routine. */ + + /* Connect up to the remote port on the data socket */ + if ((ret = connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen)) == INVALID_SOCKET){ + if (SOCKET_EINTR(ret)) + { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + if ((SOCKET_EADDRINUSE(ret)) || SOCKET_EADDRNOTAVAIL(ret)) { + /* likely something our explicit bind() would have caught in + the past, so go get another port, via create_data_socket. + yes, this is a bit more overhead than before, but the + condition should be rather rare. raj 2005-02-08 */ + close(send_socket); + goto newport; + } + perror("netperf: data socket connect failed"); + printf("\tattempted to connect on socket %d to port %d", + send_socket, + get_port_number(remote_res)); + printf(" from port %d \n",get_port_number(local_res)); + exit(1); + } + + + /* send the request */ + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (SOCKET_EINTR(len)) + { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_conn_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + + + do { + rsp_bytes_recvd = recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0); + if (rsp_bytes_recvd > 0) { + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + else { + break; + } + } while (rsp_bytes_left); + + + /* OK, we are out of the loop - now what? */ + if (rsp_bytes_recvd < 0) { + /* did the timer hit, or was there an error? */ + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_conn_rr: data recv error"); + exit(1); + } + + /* if this is a no_control test, we initiate connection close, + otherwise the remote netserver does it to remain just like + previous behaviour. raj 2007-27-08 */ + if (!no_control) { + shutdown(send_socket,SHUT_WR); + } + + /* we are expecting to get either a return of zero indicating + connection close, or an error. */ + rsp_bytes_recvd = recv(send_socket, + temp_message_ptr, + 1, + 0); + + /* our exit from the while loop should generally be when */ + /* tmp_bytes_recvd is equal to zero, which implies the connection */ + /* has been closed by the server side. By waiting until we get the */ + /* zero return we can avoid race conditions that stick us with the */ + /* TIME_WAIT connection and not the server. raj 8/96 */ + + if (rsp_bytes_recvd == 0) { + /* connection close, call close. we assume that the requisite */ + /* number of bytes have been received */ + recv_ring = recv_ring->next; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_RR_INTERVAL(1) +#endif + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed on local port %d\n", + nummessages, + get_port_number(local_res)); + fflush(where); + } + + close(send_socket); + + } + else { + /* it was less than zero - an error occured */ + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_conn_rr: data recv error"); + exit(1); + } + + } + + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting things. If + it wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where, + "WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where, + "Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where, + "DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where, + "Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = tcp_conn_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + tcp_conn_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + +void +recv_tcp_conn_rr() +{ + + char *message; + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in, peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + char *recv_message_ptr; + char *send_message_ptr; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct tcp_conn_rr_request_struct *tcp_conn_rr_request; + struct tcp_conn_rr_response_struct *tcp_conn_rr_response; + struct tcp_conn_rr_results_struct *tcp_conn_rr_results; + + tcp_conn_rr_request = + (struct tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_conn_rr_response = + (struct tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_conn_rr_results = + (struct tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_conn_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_conn_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_CRR_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_conn_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_tcp_conn_rr: requested recv alignment of %d offset %d\n", + tcp_conn_rr_request->recv_alignment, + tcp_conn_rr_request->recv_offset); + fprintf(where, + "recv_tcp_conn_rr: requested send alignment of %d offset %d\n", + tcp_conn_rr_request->send_alignment, + tcp_conn_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, tcp_conn_rr_request->recv_alignment, tcp_conn_rr_request->recv_offset); + + send_message_ptr = ALIGN_BUFFER(message, tcp_conn_rr_request->send_alignment, tcp_conn_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_tcp_conn_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_conn_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_conn_rr_request->send_buf_size; + lsr_size_req = tcp_conn_rr_request->recv_buf_size; + loc_nodelay = tcp_conn_rr_request->no_delay; + loc_rcvavoid = tcp_conn_rr_request->so_rcvavoid; + loc_sndavoid = tcp_conn_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_conn_rr_request->ipfamily), + tcp_conn_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_conn_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + if (debug) { + fprintf(where,"could not create data socket\n"); + fflush(where); + } + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not listen\n"); + fflush(where); + } + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not getsockname\n"); + fflush(where); + } + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_conn_rr_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + if (debug) { + fprintf(where,"telling the remote to call me at %d\n", + tcp_conn_rr_response->data_port_number); + fflush(where); + } + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + tcp_conn_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (tcp_conn_rr_request->measure_cpu) { + tcp_conn_rr_response->measure_cpu = 1; + tcp_conn_rr_response->cpu_rate = + calibrate_local_cpu(tcp_conn_rr_request->cpu_rate); + } + + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_conn_rr_response->send_buf_size = lss_size; + tcp_conn_rr_response->recv_buf_size = lsr_size; + tcp_conn_rr_response->no_delay = loc_nodelay; + tcp_conn_rr_response->so_rcvavoid = loc_rcvavoid; + tcp_conn_rr_response->so_sndavoid = loc_sndavoid; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_conn_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (tcp_conn_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(tcp_conn_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = tcp_conn_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + /* accept a connection from the remote */ +#ifdef WIN32 + /* The test timer will probably fire during this accept, + so to make the start_timer above work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket = s_listen; +#endif + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + fprintf(where,"recv_tcp_conn_rr: accept: errno = %d\n",errno); + fflush(where); + close(s_listen); + + exit(1); + } + + if (debug) { + fprintf(where,"recv_tcp_conn_rr: accepted data connection.\n"); + fflush(where); + } + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + temp_message_ptr = recv_message_ptr; + request_bytes_remaining = tcp_conn_rr_request->request_size; + + /* receive the request from the other side */ + while (!times_up && (request_bytes_remaining > 0)) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(request_bytes_recvd)) + { + /* the timer popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + fprintf(where,"yo5\n"); + fflush(where); + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=send(s_data, + send_message_ptr, + tcp_conn_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 99; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_tcp_conn_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + + /* close the connection. the server will likely do a graceful */ + /* close of the connection, insuring that all data has arrived at */ + /* the client. for this it will call shutdown(), and then recv() and */ + /* then close(). I'm reasonably confident that this is the */ + /* appropriate sequence of calls - I would like to hear of */ + /* examples in web servers to the contrary. raj 10/95*/ +#ifdef TCP_CRR_SHUTDOWN + shutdown(s_data,SHUT_WR); + recv(s_data, + recv_message_ptr, + 1, + 0); + close(s_data); +#else + close(s_data); +#endif /* TCP_CRR_SHUTDOWN */ + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(tcp_conn_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_conn_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + tcp_conn_rr_results->bytes_received = (trans_received * + (tcp_conn_rr_request->request_size + + tcp_conn_rr_request->response_size)); + tcp_conn_rr_results->trans_received = trans_received; + tcp_conn_rr_results->elapsed_time = elapsed_time; + if (tcp_conn_rr_request->measure_cpu) { + tcp_conn_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_tcp_conn_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + + +#ifdef DO_1644 + + /* this test is intended to test the performance of establishing a */ + /* connection, exchanging a request/response pair, and repeating. it */ + /* is expected that this would be a good starting-point for */ + /* comparision of T/TCP with classic TCP for transactional workloads. */ + /* it will also look (can look) much like the communication pattern */ + /* of http for www access. */ + +int +send_tcp_tran_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\n\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int one = 1; + int timed_out = 0; + float elapsed_time; + + int len; + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + int sock_opt_len = sizeof(int); + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct hostent *hp; + struct sockaddr_in server; + struct sockaddr_in *myaddr; + unsigned int addr; + int myport; + + struct tcp_tran_rr_request_struct *tcp_tran_rr_request; + struct tcp_tran_rr_response_struct *tcp_tran_rr_response; + struct tcp_tran_rr_results_struct *tcp_tran_rr_result; + + tcp_tran_rr_request = + (struct tcp_tran_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_tran_rr_response = + (struct tcp_tran_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_tran_rr_result = + (struct tcp_tran_rr_results_struct *)netperf_response.content.test_specific_data; + + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + myaddr = (struct sockaddr_storage *)malloc(sizeof(struct sockaddr_storage)); + if (myaddr == NULL) { + printf("malloc(%d) failed!\n", sizeof(struct sockaddr_storage)); + exit(1); + } + + bzero((char *)&server, + sizeof(server)); + bzero((char *)myaddr, + sizeof(struct sockaddr_storage)); + myaddr->sin_family = AF_INET; + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP Transactional/Request/Response TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + + + if (debug) { + fprintf(where,"send_tcp_tran_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_TRR; + tcp_tran_rr_request->recv_buf_size = rsr_size_req; + tcp_tran_rr_request->send_buf_size = rss_size_req; + tcp_tran_rr_request->recv_alignment = remote_recv_align; + tcp_tran_rr_request->recv_offset = remote_recv_offset; + tcp_tran_rr_request->send_alignment = remote_send_align; + tcp_tran_rr_request->send_offset = remote_send_offset; + tcp_tran_rr_request->request_size = req_size; + tcp_tran_rr_request->response_size = rsp_size; + tcp_tran_rr_request->no_delay = rem_nodelay; + tcp_tran_rr_request->measure_cpu = remote_cpu_usage; + tcp_tran_rr_request->cpu_rate = remote_cpu_rate; + tcp_tran_rr_request->so_rcvavoid = rem_rcvavoid; + tcp_tran_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + tcp_tran_rr_request->test_length = test_time; + } + else { + tcp_tran_rr_request->test_length = test_trans * -1; + } + tcp_tran_rr_request->port = atoi(remote_data_port); + tcp_tran_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_tcp_tran_rr: requesting TCP_TRR test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + rsr_size = tcp_tran_rr_response->recv_buf_size; + rss_size = tcp_tran_rr_response->send_buf_size; + rem_nodelay = tcp_tran_rr_response->no_delay; + remote_cpu_usage= tcp_tran_rr_response->measure_cpu; + remote_cpu_rate = tcp_tran_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + server.sin_port = tcp_tran_rr_response->data_port_number; + server.sin_port = htons(server.sin_port); + if (debug) { + fprintf(where,"remote listen done.\n"); + fprintf(where,"remote port is %d\n",ntohs(server.sin_port)); + fflush(where); + } + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + + /* pick a nice random spot between client_port_min and */ + /* client_port_max for our initial port number. if they are the */ + /* same, then just set to _min */ + if (client_port_max - client_port_min) { + srand(getpid()); + myport = client_port_min + + (rand() % (client_port_max - client_port_min)); + } + else { + myport = client_port_min; + } + + /* there will be a ++ before the first call to bind, so subtract one */ + myport--; + myaddr->sin_port = htons((unsigned short)myport); + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to create the socket, and then */ + /* again just after the receive raj 3/95 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + /* set up the data socket - is this really necessary or can I just */ + /* re-use the same socket and move this cal out of the while loop. */ + /* it does introcudea *boatload* of system calls. I guess that it */ + /* all depends on "reality of programming." keeping it this way is */ + /* a bit more conservative I imagine - raj 3/95 */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET) { + perror("netperf: send_tcp_tran_rr: tcp stream data socket"); + exit(1); + } + + /* we set SO_REUSEADDR on the premis that no unreserved port */ + /* number on the local system is going to be already connected to */ + /* the remote netserver's port number. One thing that I might */ + /* try later is to have the remote actually allocate a couple of */ + /* port numbers and cycle through those as well. depends on if we */ + /* can get through all the unreserved port numbers in less than */ + /* the length of the TIME_WAIT state raj 8/94 */ + one = 1; + if(setsockopt(send_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sock_opt_len) == SOCKET_ERROR) { + perror("netperf: send_tcp_tran_rr: so_reuseaddr"); + exit(1); + } + +newport: + /* pick a new port number */ + myport = ntohs(myaddr->sin_port); + myport++; + + /* we do not want to use the port number that the server is */ + /* sitting at - this would cause us to fail in a loopback test. we */ + /* could just rely on the failure of the bind to get us past this, */ + /* but I'm guessing that in this one case at least, it is much */ + /* faster, given that we *know* that port number is already in use */ + /* (or rather would be in a loopback test) */ + + if (myport == ntohs(server.sin_port)) myport++; + + /* wrap the port number when we get to 65535. NOTE, some broken */ + /* TCP's might treat the port number as a signed 16 bit quantity. */ + /* we aren't interested in testing such broken implementations :) */ + /* raj 8/94 */ + if (myport >= client_port_max) { + myport = client_port_min; + } + myaddr->sin_port = htons((unsigned short)myport); + + if (debug) { + if ((nummessages % 100) == 0) { + printf("port %d\n",myport); + } + } + + /* we want to bind our socket to a particular port number. */ + if (bind(send_socket, + (struct sockaddr *)myaddr, + sizeof(struct sockaddr_storage)) == SOCKET_ERROR) { + /* if the bind failed, someone else must have that port number */ + /* - perhaps in the listen state. since we can't use it, skip to */ + /* the next port number. we may have to do this again later, but */ + /* that's just too bad :) */ + if (debug > 1) { + fprintf(where, + "send_tcp_tran_rr: tried to bind to port %d errno %d\n", + ntohs(myaddr->sin_port), + errno); + fflush(where); + } + /* yes, goto's are supposed to be evil, but they do have their */ + /* uses from time to time. the real world doesn't always have */ + /* to code to ge tthe A in CS 101 :) raj 3/95 */ + goto newport; + } + + /* Connect up to the remote port on the data socket. Since this is */ + /* a test for RFC_1644-style transactional TCP, we can use the */ + /* sendto() call instead of calling connect and then send() */ + + /* send the request */ + if((len=sendto(send_socket, + send_ring->buffer_ptr, + req_size, + MSG_EOF, + (struct sockaddr *)&server, + sizeof(server))) != req_size) { + if (SOCKET_EINTR(len)) + { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_tran_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_tran_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + close(send_socket); + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed on local port %d\n", + nummessages, + ntohs(myaddr->sin_port)); + fflush(where); + } + + + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = tcp_tran_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + tcp_tran_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + +int +recv_tcp_tran_rr() +{ + + char *message; + struct sockaddr_in myaddr_in, + peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + int NoPush = 1; + + char *recv_message_ptr; + char *send_message_ptr; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct tcp_tran_rr_request_struct *tcp_tran_rr_request; + struct tcp_tran_rr_response_struct *tcp_tran_rr_response; + struct tcp_tran_rr_results_struct *tcp_tran_rr_results; + + tcp_tran_rr_request = + (struct tcp_tran_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_tran_rr_response = + (struct tcp_tran_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_tran_rr_results = + (struct tcp_tran_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_tran_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_tran_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_TRR_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_tran_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_tcp_tran_rr: requested recv alignment of %d offset %d\n", + tcp_tran_rr_request->recv_alignment, + tcp_tran_rr_request->recv_offset); + fprintf(where, + "recv_tcp_tran_rr: requested send alignment of %d offset %d\n", + tcp_tran_rr_request->send_alignment, + tcp_tran_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, tcp_tran_rr_request->recv_alignment, tcp_tran_rr_request->recv_offset); + + send_message_ptr = ALIGN_BUFFER(message, tcp_tran_rr_request->send_alignment, tcp_tran_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_tcp_tran_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = htons((unsigned short)tcp_tran_rr_request->port); + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_tran_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_tran_rr_request->send_buf_size; + lsr_size_req = tcp_tran_rr_request->recv_buf_size; + loc_nodelay = tcp_tran_rr_request->no_delay; + loc_rcvavoid = tcp_tran_rr_request->so_rcvavoid; + loc_sndavoid = tcp_tran_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_tran_rr_request->ipfamily), + tcp_tran_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_tran_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + if (debug) { + fprintf(where,"could not create data socket\n"); + fflush(where); + } + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + if (bind(s_listen, + (struct sockaddr *)&myaddr_in, + sizeof(myaddr_in)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not bind\n"); + fflush(where); + } + exit(1); + } + + /* we want to disable the implicit PUSH on all sends. at some point, */ + /* this might want to be a parm to the test raj 3/95 */ + if (setsockopt(s_listen, + IPPROTO_TCP, + TCP_NOPUSH, + (const char *)&NoPush, + sizeof(int)) == SOCKET_ERROR) { + fprintf(where, + "recv_tcp_tran_rr: could not set TCP_NOPUSH errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not listen\n"); + fflush(where); + } + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not geetsockname\n"); + fflush(where); + } + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_tran_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + if (debug) { + fprintf(where,"telling the remote to call me at %d\n", + tcp_tran_rr_response->data_port_number); + fflush(where); + } + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + tcp_tran_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (tcp_tran_rr_request->measure_cpu) { + tcp_tran_rr_response->measure_cpu = 1; + tcp_tran_rr_response->cpu_rate = + calibrate_local_cpu(tcp_tran_rr_request->cpu_rate); + } + + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_tran_rr_response->send_buf_size = lss_size; + tcp_tran_rr_response->recv_buf_size = lsr_size; + tcp_tran_rr_response->no_delay = loc_nodelay; + tcp_tran_rr_response->so_rcvavoid = loc_rcvavoid; + tcp_tran_rr_response->so_sndavoid = loc_sndavoid; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_tran_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (tcp_tran_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(tcp_tran_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = tcp_tran_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + /* accept a connection from the remote */ + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + fprintf(where,"recv_tcp_tran_rr: accept: errno = %d\n",errno); + fflush(where); + close(s_listen); + + exit(1); + } + + if (debug) { + fprintf(where,"recv_tcp_tran_rr: accepted data connection.\n"); + fflush(where); + } + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + temp_message_ptr = recv_message_ptr; + request_bytes_remaining = tcp_tran_rr_request->request_size; + + /* receive the request from the other side. we can just receive */ + /* until we get zero bytes, but that would be a slight structure */ + /* change in the code, with minimal perfomance effects. If */ + /* however, I has variable-length messages, I would want to do */ + /* this to avoid needing "double reads" - one for the message */ + /* length, and one for the rest of the message raj 3/95 */ + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if ( SOCKET_EINTR(request_bytes_recvd) ) + { + /* the timer popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + fprintf(where,"yo5\n"); + fflush(where); + break; + } + + /* Now, send the response to the remote we can use sendto here to */ + /* help remind people that this is an rfc 1644 style of test */ + if((bytes_sent=sendto(s_data, + send_message_ptr, + tcp_tran_rr_request->response_size, + MSG_EOF, + (struct sockaddr *)&peeraddr_in, + sizeof(struct sockaddr_storage))) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_sent)) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 99; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_tcp_tran_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + + /* close the connection. since we have disable PUSH on sends, the */ + /* FIN should be tacked-onto our last send instead of being */ + /* standalone */ + close(s_data); + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(tcp_tran_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_tran_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + tcp_tran_rr_results->bytes_received = (trans_received * + (tcp_tran_rr_request->request_size + + tcp_tran_rr_request->response_size)); + tcp_tran_rr_results->trans_received = trans_received; + tcp_tran_rr_results->elapsed_time = elapsed_time; + if (tcp_tran_rr_request->measure_cpu) { + tcp_tran_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_tcp_tran_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} +#endif /* DO_1644 */ + +#ifdef DO_NBRR + /* this routine implements the sending (netperf) side of the TCP_RR */ + /* test using POSIX-style non-blocking sockets. */ + +void +send_tcp_nbrr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct hostent *hp; + struct sockaddr_storage server; + unsigned int addr; + + struct tcp_rr_request_struct *tcp_rr_request; + struct tcp_rr_response_struct *tcp_rr_response; + struct tcp_rr_results_struct *tcp_rr_result; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + tcp_rr_request = + (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_rr_response= + (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_rr_result = + (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP Non-Blocking REQUEST/RESPONSE TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_tcp_nbrr: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_tcp_nbrr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_NBRR; + tcp_rr_request->recv_buf_size = rsr_size_req; + tcp_rr_request->send_buf_size = rss_size_req; + tcp_rr_request->recv_alignment = remote_recv_align; + tcp_rr_request->recv_offset = remote_recv_offset; + tcp_rr_request->send_alignment = remote_send_align; + tcp_rr_request->send_offset = remote_send_offset; + tcp_rr_request->request_size = req_size; + tcp_rr_request->response_size = rsp_size; + tcp_rr_request->no_delay = rem_nodelay; + tcp_rr_request->measure_cpu = remote_cpu_usage; + tcp_rr_request->cpu_rate = remote_cpu_rate; + tcp_rr_request->so_rcvavoid = rem_rcvavoid; + tcp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + tcp_rr_request->test_length = test_time; + } + else { + tcp_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_tcp_nbrr: requesting TCP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = tcp_rr_response->recv_buf_size; + rss_size = tcp_rr_response->send_buf_size; + rem_nodelay = tcp_rr_response->no_delay; + remote_cpu_usage = tcp_rr_response->measure_cpu; + remote_cpu_rate = tcp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + server.sin_port = (unsigned short)tcp_rr_response->data_port_number; + server.sin_port = htons(server.sin_port); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: data socket connect failed"); + + exit(1); + } + + /* now that we are connected, mark the socket as non-blocking */ + if (!set_nonblock(send_socket)) { + perror("netperf: set_nonblock"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to send, and then again just */ + /* after the receive raj 8/94 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + /* even though this is a non-blocking socket, we will assume for */ + /* the time being that we will be able to send an entire request */ + /* without getting an EAGAIN */ + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (SOCKET_EINTR(len)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_nbrr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response. since we are using non-blocking I/O, we */ + /* will "spin" on the recvs */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } +#ifndef WIN32 // But what does WinNT indicate in this situation... + else if (errno == EAGAIN) { + Set_errno(0); + continue; + } +#endif + else { + perror("send_tcp_nbrr: data recv error"); + exit(1); + } + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + + /* At this point we used to call shutdown on the data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = tcp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + tcp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + close(send_socket); + + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(tcp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + /* this routine implements the receive (netserver) side of a TCP_RR */ + /* test */ +void +recv_tcp_nbrr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct sockaddr_in myaddr_in, + peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct tcp_rr_request_struct *tcp_rr_request; + struct tcp_rr_response_struct *tcp_rr_response; + struct tcp_rr_results_struct *tcp_rr_results; + + tcp_rr_request = + (struct tcp_rr_request_struct *)netperf_request.content.test_specific_data; + tcp_rr_response = + (struct tcp_rr_response_struct *)netperf_response.content.test_specific_data; + tcp_rr_results = + (struct tcp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_nbrr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_nbrr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_nbrr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_tcp_nbrr: requested recv alignment of %d offset %d\n", + tcp_rr_request->recv_alignment, + tcp_rr_request->recv_offset); + fprintf(where,"recv_tcp_nbrr: requested send alignment of %d offset %d\n", + tcp_rr_request->send_alignment, + tcp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + tcp_rr_request->response_size, + tcp_rr_request->send_alignment, + tcp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + tcp_rr_request->request_size, + tcp_rr_request->recv_alignment, + tcp_rr_request->recv_offset); + + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = htons((unsigned short)tcp_rr_request->port); + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_nbrr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_rr_request->send_buf_size; + lsr_size_req = tcp_rr_request->recv_buf_size; + loc_nodelay = tcp_rr_request->no_delay; + loc_rcvavoid = tcp_rr_request->so_rcvavoid; + loc_sndavoid = tcp_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_rr_request->ipfamily), + tcp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + if (bind(s_listen, + (struct sockaddr *)&myaddr_in, + sizeof(myaddr_in)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + tcp_rr_response->cpu_rate = 0.0; /* assume no cpu */ + tcp_rr_response->measure_cpu = 0; + + if (tcp_rr_request->measure_cpu) { + tcp_rr_response->measure_cpu = 1; + tcp_rr_response->cpu_rate = calibrate_local_cpu(tcp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_rr_response->send_buf_size = lss_size; + tcp_rr_response->recv_buf_size = lsr_size; + tcp_rr_response->no_delay = loc_nodelay; + tcp_rr_response->so_rcvavoid = loc_rcvavoid; + tcp_rr_response->so_sndavoid = loc_sndavoid; + tcp_rr_response->test_length = tcp_rr_request->test_length; + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + + if (debug) { + fprintf(where,"recv_tcp_nbrr: accept completes on the data connection.\n"); + fflush(where); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* now that we are connected, mark the socket as non-blocking */ + if (!set_nonblock(s_data)) { + close(s_data); + exit(1); + } + + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_rr_request->measure_cpu); + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (tcp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(tcp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = tcp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = tcp_rr_request->request_size; + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if ( SOCKET_EINTR(request_bytes_recvd)) + { + /* the timer popped */ + timed_out = 1; + break; + } +#ifndef WIN32 // But what does WinNT indicate in this situation... + else if (errno == EAGAIN) { + Set_errno(0); + if (times_up) { + timed_out = 1; + break; + } + continue; + } +#endif + else { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + fprintf(where,"yo5\n"); + fflush(where); + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=send(s_data, + send_ring->buffer_ptr, + tcp_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_sent)) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 992; + send_response(); + exit(1); + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(tcp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_nbrr: got %d transactions\n", + trans_received); + fflush(where); + } + + tcp_rr_results->bytes_received = (trans_received * + (tcp_rr_request->request_size + + tcp_rr_request->response_size)); + tcp_rr_results->trans_received = trans_received; + tcp_rr_results->elapsed_time = elapsed_time; + tcp_rr_results->cpu_method = cpu_method; + tcp_rr_results->num_cpus = lib_num_loc_cpus; + if (tcp_rr_request->measure_cpu) { + tcp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_tcp_nbrr: test complete, sending results.\n"); + fflush(where); + } + + /* we are done with the socket, free it */ + close(s_data); + + send_response(); + +} + +#endif /* DO_NBRR */ + + + /* this test is intended to test the performance of establishing a */ + /* connection, and then closing it again. this test is of somewhat */ + /* arcane interest since no packets are exchanged between the */ + /* user-space processes, but it will show the raw overhead of */ + /* establishing a TCP connection. that service demand could then be */ + /* compared with the sum of the service demands of a TCP_CRR and */ + /* TCP_RR test - presumeably, they would all relate */ + +void +send_tcp_cc(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\n\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + char temp_message_ptr[1]; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + int rsp_bytes_left = 1; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + int myport; + int ret; + + struct tcp_cc_request_struct *tcp_cc_request; + struct tcp_cc_response_struct *tcp_cc_response; + struct tcp_cc_results_struct *tcp_cc_result; + + tcp_cc_request = + (struct tcp_cc_request_struct *)netperf_request.content.test_specific_data; + tcp_cc_response = + (struct tcp_cc_response_struct *)netperf_response.content.test_specific_data; + tcp_cc_result = + (struct tcp_cc_results_struct *)netperf_response.content.test_specific_data; + + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("TCP Connect/Close TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* since there are no data buffers in this test, we need no send or */ + /* recv rings */ + + if (debug) { + fprintf(where,"send_tcp_cc: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_TCP_CC; + tcp_cc_request->recv_buf_size = rsr_size_req; + tcp_cc_request->send_buf_size = rss_size_req; + tcp_cc_request->recv_alignment = remote_recv_align; + tcp_cc_request->recv_offset = remote_recv_offset; + tcp_cc_request->send_alignment = remote_send_align; + tcp_cc_request->send_offset = remote_send_offset; + tcp_cc_request->request_size = req_size; + tcp_cc_request->response_size = rsp_size; + tcp_cc_request->no_delay = rem_nodelay; + tcp_cc_request->measure_cpu = remote_cpu_usage; + tcp_cc_request->cpu_rate = remote_cpu_rate; + tcp_cc_request->so_rcvavoid = rem_rcvavoid; + tcp_cc_request->so_sndavoid = rem_sndavoid; + if (test_time) { + tcp_cc_request->test_length = test_time; + } + else { + tcp_cc_request->test_length = test_trans * -1; + } + tcp_cc_request->port = atoi(remote_data_port); + tcp_cc_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_tcp_cc: requesting TCP crr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + rsr_size = tcp_cc_response->recv_buf_size; + rss_size = tcp_cc_response->send_buf_size; + rem_nodelay = tcp_cc_response->no_delay; + remote_cpu_usage= tcp_cc_response->measure_cpu; + remote_cpu_rate = tcp_cc_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res,(unsigned short)tcp_cc_response->data_port_number); + + if (debug) { + fprintf(where,"remote listen done.\n"); + fprintf(where,"remote port is %d\n",get_port_number(remote_res)); + fflush(where); + } + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + +#ifdef WANT_DEMO + DEMO_RR_SETUP(100) +#endif + + /* pick a nice random spot between client_port_min and */ + /* client_port_max for our initial port number */ + srand(getpid()); + if (client_port_max - client_port_min) { + myport = client_port_min + + (rand() % (client_port_max - client_port_min)); + } + else { + myport = client_port_min; + } + /* there will be a ++ before the first call to bind, so subtract one */ + myport--; + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to create the socket, and then */ + /* again just after the receive raj 3/95 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + /* set up the data socket */ + /* newport: is this label really required any longer? */ + /* pick a new port number */ + myport++; + + /* wrap the port number when we get to client_port_max. NOTE, some */ + /* broken TCP's might treat the port number as a signed 16 bit */ + /* quantity. we aren't interested in testing such broken */ + /* implementations :) so we won't make sure that it is below 32767 */ + /* raj 8/94 */ + if (myport >= client_port_max) { + myport = client_port_min; + } + + /* we do not want to use the port number that the server is */ + /* sitting at - this would cause us to fail in a loopback test. we */ + /* could just rely on the failure of the bind to get us past this, */ + /* but I'm guessing that in this one case at least, it is much */ + /* faster, given that we *know* that port number is already in use */ + /* (or rather would be in a loopback test) */ + + if (myport == get_port_number(remote_res)) myport++; + + if (debug) { + if ((nummessages % 100) == 0) { + printf("port %d\n",myport); + } + } + set_port_number(local_res, (unsigned short)myport); + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET) { + perror("netperf: send_tcp_cc: tcp stream data socket"); + exit(1); + } + + /* we used to have a call to bind() here, but that is being + taken care of by create_data_socket(). raj 2005-02-08 */ + + /* Connect up to the remote port on the data socket */ + if ((ret = connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen)) == INVALID_SOCKET){ + if (SOCKET_EINTR(ret)) + { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("netperf: data socket connect failed"); + printf("\tattempted to connect on socket %d to port %d", + send_socket, + get_port_number(remote_res)); + printf(" from port %u \n",get_port_number(local_res)); + exit(1); + } + + /* we hang in a recv() to get the remote's close indication */ + + rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0); + + + if (rsp_bytes_recvd == 0) { + /* connection close, call close. we assume that the requisite */ + /* number of bytes have been received */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_RR_INTERVAL(1) +#endif + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed on local port %u\n", + nummessages, + get_port_number(local_res)); + fflush(where); + } + + close(send_socket); + + } + else { + /* it was less than zero - an error occured */ + if (SOCKET_EINTR(rsp_bytes_recvd)) + { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_tcp_cc: data recv error"); + exit(1); + } + + } + + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = tcp_cc_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + tcp_cc_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + +void +recv_tcp_cc() +{ + + char *message; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_storage myaddr_in, peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + char *recv_message_ptr; + char *send_message_ptr; + int trans_received; + int trans_remaining; + int timed_out = 0; + float elapsed_time; + + struct tcp_cc_request_struct *tcp_cc_request; + struct tcp_cc_response_struct *tcp_cc_response; + struct tcp_cc_results_struct *tcp_cc_results; + + tcp_cc_request = + (struct tcp_cc_request_struct *)netperf_request.content.test_specific_data; + tcp_cc_response = + (struct tcp_cc_response_struct *)netperf_response.content.test_specific_data; + tcp_cc_results = + (struct tcp_cc_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_tcp_cc: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_tcp_cc: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = TCP_CC_RESPONSE; + + if (debug) { + fprintf(where,"recv_tcp_cc: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_tcp_cc: requested recv alignment of %d offset %d\n", + tcp_cc_request->recv_alignment, + tcp_cc_request->recv_offset); + fprintf(where, + "recv_tcp_cc: requested send alignment of %d offset %d\n", + tcp_cc_request->send_alignment, + tcp_cc_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, tcp_cc_request->recv_alignment, tcp_cc_request->recv_offset); + + send_message_ptr = ALIGN_BUFFER(message, tcp_cc_request->send_alignment, tcp_cc_request->send_offset); + + if (debug) { + fprintf(where,"recv_tcp_cc: receive alignment and offset set...\n"); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_tcp_cc: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = tcp_cc_request->send_buf_size; + lsr_size_req = tcp_cc_request->recv_buf_size; + loc_nodelay = tcp_cc_request->no_delay; + loc_rcvavoid = tcp_cc_request->so_rcvavoid; + loc_sndavoid = tcp_cc_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(tcp_cc_request->ipfamily), + tcp_cc_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(tcp_cc_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + if (debug) { + fprintf(where,"could not create data socket\n"); + fflush(where); + } + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not listen\n"); + fflush(where); + } + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not geetsockname\n"); + fflush(where); + } + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + tcp_cc_response->data_port_number = + (int) ntohs(((struct sockaddr_in *)&myaddr_in)->sin_port); + if (debug) { + fprintf(where,"telling the remote to call me at %d\n", + tcp_cc_response->data_port_number); + fflush(where); + } + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + tcp_cc_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (tcp_cc_request->measure_cpu) { + tcp_cc_response->measure_cpu = 1; + tcp_cc_response->cpu_rate = + calibrate_local_cpu(tcp_cc_request->cpu_rate); + } + + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + tcp_cc_response->send_buf_size = lss_size; + tcp_cc_response->recv_buf_size = lsr_size; + tcp_cc_response->no_delay = loc_nodelay; + tcp_cc_response->so_rcvavoid = loc_rcvavoid; + tcp_cc_response->so_sndavoid = loc_sndavoid; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(tcp_cc_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (tcp_cc_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(tcp_cc_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = tcp_cc_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { +#ifdef WIN32 + /* The test timer will probably fire during this accept, + so to make the start_timer above work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket = s_listen; +#endif + /* accept a connection from the remote */ + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + fprintf(where,"recv_tcp_cc: accept: errno = %d\n",errno); + fflush(where); + close(s_listen); + + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + if (debug) { + fprintf(where,"recv_tcp_cc: accepted data connection.\n"); + fflush(where); + } + + + /* close the connection. the server will likely do a graceful */ + /* close of the connection, insuring that all data has arrived at */ + /* the client. for this it will call shutdown(), and then recv() and */ + /* then close(). I'm reasonably confident that this is the */ + /* appropriate sequence of calls - I would like to hear of */ + /* examples in web servers to the contrary. raj 10/95*/ + close(s_data); + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_tcp_cc: Transaction %d complete\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(tcp_cc_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_tcp_cc: got %d transactions\n", + trans_received); + fflush(where); + } + + tcp_cc_results->bytes_received = (trans_received * + (tcp_cc_request->request_size + + tcp_cc_request->response_size)); + tcp_cc_results->trans_received = trans_received; + tcp_cc_results->elapsed_time = elapsed_time; + if (tcp_cc_request->measure_cpu) { + tcp_cc_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_tcp_cc: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +void +print_sockets_usage() +{ + + fwrite(sockets_usage, sizeof(char), strlen(sockets_usage), stdout); + exit(1); + +} + +void +scan_sockets_args(int argc, char *argv[]) + +{ + +#define SOCKETS_ARGS "b:CDnNhH:L:m:M:p:P:r:s:S:T:Vw:W:z46" + + extern char *optarg; /* pointer to option string */ + + int c; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (debug) { + int i; + printf("%s called with the following argument vector\n", + __func__); + for (i = 0; i< argc; i++) { + printf("%s ",argv[i]); + } + printf("\n"); + } + + strncpy(local_data_port,"0",sizeof(local_data_port)); + strncpy(remote_data_port,"0",sizeof(remote_data_port)); + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form "first," (see the routine break_args.. */ + + while ((c= getopt(argc, argv, SOCKETS_ARGS)) != EOF) { + switch (c) { + case '?': + case '4': + remote_data_family = AF_INET; + local_data_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + remote_data_family = AF_INET6; + local_data_family = AF_INET6; +#else + fprintf(stderr, + "This netperf was not compiled on an IPv6 capable host!\n"); + fflush(stderr); + exit(-1); +#endif + break; + case 'h': + print_sockets_usage(); + exit(1); + case 'b': +#ifdef WANT_FIRST_BURST + first_burst_size = atoi(optarg); +#else /* WANT_FIRST_BURST */ + printf("Initial request burst functionality not compiled-in!\n"); +#endif /* WANT_FIRST_BURST */ + break; + case 'C': +#ifdef TCP_CORK + /* set TCP_CORK */ + loc_tcpcork = 1; + rem_tcpcork = 1; /* however, at first, we ony have cork affect loc */ +#else + printf("WARNING: TCP_CORK not available on this platform!\n"); +#endif /* TCP_CORK */ + break; + case 'D': + /* set the TCP nodelay flag */ + loc_nodelay = 1; + rem_nodelay = 1; + break; + case 'H': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + remote_data_address = malloc(strlen(arg1)+1); + strncpy(remote_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + remote_data_family = parse_address_family(arg2); + break; + case 'L': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + local_data_address = malloc(strlen(arg1)+1); + strncpy(local_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + local_data_family = parse_address_family(arg2); + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size_req = convert(arg1); + if (arg2[0]) + lsr_size_req = convert(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size_req = convert(arg1); + if (arg2[0]) + rsr_size_req = convert(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = convert(arg1); + if (arg2[0]) + rsp_size = convert(arg2); + break; + case 'm': + /* set the send size */ + send_size = convert(optarg); + break; + case 'M': + /* set the recv size */ + recv_size = convert(optarg); + break; + case 'n': + /* set the local socket type*/ + local_connected = 1; + break; + case 'N': + /* set the remote socket type*/ + remote_connected = 1; + break; + case 'p': + /* set the min and max port numbers for the TCP_CRR and TCP_TRR */ + /* tests. */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + client_port_min = atoi(arg1); + if (arg2[0]) + client_port_max = atoi(arg2); + break; + case 'P': + /* set the local and remote data port numbers for the tests to + allow them to run through those blankety blank end-to-end + breaking firewalls. raj 2004-06-15 */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_data_port,arg1,sizeof(local_data_port)); + if (arg2[0]) + strncpy(remote_data_port,arg2,sizeof(remote_data_port)); + break; + case 't': + /* set the test name */ + strcpy(test_name,optarg); + break; + case 'W': + /* set the "width" of the user space data */ + /* buffer. This will be the number of */ + /* send_size buffers malloc'd in the */ + /* *_STREAM test. It may be enhanced to set */ + /* both send and receive "widths" but for now */ + /* it is just the sending *_STREAM. */ + send_width = convert(optarg); + break; + case 'V' : + /* we want to do copy avoidance and will set */ + /* it for everything, everywhere, if we really */ + /* can. of course, we don't know anything */ + /* about the remote... */ +#ifdef SO_SND_COPYAVOID + loc_sndavoid = 1; +#else + loc_sndavoid = 0; + printf("Local send copy avoidance not available.\n"); +#endif +#ifdef SO_RCV_COPYAVOID + loc_rcvavoid = 1; +#else + loc_rcvavoid = 0; + printf("Local recv copy avoidance not available.\n"); +#endif + rem_sndavoid = 1; + rem_rcvavoid = 1; + break; + }; + } + +#if defined(WANT_FIRST_BURST) +#if defined(WANT_HISTOGRAM) + /* if WANT_FIRST_BURST and WANT_HISTOGRAM are defined and the user + indeed wants a non-zero first burst size, and we would emit a + histogram, then we should emit a warning that the two are not + compatible. raj 2006-01-31 */ + if ((first_burst_size > 0) && (verbosity >= 2)) { + fprintf(stderr, + "WARNING! Histograms and first bursts are incompatible!\n"); + fflush(stderr); + } +#endif +#endif + + /* we do not want to make remote_data_address non-NULL because if + the user has not specified a remote adata address, we want to + take it from the hostname in the -H global option. raj + 2005-02-08 */ + + /* so, if there is to be no control connection, we want to have some + different settings for a few things */ + + if (no_control) { + + if (strcmp(remote_data_port,"0") == 0) { + /* we need to select either the discard port, echo port or + chargen port dedepending on the test name. raj 2007-02-08 */ + if (strstr(test_name,"STREAM") || + strstr(test_name,"SENDFILE")) { + strncpy(remote_data_port,"discard",sizeof(remote_data_port)); + } + else if (strstr(test_name,"RR")) { + strncpy(remote_data_port,"echo",sizeof(remote_data_port)); + } + else if (strstr(test_name,"MAERTS")) { + strncpy(remote_data_port,"chargen",sizeof(remote_data_port)); + } + else { + printf("No default port known for the %s test, please set one yourself\n",test_name); + exit(-1); + } + } + remote_data_port[sizeof(remote_data_port) - 1] = '\0'; + + /* I go back and forth on whether these should become -1 or if + they should become 0 for a no_control test. what do you think? + raj 2006-02-08 */ + + rem_rcvavoid = -1; + rem_sndavoid = -1; + rss_size_req = -1; + rsr_size_req = -1; + rem_nodelay = -1; + + if (strstr(test_name,"STREAM") || + strstr(test_name,"SENDFILE")) { + recv_size = -1; + } + else if (strstr(test_name,"RR")) { + /* I am however _certain_ that for a no control RR test the + response size must equal the request size since 99 times out + of ten we will be speaking to the echo service somewhere */ + rsp_size = req_size; + } + else if (strstr(test_name,"MAERTS")) { + send_size = -1; + } + else { + printf("No default port known for the %s test, please set one yourself\n",test_name); + exit(-1); + } + } +} diff --git a/nettest_bsd.h b/nettest_bsd.h new file mode 100644 index 0000000..5491290 --- /dev/null +++ b/nettest_bsd.h @@ -0,0 +1,471 @@ +/* + Copyright (C) 1993-2004 Hewlett-Packard Company +*/ + + /* This file contains the test-specific definitions for netperf's BSD */ + /* sockets tests */ + +/* well boys and girls, seems that while AF_INET is "2" and AF_UNSPEC + is "0" the world over, AF_INET6 is different values depending on + the platform... grrr. On HP-UX 11i it is "22" and on Linux 2.6 it + is "10" sooooo... we have to define our own space for netperf to + enable us to pass values around from machine to machine. raj + 2005-02-08 */ +#define NF_UNSPEC 0 +#define NF_INET 4 +#define NF_INET6 6 + +struct tcp_stream_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int receive_size; /* how many bytes do we want to receive at one */ + /* time? */ + int recv_alignment; /* what is the alignment of the receive */ + /* buffer? */ + int recv_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; /* the address family of ipaddress */ +}; + +struct tcp_stream_response_struct { + int recv_buf_size; /* how big does the client want it */ + int receive_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_stream_results_struct { + double bytes_received; + unsigned int recv_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct tcp_maerts_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int send_size; /* how many bytes do we want netserver to send + at one time? */ + int send_alignment; /* what is the alignment of the send */ + /* buffer? */ + int send_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the send buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + +struct tcp_maerts_response_struct { + int recv_buf_size; /* how big does the client want it */ + int send_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_maerts_results_struct { + double bytes_sent; + unsigned int send_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct tcp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + +struct tcp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct tcp_conn_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + + +struct tcp_conn_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_conn_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct tcp_tran_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + + +struct tcp_tran_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_tran_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ + +}; + +struct udp_stream_request_struct { + int recv_buf_size; + int message_size; + int recv_connected; + int recv_alignment; + int recv_offset; + int checksum_off; + int measure_cpu; + float cpu_rate; + int test_length; + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; + +}; + +struct udp_stream_response_struct { + int recv_buf_size; + int send_buf_size; + int measure_cpu; + int test_length; + int data_port_number; + float cpu_rate; + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct udp_stream_results_struct { + unsigned int messages_recvd; + unsigned int bytes_received; + float elapsed_time; + float cpu_util; + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + + +struct udp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + +struct udp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct udp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct tcp_cc_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + + +struct tcp_cc_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct tcp_cc_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +extern int rss_size_req, /* requested remote socket send buffer size */ + rsr_size_req, /* requested remote socket recv buffer size */ + rss_size, /* remote socket send buffer size */ + rsr_size, /* remote socket recv buffer size */ + lss_size_req, /* requested local socket send buffer size */ + lsr_size_req, /* requested local socket recv buffer size */ + lss_size, /* local socket send buffer size */ + lsr_size, /* local socket recv buffer size */ + req_size, /* request size */ + rsp_size, /* response size */ + send_size, /* how big are individual sends */ + recv_size, /* how big are individual receives */ + loc_nodelay, /* don't/do use NODELAY locally */ + rem_nodelay, /* don't/do use NODELAY remotely */ + loc_sndavoid, /* avoid send copies locally */ + loc_rcvavoid, /* avoid recv copies locally */ + rem_sndavoid, /* avoid send copies remotely */ + rem_rcvavoid; /* avoid recv_copies remotely */ + + +extern void scan_sockets_args(int argc, char *argv[]); +extern struct addrinfo *complete_addrinfo(char *controlhost, + char *data_address, + char *port, + int family, + int type, + int protocol, + int flags); +extern void complete_addrinfos(struct addrinfo **remote, + struct addrinfo **local, + char remote_host[], + int type, + int protocol, + int flags); +extern int af_to_nf(int af); +extern int nf_to_af(int nf); +extern void print_top_test_header(char test_name[], + struct addrinfo *source, + struct addrinfo *destination); +extern void set_port_number(struct addrinfo *res, + unsigned short port); +extern void set_hostname_and_port(char *hostname, + char *portstr, + int family, + int port); +extern void send_tcp_stream(char remote_host[]); +extern void send_tcp_maerts(char remote_host[]); +extern void send_tcp_rr(char remote_host[]); +extern void send_tcp_conn_rr(char remote_host[]); +extern void send_tcp_cc(char remote_host[]); +extern void send_udp_stream(char remote_host[]); +extern void send_udp_rr(char remote_host[]); + +extern void recv_tcp_stream(); +extern void recv_tcp_maerts(); +extern void recv_tcp_rr(); +extern void recv_tcp_conn_rr(); +extern void recv_tcp_cc(); +extern void recv_udp_stream(); +extern void recv_udp_rr(); + +extern void loc_cpu_rate(); +extern void rem_cpu_rate(); + +#ifdef HAVE_ICSC_EXS +extern void send_exs_tcp_stream(char remotehost[]); +#endif /* HAVE_ICSC_EXS */ + +#ifdef HAVE_SENDFILE +extern void sendfile_tcp_stream(char remotehost[]); +#endif /* HAVE_SENDFILE */ + +#if !defined(HAVE_STRUCT_SOCKADDR_STORAGE) && !defined(sockaddr_storage) +#define sockaddr_storage sockaddr_in +#endif + +#ifdef DO_NBRR +extern void send_tcp_nbrr(char remote_host[]); + +extern void recv_tcp_nbrr(); +#endif + diff --git a/nettest_dlpi.c b/nettest_dlpi.c new file mode 100644 index 0000000..ab3e79f --- /dev/null +++ b/nettest_dlpi.c @@ -0,0 +1,3798 @@ + +/****************************************************************/ +/* */ +/* nettest_dlpi.c */ +/* */ +/* the actual test routines... */ +/* */ +/* send_dlpi_co_stream() perform a CO DLPI stream test */ +/* recv_dlpi_co_stream() */ +/* send_dlpi_co_rr() perform a CO DLPI req/res */ +/* recv_dlpi_co_rr() */ +/* send_dlpi_cl_stream() perform a CL DLPI stream test */ +/* recv_dlpi_cl_stream() */ +/* send_dlpi_cl_rr() perform a CL DLPI req/res */ +/* recv_dlpi_cl_rr() */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WANT_DLPI +char nettest_dlpi_id[]="\ +@(#)nettest_dlpi.c (c) Copyright 1993,1995,2004 Hewlett-Packard Co. Version 2.4.3"; + +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <malloc.h> +#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__ */ + +#include "netlib.h" +#include "netsh.h" +#include "nettest_dlpi.h" + +/* these are some variables global to all the DLPI tests. declare */ +/* them static to make them global only to this file */ + +static int + rsw_size, /* remote send window size */ + rrw_size, /* remote recv window size */ + lsw_size, /* local send window size */ + lrw_size, /* local recv window size */ + req_size = 100, /* request size */ + rsp_size = 200, /* response size */ + send_size, /* how big are individual sends */ + recv_size; /* how big are individual receives */ + +int + loc_ppa = 4, /* the ppa for the local interface, */ + /* as shown as the NM Id in lanscan */ + rem_ppa = 4, /* the ppa for the remote interface */ + dlpi_sap = 84; /* which 802.2 SAP should we use? */ + +char loc_dlpi_device[32] = "/dev/dlpi"; +char rem_dlpi_device[32] = "/dev/dlpi"; + +char dlpi_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +CO/CL DLPI Test Options:\n\ + -D dev[,dev] Set the local/remote DLPI device file name\n\ + -h Display this text\n\ + -M bytes Set the recv size (DLCO_STREAM, DLCL_STREAM)\n\ + -m bytes Set the send size (DLCO_STREAM, DLCL_STREAM)\n\ + -p loc[,rem] Set the local/remote PPA for the test\n\ + -R bytes Set response size (DLCO_RR, DLCL_RR)\n\ + -r bytes Set request size (DLCO_RR, DLCL_RR)\n\ + -s sap Set the 802.2 sap for the test\n\ + -W send[,recv] Set remote send/recv window sizes\n\ + -w send[,recv] Set local send/recv window sizes\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + +/* This routine implements the CO unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_dlpi_co_stream() +{ + + char *tput_title = "\ +Recv Send Send \n\ +Window Window Message Elapsed \n\ +Size Size Size Time Throughput \n\ +frames frames bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Window Window Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +frames frames bytes secs. %-8.8s/s %% %% us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; +#endif /* WANT_INTERVALS */ + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *send_ring; + char *message; + char *message_ptr; + struct strbuf send_message; + char dlsap[BUFSIZ]; + int dlsap_len; + int *message_int_ptr; + int message_offset; + int malloc_size; + + int len; + int nummessages; + int send_descriptor; + int bytes_remaining; + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) */ + double bytes_sent; + +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct dlpi_co_stream_request_struct *dlpi_co_stream_request; + struct dlpi_co_stream_response_struct *dlpi_co_stream_response; + struct dlpi_co_stream_results_struct *dlpi_co_stream_result; + + dlpi_co_stream_request = + (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_stream_response = + (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_stream_result = + (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; + + if ( print_headers ) { + fprintf(where,"DLPI CO STREAM TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data descriptor */ + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_co_stream: dlpi stream data descriptor"); + exit(1); + } + + /* bind the puppy and get the assigned dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_co_rr: bind failure\n"); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_co_stream: send_descriptor obtained...\n"); + } + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_stream: window send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where, + "netperf: send_dlpi_co_stream: window recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* 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. */ + + + if (debug) { + fprintf(where, + "netperf: send_dlpi_co_stream: window sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + ffluch(where); + } + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* we should pick a default send_size, it should not be larger than */ + /* the min of the two interface MTU's, and should perhaps default to */ + /* the Interface MTU, but for now, we will default it to 1024... if */ + /* someone wants to change this, the should change the corresponding */ + /* lines in the recv_dlpi_co_stream routine */ + + if (send_size == 0) { + send_size = 1024; + } + + /* set-up the data buffer with the requested alignment and offset. */ + /* After we have calculated the proper starting address, we want to */ + /* put that back into the message variable so we go back to the */ + /* proper place. note that this means that only the first send is */ + /* guaranteed to be at the alignment specified by the -a parameter. I */ + /* think that this is a little more "real-world" than what was found */ + /* in previous versions. note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lsw_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + + send_message.maxlen = send_size; + send_message.len = send_size; + send_message.buf = send_ring->buffer_ptr; + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. */ + + netperf_request.content.request_type = DO_DLPI_CO_STREAM; + dlpi_co_stream_request->send_win_size = rsw_size; + dlpi_co_stream_request->recv_win_size = rrw_size; + dlpi_co_stream_request->receive_size = recv_size; + dlpi_co_stream_request->recv_alignment= remote_recv_align; + dlpi_co_stream_request->recv_offset = remote_recv_offset; + dlpi_co_stream_request->measure_cpu = remote_cpu_usage; + dlpi_co_stream_request->cpu_rate = remote_cpu_rate; + dlpi_co_stream_request->ppa = rem_ppa; + dlpi_co_stream_request->sap = dlpi_sap; + dlpi_co_stream_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_co_stream_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_stream_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_co_stream_request->test_length = test_time; + } + else { + dlpi_co_stream_request->test_length = test_bytes; + } +#ifdef DIRTY + dlpi_co_stream_request->dirty_count = rem_dirty_count; + dlpi_co_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + + + if (debug > 1) { + fprintf(where, + "netperf: send_dlpi_co_stream: requesting DLPI CO stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_co_stream_response->recv_win_size; + rsw_size = dlpi_co_stream_response->send_win_size; + remote_cpu_usage= dlpi_co_stream_response->measure_cpu; + remote_cpu_rate = dlpi_co_stream_response->cpu_rate; + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + exit(1); + } + + /* Connect up to the remote port on the data descriptor */ + if(dl_connect(send_descriptor, + dlpi_co_stream_response->station_addr, + dlpi_co_stream_response->station_addr_len) != 0) { + fprintf(where,"recv_dlpi_co_stream: connect failure\n"); + fflush(where); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + +#ifdef DIRTY + /* initialize the random number generator for putting dirty stuff */ + /* into the send buffer. raj */ + srand((int) getpid()); +#endif /* DIRTY */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + message_int_ptr = (int *)message_ptr; + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) + break; + perror("netperf: data send error"); + exit(1); + } + send_ring = send_ring->next; + send_message.buf = send_ring->buffer_ptr; +#ifdef WANT_INTERVALS + for (interval_count = 0; + interval_count < interval_wate; + interval_count++); +#endif /* WANT_INTERVALS */ + + if (debug > 4) { + fprintf(where,"netperf: send_clpi_co_stream: putmsg called "); + fprintf(where,"len is %d\n",send_message.len); + fflush(where); + } + + nummessages++; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. this needs a little work - there is no three-way */ + /* handshake with type two as there is with TCP, so there really */ + /* should be a message exchange here. however, we will finesse it by */ + /* saying that the tests shoudl run for a while. */ + + if (debug) { + fprintf(where,"sending test end signal \n"); + fflush(where); + } + + send_message.len = (send_size - 1); + if (send_message.len == 0) send_message.len = 2; + + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + perror("netperf: data send error"); + exit(1); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ((double) send_size * (double) nummessages) + (double) len; + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where, + "WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where, + "Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where, + "DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where, + "Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_co_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + dlpi_co_stream_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1, /* the format string */ + rrw_size, /* remote recvbuf size */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1, /* the format string */ + rrw_size, /* remote recvbuf size */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)dlpi_co_stream_result->recv_calls, + dlpi_co_stream_result->recv_calls); + } + +} + + +/* This is the server-side routine for the tcp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +int + recv_dlpi_co_stream() +{ + + int data_descriptor; + int flags = 0; + int measure_cpu; + int bytes_received; + int receive_calls; + float elapsed_time; + + struct ring_elt *recv_ring; + char *message_ptr; + char *message; + int *message_int_ptr; + struct strbuf recv_message; + int dirty_count; + int clean_count; + int i; + + struct dlpi_co_stream_request_struct *dlpi_co_stream_request; + struct dlpi_co_stream_response_struct *dlpi_co_stream_response; + struct dlpi_co_stream_results_struct *dlpi_co_stream_results; + + dlpi_co_stream_request = (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_stream_response = (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_stream_results = (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_co_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + netperf_response.content.response_type = DLPI_CO_STREAM_RESPONSE; + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_co_stream: requested alignment of %d\n", + dlpi_co_stream_request->recv_alignment); + fflush(where); + } + + + /* Grab a descriptor to listen on, and then listen on it. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_co_stream: grabbing a descriptor...\n"); + fflush(where); + } + + + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_stream_request->dlpi_device; + lastword = initword + ((dlpi_co_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_co_stream_request->dlpi_device, + dlpi_co_stream_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this descriptor so we can tell the */ + /* initiator how to reach the data descriptor. There may be a desire to */ + /* nail this descriptor to a specific address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_co_stream_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_co_stream_request->sap, + DL_CODLS, + (char *)dlpi_co_stream_response->station_addr, + &dlpi_co_stream_response->station_addr_len) != 0) { + fprintf(where,"recv_dlpi_co_stream: bind failure\n"); + fflush(where); + exit(1); + } + + /* The initiator may have wished-us to modify the socket buffer */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the socket buffer by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_co_stream_request->recv_win_size) { + } + /* 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. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_co_stream_response->recv_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* what sort of sizes did we end-up with? */ + /* this bit of code whould default to the Interface MTU */ + if (dlpi_co_stream_request->receive_size == 0) { + recv_size = 1024; + } + else { + recv_size = dlpi_co_stream_request->receive_size; + } + + /* tell the other fellow what our receive size became */ + dlpi_co_stream_response->receive_size = recv_size; + + /* just a little prep work for when we may have to behave like the */ + /* sending side... */ + message = (char *)malloc(recv_size * 2); + if (message == NULL) { + printf("malloc(%d) failed!\n", recv_size * 2); + exit(1); + } + + message_ptr = ALIGN_BUFFER(message, dlpi_co_stream_request->recv_alignment, dlpi_co_stream_request->recv_offset); + recv_message.maxlen = recv_size; + recv_message.len = 0; + recv_message.buf = message_ptr; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_co_stream: receive alignment and offset set...\n"); + fflush(where); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + dlpi_co_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_co_stream_request->measure_cpu) { + dlpi_co_stream_response->measure_cpu = 1; + dlpi_co_stream_response->cpu_rate = + calibrate_local_cpu(dlpi_co_stream_request->cpu_rate); + } + + send_response(); + + /* accept a connection on this file descriptor. at some point, */ + /* dl_accept will "do the right thing" with the last two parms, but */ + /* for now it ignores them, so we will pass zeros. */ + + if(dl_accept(data_descriptor, 0, 0) != 0) { + fprintf(where, + "recv_dlpi_co_stream: error in accept, errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where,"netserver:recv_dlpi_co_stream: connection accepted\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_co_stream_request->measure_cpu); + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + dirty_count = dlpi_co_stream_request->dirty_count; + clean_count = dlpi_co_stream_request->clean_count; + message_int_ptr = (int *)message_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + recv_message.len = recv_size; + while (recv_message.len == recv_size) { + if (getmsg(data_descriptor, + 0, + &recv_message, + &flags) != 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + bytes_received += recv_message.len; + receive_calls++; + + if (debug) { + fprintf(where, + "netserver:recv_dlpi_co_stream: getmsg accepted %d bytes\n", + recv_message.len); + fflush(where); + } + + +#ifdef DIRTY + message_int_ptr = (int *)message_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + } + + /* The loop now exits due to zero bytes received. */ + /* should perform a disconnect to signal the sender that */ + /* we have received all the data sent. */ + + if (close(data_descriptor) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(dlpi_co_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_stream: got %d bytes\n", + bytes_received); + fprintf(where, + "recv_dlpi_co_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + dlpi_co_stream_results->bytes_received = bytes_received; + dlpi_co_stream_results->elapsed_time = elapsed_time; + dlpi_co_stream_results->recv_calls = receive_calls; + + if (dlpi_co_stream_request->measure_cpu) { + dlpi_co_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_co_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); +} + +/*********************************/ + +int send_dlpi_co_rr(char remote_host[]) +{ + + char *tput_title = "\ + Local /Remote\n\ + Window Size Request Resp. Elapsed Trans.\n\ + Send Recv Size Size Time Rate \n\ + frames frames bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ + %-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ + %-6d %-6d\n"; + + char *cpu_title = "\ + Local /Remote\n\ + Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ + Send Recv Size Size Time Rate local remote local remote\n\ + frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ + %-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ + %-6d %-6d\n"; + + char *ksink_fmt = "\ + Alignment Offset\n\ + Local Remote Local Remote\n\ + Send Recv Send Recv\n\ + %5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + int dlsap_len; + char dlsap[BUFSIZ]; + + int flags = 0; + char *send_message_ptr; + char *recv_message_ptr; + char *temp_message_ptr; + struct strbuf send_message; + struct strbuf recv_message; + + int nummessages; + int send_descriptor; + int trans_remaining; + double bytes_xferd; + + int rsp_bytes_left; + + /* we assume that station adresses fit within two ints */ + unsigned int remote_address[1]; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct dlpi_co_rr_request_struct *dlpi_co_rr_request; + struct dlpi_co_rr_response_struct *dlpi_co_rr_response; + struct dlpi_co_rr_results_struct *dlpi_co_rr_result; + + dlpi_co_rr_request = + (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_rr_response = + (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_rr_result = + (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + if ( print_headers ) { + fprintf(where,"DLPI CO REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset */ + temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + send_message_ptr = (char *)(( (long) temp_message_ptr + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + send_message_ptr = send_message_ptr + local_send_offset; + send_message.maxlen = req_size+MAXALIGNMENT+MAXOFFSET; + send_message.len = req_size; + send_message.buf = send_message_ptr; + + temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + recv_message_ptr = (char *)(( (long) temp_message_ptr + + (long) local_recv_align - 1) & + ~((long) local_recv_align - 1)); + recv_message_ptr = recv_message_ptr + local_recv_offset; + recv_message.maxlen = rsp_size+MAXALIGNMENT+MAXOFFSET; + recv_message.len = 0; + recv_message.buf = send_message_ptr; + + /*set up the data socket */ + + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_co_rr: tcp stream data descriptor"); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_co_rr: send_descriptor obtained...\n"); + } + + /* bind the puppy and get the assigned dlsap */ + + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_co_rr: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size. The reason we alter the send buffer */ + /* size here rather than when the connection is made is to take care */ + /* of decreases in buffer size. Decreasing the window size after */ + /* connection establishment is a TCP no-no. Also, by setting the */ + /* buffer (window) size before the connection is established, we can */ + /* control the TCP MSS (segment size). The MSS is never more that 1/2 */ + /* the minimum receive buffer size at each half of the connection. */ + /* This is why we are altering the receive buffer size on the sending */ + /* size of a unidirectional transfer. If the user has not requested */ + /* that the socket buffers be altered, we will try to find-out what */ + /* their values are. If we cannot touch the socket buffer in any way, */ + /* we will set the values to -1 to indicate that. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: socket send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: socket recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* 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. */ + + + if (debug) { + fprintf(where,"netperf: send_dlpi_co_rr: socket sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + } + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_DLPI_CO_RR; + dlpi_co_rr_request->recv_win_size = rrw_size; + dlpi_co_rr_request->send_win_size = rsw_size; + dlpi_co_rr_request->recv_alignment = remote_recv_align; + dlpi_co_rr_request->recv_offset = remote_recv_offset; + dlpi_co_rr_request->send_alignment = remote_send_align; + dlpi_co_rr_request->send_offset = remote_send_offset; + dlpi_co_rr_request->request_size = req_size; + dlpi_co_rr_request->response_size = rsp_size; + dlpi_co_rr_request->measure_cpu = remote_cpu_usage; + dlpi_co_rr_request->cpu_rate = remote_cpu_rate; + dlpi_co_rr_request->ppa = rem_ppa; + dlpi_co_rr_request->sap = dlpi_sap; + dlpi_co_rr_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_co_rr_request->dlpi_device, + rem_dlpi_device); +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_rr_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_co_rr_request->test_length = test_time; + } + else { + dlpi_co_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_co_rr: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_co_rr_response->recv_win_size; + rsw_size = dlpi_co_rr_response->send_win_size; + remote_cpu_usage= dlpi_co_rr_response->measure_cpu; + remote_cpu_rate = dlpi_co_rr_response->cpu_rate; + + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /*Connect up to the remote port on the data descriptor */ + + if(dl_connect(send_descriptor, + dlpi_co_rr_response->station_addr, + dlpi_co_rr_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_co_rr: connect failure\n"); + fflush(where); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ + if((putmsg(send_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_dlpi_co_rr: putmsg error"); + exit(1); + } + + if (debug) { + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + /* receive the response */ + /* this needs some work with streams buffers if we are going to */ + /* support requests and responses larger than the MTU of the */ + /* network, but this can wait until later */ + rsp_bytes_left = rsp_size; + recv_message.len = rsp_size; + while(rsp_bytes_left > 0) { + if((getmsg(send_descriptor, + 0, + &recv_message, + &flags)) < 0) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_dlpi_co_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= recv_message.len; + } + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + + /* At this point we used to call shutdown onthe data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_co_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + dlpi_co_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lsw_size, /* local sendbuf size */ + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rsw_size, + rrw_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lsw_size, + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rsw_size, /* remote recvbuf size */ + rrw_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt); + } + /* The test is over. Kill the data descriptor */ + + if (close(send_descriptor) == -1) { + perror("send_dlpi_co_rr: cannot shutdown tcp stream descriptor"); + } + +} + +void + send_dlpi_cl_stream(char remote_host[]) +{ + /************************************************************************/ + /* */ + /* UDP Unidirectional Send Test */ + /* */ + /************************************************************************/ + char *tput_title = + "Window Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +frames bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.2f\n\ +%5d %-7.2f %7d %7.2f\n\n"; + + + char *cpu_title = + "Window Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +frames bytes secs # # %s/sec %% us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f\n"; + + char *cpu_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ +%5d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; + + int messages_recvd; + float elapsed_time, + local_cpu_utilization, + remote_cpu_utilization; + + float local_service_demand, remote_service_demand; + double local_thruput, remote_thruput; + double bytes_sent; + double bytes_recvd; + + + int *message_int_ptr; + char *message_ptr; + char *message; + char sctl_data[BUFSIZ]; + struct strbuf send_message; + struct strbuf sctl_message; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + + char dlsap[BUFSIZ]; + int dlsap_len; + int message_offset; + int message_max_offset; + int failed_sends; + int failed_cows; + int messages_sent; + int data_descriptor; + + +#ifdef WANT_INTERVALS + int interval_count; +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; + struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; + struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; + + dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; + + if ( print_headers ) { + printf("DLPI CL UNIDIRECTIONAL SEND TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + printf(cpu_title,format_units()); + else + printf(tput_title,format_units()); + } + + failed_sends = 0; + messages_sent = 0; + times_up = 0; + + /*set up the data descriptor */ + + data_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (data_descriptor < 0){ + perror("send_dlpi_cl_stream: data descriptor"); + exit(1); + } + + /* bind the puppy and get the assigned dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(data_descriptor, + dlpi_sap, DL_CLDLS, dlsap, &dlsap_len) != 0) { + fprintf(where,"send_dlpi_cl_stream: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size (SNDBUF size) */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_stream: descriptor send size altered from system default...\n"); + fprintf(where," send: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_stream: descriptor recv size altered from system default...\n"); + fprintf(where," recv: %d\n",lrw_size); + } + } + + + /* 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. */ + +#else /* DL_HP_SET_LOCAL_WIN_REQ */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* now, we want to see if we need to set the send_size */ + if (send_size == 0) { + send_size = 1024; + } + + + /* set-up the data buffer with the requested alignment and offset, */ + /* most of the numbers here are just a hack to pick something nice */ + /* and big in an attempt to never try to send a buffer a second time */ + /* before it leaves the node...unless the user set the width */ + /* explicitly. */ + if (send_width == 0) send_width = 32; + message = (char *)malloc(send_size * (send_width + 1) + local_send_align + local_send_offset); + if (message == NULL) { + printf("malloc(%d) failed!\n", send_size * (send_width + 1) + local_send_align + local_send_offset); + exit(1); + } + message_ptr = (char *)(( (long) message + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + message_ptr = message_ptr + local_send_offset; + message = message_ptr; + send_message.maxlen = send_size; + send_message.len = send_size; + send_message.buf = message; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + /* if the user supplied a cpu rate, this call will complete rather */ + /* quickly, otherwise, the cpu rate will be retured to us for */ + /* possible display. The Library will keep it's own copy of this data */ + /* for use elsewhere. We will only display it. (Does that make it */ + /* "opaque" to us?) */ + + if (local_cpu_usage) + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + + /* Tell the remote end to set up the data connection. The server */ + /* sends back the port number and alters the socket parameters there. */ + /* Of course this is a datagram service so no connection is actually */ + /* set up, the server just sets up the socket and binds it. */ + + netperf_request.content.request_type = DO_DLPI_CL_STREAM; + dlpi_cl_stream_request->recv_win_size = rrw_size; + dlpi_cl_stream_request->message_size = send_size; + dlpi_cl_stream_request->recv_alignment = remote_recv_align; + dlpi_cl_stream_request->recv_offset = remote_recv_offset; + dlpi_cl_stream_request->measure_cpu = remote_cpu_usage; + dlpi_cl_stream_request->cpu_rate = remote_cpu_rate; + dlpi_cl_stream_request->ppa = rem_ppa; + dlpi_cl_stream_request->sap = dlpi_sap; + dlpi_cl_stream_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_cl_stream_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_stream_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_cl_stream_request->test_length = test_time; + } + else { + dlpi_cl_stream_request->test_length = test_bytes * -1; + } + + + send_request(); + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dlpi_cl_stream: remote data connection done.\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dlpi_cl_stream: error on remote"); + exit(1); + } + + /* place some of the remote's addressing information into the send */ + /* structure so our sends can be sent to the correct place. Also get */ + /* some of the returned socket buffer information for user display. */ + + /* set-up the destination addressing control info */ + data_req->dl_primitive = DL_UNITDATA_REQ; + bcopy((char *)(dlpi_cl_stream_response->station_addr), + ((char *)data_req + sizeof(dl_unitdata_req_t)), + dlpi_cl_stream_response->station_addr_len); + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + data_req->dl_dest_addr_length = dlpi_cl_stream_response->station_addr_len; + /* there is a dl_priority structure too, but I am ignoring it for */ + /* the time being. */ + /* however... it is best to put some value in there lest some code + get grumpy about it - fix from Nicolas Thomas */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_req->dl_dest_addr_length; + + rrw_size = dlpi_cl_stream_response->recv_win_size; + rsw_size = dlpi_cl_stream_response->send_win_size; + remote_cpu_rate = dlpi_cl_stream_response->cpu_rate; + + + /* set up the timer to call us after test_time */ + start_timer(test_time); + + /* Get the start count for the idle counter and the start time */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + interval_count = interval_burst; +#endif /* WANT_INTERVALS */ + + /* Send datagrams like there was no tomorrow */ + while (!times_up) { +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + message_int_ptr = (int *)message_ptr; + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = 4; + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + if (putmsg(data_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + break; + } + if (errno == ENOBUFS) { + /* we might not ever hit this with STREAMS, it would probably */ + /* be better to do a getinfo request at the end of the test to */ + /* get all sorts of gory statistics. in the meantime, we will */ + /* keep this code in place. */ + failed_sends++; + continue; + } + perror("send_dlpi_cl_stream: data send error"); + if (debug) { + fprintf(where,"messages_sent %u\n",messages_sent); + fflush(where); + } + exit(1); + } + messages_sent++; + + /* now we want to move our pointer to the next position in the */ + /* data buffer...since there was a successful send */ + + +#ifdef WANT_INTERVALS + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call the sleep routine for some milliseconds, if our */ + /* timer popped while we were in there, we want to */ + /* break out of the loop. */ + if (msec_sleep(interval_wate)) { + break; + } + interval_count = interval_burst; + } + +#endif /* WANT_INTERVALS */ + + } + + /* This is a timed test, so the remote will be returning to us after */ + /* a time. We should not need to send any "strange" messages to tell */ + /* the remote that the test is completed, unless we decide to add a */ + /* number of messages to the test. */ + + /* the test is over, so get stats and stuff */ + cpu_stop(local_cpu_usage, + &elapsed_time); + + /* Get the statistics from the remote end */ + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dlpi_cl_stream: remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dlpi_cl_stream: error on remote"); + exit(1); + } + + bytes_sent = send_size * messages_sent; + local_thruput = calc_thruput(bytes_sent); + + messages_recvd = dlpi_cl_stream_results->messages_recvd; + bytes_recvd = send_size * messages_recvd; + + /* we asume that the remote ran for as long as we did */ + + remote_thruput = calc_thruput(bytes_recvd); + + /* print the results for this descriptor and message size */ + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) We pass zeros for the local */ + /* cpu utilization and elapsed time to tell the routine to use */ + /* the libraries own values for those. */ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + /* The local calculations could use variables being kept by */ + /* the local netlib routines. The remote calcuations need to */ + /* have a few things passed to them. */ + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"REMOTE CPU usage numbers based on process information only!\n"); + fflush(where); + } + + remote_cpu_utilization = dlpi_cl_stream_results->cpu_util; + remote_service_demand = calc_service_demand(bytes_recvd, + 0.0, + remote_cpu_utilization, + dlpi_cl_stream_results->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1, /* the format string */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + messages_sent, + failed_sends, + local_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rrw_size, + elapsed_time, + messages_recvd, + remote_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_thruput); + break; + case 1: + fprintf(where, + tput_fmt_1, /* the format string */ + lsw_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + messages_sent, + failed_sends, + local_thruput, + rrw_size, /* remote recvbuf size */ + elapsed_time, + messages_recvd, + remote_thruput + ); + break; + } + } +} + +int + recv_dlpi_cl_stream() +{ + + char *message; + int data_descriptor; + int len; + char *message_ptr; + char rctl_data[BUFSIZ]; + struct strbuf recv_message; + struct strbuf rctl_message; + int flags = 0; + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int bytes_received = 0; + float elapsed_time; + + int message_size; + int messages_recvd = 0; + int measure_cpu; + + struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; + struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; + struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; + + dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_cl_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen descriptor with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_STREAM_RESPONSE; + + if (debug > 2) { + fprintf(where,"recv_dlpi_cl_stream: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: requested alignment of %d\n", + dlpi_cl_stream_request->recv_alignment); + fflush(where); + } + + message_ptr = ALIGN_BUFFER(message, dlpi_cl_stream_request->recv_alignment, dlpi_cl_stream_request->recv_offset); + + if (dlpi_cl_stream_request->message_size > 0) { + recv_message.maxlen = dlpi_cl_stream_request->message_size; + } + else { + recv_message.maxlen = 4096; + } + recv_message.len = 0; + recv_message.buf = message_ptr; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + if (debug > 1) { + fprintf(where, + "recv_dlpi_cl_stream: receive alignment and offset set...\n"); + fflush(where); + } + + if (debug > 1) { + fprintf(where,"recv_dlpi_cl_stream: grabbing a descriptor...\n"); + fflush(where); + } + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_stream_request->dlpi_device; + lastword = initword + ((dlpi_cl_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_cl_stream_request->dlpi_device, + dlpi_cl_stream_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* The initiator may have wished-us to modify the window */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the sizes by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_cl_stream_request->recv_win_size) { + dlpi_cl_stream_response->recv_win_size = -1; + } + +#else /* the system won't let us play with the buffers */ + + dlpi_cl_stream_response->recv_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + dlpi_cl_stream_response->test_length = dlpi_cl_stream_request->test_length; + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_cl_stream_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_cl_stream_request->sap, + DL_CLDLS, + (char *)dlpi_cl_stream_response->station_addr, + &dlpi_cl_stream_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_cl_stream: bind failure\n"); + fflush(where); + exit(1); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + dlpi_cl_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_cl_stream_request->measure_cpu) { + /* We will pass the rate into the calibration routine. If the */ + /* user did not specify one, it will be 0.0, and we will do a */ + /* "real" calibration. Otherwise, all it will really do is */ + /* store it away... */ + dlpi_cl_stream_response->measure_cpu = 1; + dlpi_cl_stream_response->cpu_rate = calibrate_local_cpu(dlpi_cl_stream_request->cpu_rate); + } + + message_size = dlpi_cl_stream_request->message_size; + test_time = dlpi_cl_stream_request->test_length; + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_cl_stream_request->measure_cpu); + + /* The loop will exit when the timer pops, or if we happen to recv a */ + /* message of less than send_size bytes... */ + + times_up = 0; + start_timer(test_time + PAD_TIME); + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: about to enter inner sanctum.\n"); + fflush(where); + } + + while (!times_up) { + if((getmsg(data_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "dlpi_recv_cl_stream: getmsg failure: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fflush(where); + netperf_response.content.serv_errno = 996; + send_response(); + exit(1); + } + messages_recvd++; + } + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: got %d messages.\n",messages_recvd); + fflush(where); + } + + + /* The loop now exits due timer or < send_size bytes received. */ + + cpu_stop(dlpi_cl_stream_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended on a timer, subtract the PAD_TIME */ + elapsed_time -= (float)PAD_TIME; + } + else { + stop_timer(); + } + + if (debug) { + fprintf(where,"recv_dlpi_cl_stream: test ended in %f seconds.\n",elapsed_time); + fflush(where); + } + + + /* We will count the "off" message */ + bytes_received = (messages_recvd * message_size) + len; + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_stream: got %d bytes\n", + bytes_received); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_STREAM_RESULTS; + dlpi_cl_stream_results->bytes_received = bytes_received; + dlpi_cl_stream_results->messages_recvd = messages_recvd; + dlpi_cl_stream_results->elapsed_time = elapsed_time; + if (dlpi_cl_stream_request->measure_cpu) { + dlpi_cl_stream_results->cpu_util = calc_cpu_util(elapsed_time); + } + else { + dlpi_cl_stream_results->cpu_util = -1.0; + } + + if (debug > 1) { + fprintf(where, + "recv_dlpi_cl_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +int send_dlpi_cl_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Window Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +frames frames bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + float elapsed_time; + + int dlsap_len; + int flags = 0; + char *send_message_ptr; + char *recv_message_ptr; + char *temp_message_ptr; + char sctl_data[BUFSIZ]; + char rctl_data[BUFSIZ]; + char dlsap[BUFSIZ]; + struct strbuf send_message; + struct strbuf recv_message; + struct strbuf sctl_message; + struct strbuf rctl_message; + + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int nummessages; + int send_descriptor; + int trans_remaining; + int bytes_xferd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + +#ifdef WANT_INTERVALS + /* timing stuff */ +#define MAX_KEPT_TIMES 1024 + int time_index = 0; + int unused_buckets; + int kept_times[MAX_KEPT_TIMES]; + int sleep_usecs; + unsigned int total_times=0; + struct timezone dummy_zone; + struct timeval send_time; + struct timeval recv_time; + struct timeval sleep_timeval; +#endif /* WANT_INTERVALS */ + + struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; + struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; + struct dlpi_cl_rr_results_struct *dlpi_cl_rr_result; + + dlpi_cl_rr_request = + (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_rr_response = + (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_rr_result = + (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; + + /* we want to zero out the times, so we can detect unused entries. */ +#ifdef WANT_INTERVALS + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + kept_times[time_index] = 0; + time_index += 1; + } + time_index = 0; +#endif /* WANT_INTERVALS */ + + if (print_headers) { + fprintf(where,"DLPI CL REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + + /* set-up the data buffer with the requested alignment and offset */ + temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + send_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + send_message_ptr = send_message_ptr + local_send_offset; + send_message.maxlen = req_size; + send_message.len = req_size; + send_message.buf = send_message_ptr; + + temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); + exit(1); + } + recv_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_recv_align - 1) & + ~((long) local_recv_align - 1)); + recv_message_ptr = recv_message_ptr + local_recv_offset; + recv_message.maxlen = rsp_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + /* lets get ourselves a file descriptor */ + + send_descriptor = dl_open(loc_dlpi_device,loc_ppa); + if (send_descriptor < 0){ + perror("netperf: send_dlpi_cl_rr: dlpi cl rr send descriptor"); + exit(1); + } + + if (debug) { + fprintf(where,"send_dlpi_cl_rr: send_descriptor obtained...\n"); + } + + /* bind the sap to the descriptor and get the dlsap */ + dlsap_len = BUFSIZ; + if (dl_bind(send_descriptor, + dlpi_sap, + DL_CLDLS, + dlsap, + &dlsap_len) != 0) { + fprintf(where,"send_dlpi_cl_rr: bind failure\n"); + fflush(where); + exit(1); + } + + /* Modify the local socket size. If the user has not requested that */ + /* the socket buffers be altered, we will try to find-out what their */ + /* values are. If we cannot touch the socket buffer in any way, we */ + /* will set the values to -1 to indicate that. The receive socket */ + /* must have enough space to hold addressing information so += a */ + /* sizeof struct sockaddr_in to it. */ + + /* this is actually nothing code, and should be replaced with the */ + /* alalagous calls in the STREAM test where the window size is set */ + /* with the HP DLPI Extension. raj 8/94 */ +#ifdef SO_SNDBUF + if (lsw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: local window size altered from system default...\n"); + fprintf(where," window: %d\n",lsw_size); + } + } + if (lrw_size > 0) { + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: remote window size altered from system default...\n"); + fprintf(where," remote: %d\n",lrw_size); + } + } + + + /* 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. */ + + if (debug) { + fprintf(where,"netperf: send_dlpi_cl_rr: socket sizes determined...\n"); + fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); + } + +#else /* SO_SNDBUF */ + + lsw_size = -1; + lrw_size = -1; + +#endif /* SO_SNDBUF */ + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. If */ + /* there is no idle counter in the kernel idle loop, the */ + /* local_cpu_rate will be set to -1. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_DLPI_CL_RR; + dlpi_cl_rr_request->recv_win_size = rrw_size; + dlpi_cl_rr_request->send_win_size = rsw_size; + dlpi_cl_rr_request->recv_alignment = remote_recv_align; + dlpi_cl_rr_request->recv_offset = remote_recv_offset; + dlpi_cl_rr_request->send_alignment = remote_send_align; + dlpi_cl_rr_request->send_offset = remote_send_offset; + dlpi_cl_rr_request->request_size = req_size; + dlpi_cl_rr_request->response_size = rsp_size; + dlpi_cl_rr_request->measure_cpu = remote_cpu_usage; + dlpi_cl_rr_request->cpu_rate = remote_cpu_rate; + dlpi_cl_rr_request->ppa = rem_ppa; + dlpi_cl_rr_request->sap = dlpi_sap; + dlpi_cl_rr_request->dev_name_len = strlen(rem_dlpi_device); + strcpy(dlpi_cl_rr_request->dlpi_device, + rem_dlpi_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_rr_request->dlpi_device; + lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (test_time) { + dlpi_cl_rr_request->test_length = test_time; + } + else { + dlpi_cl_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_dlpi_cl_rr: requesting DLPI CL request/response test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rrw_size = dlpi_cl_rr_response->recv_win_size; + rsw_size = dlpi_cl_rr_response->send_win_size; + remote_cpu_usage= dlpi_cl_rr_response->measure_cpu; + remote_cpu_rate = dlpi_cl_rr_response->cpu_rate; + + /* set-up the destination addressing control info */ + data_req->dl_primitive = DL_UNITDATA_REQ; + bcopy((char *)(dlpi_cl_rr_response->station_addr), + ((char *)data_req + sizeof(dl_unitdata_req_t)), + dlpi_cl_rr_response->station_addr_len); + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + data_req->dl_dest_addr_length = dlpi_cl_rr_response->station_addr_len; + /* there is a dl_priority structure too, but I am ignoring it for */ + /* the time being. */ + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_req->dl_dest_addr_length; + /* famous last words - some DLPI providers get unhappy if the + priority stuff is not initialized. fix from Nicolas Thomas. */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ +#ifdef WANT_INTERVALS + gettimeofday(&send_time,&dummy_zone); +#endif /* WANT_INTERVALS */ + if(putmsg(send_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + /* there is more we could do here, but it can wait */ + perror("send_dlpi_cl_rr: data send error"); + exit(1); + } + + /* receive the response. at some point, we will need to handle */ + /* sending responses which are greater than the datalink MTU. we */ + /* may also want to add some DLPI error checking, but for now we */ + /* will ignore that and just let errors stop the test with little */ + /* indication of what might actually be wrong. */ + + if((getmsg(send_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "send_dlpi_cl_rr: recv error: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fflush(where); + exit(1); + } +#ifdef WANT_INTERVALS + gettimeofday(&recv_time,&dummy_zone); + + /* now we do some arithmatic on the two timevals */ + if (recv_time.tv_usec < send_time.tv_usec) { + /* we wrapped around a second */ + recv_time.tv_usec += 1000000; + recv_time.tv_sec -= 1; + } + + /* and store it away */ + kept_times[time_index] = (recv_time.tv_sec - send_time.tv_sec) * 1000000; + kept_times[time_index] += (recv_time.tv_usec - send_time.tv_usec); + + /* at this point, we may wish to sleep for some period of */ + /* time, so we see how long that last transaction just took, */ + /* and sleep for the difference of that and the interval. We */ + /* will not sleep if the time would be less than a */ + /* millisecond. */ + if (interval_usecs > 0) { + sleep_usecs = interval_usecs - kept_times[time_index]; + if (sleep_usecs > 1000) { + /* we sleep */ + sleep_timeval.tv_sec = sleep_usecs / 1000000; + sleep_timeval.tv_usec = sleep_usecs % 1000000; + select(0, + 0, + 0, + 0, + &sleep_timeval); + } + } + + /* now up the time index */ + time_index = (time_index +1)%MAX_KEPT_TIMES; +#endif /* WANT_INTERVALS */ + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where,"Transaction %d completed\n",nummessages); + fflush(where); + } + + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a UDP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dlpi_cl_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + dlpi_cl_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lsw_size, /* local sendbuf size */ + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rsw_size, + rrw_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lsw_size, + lrw_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rsw_size, /* remote recvbuf size */ + rrw_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* UDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_INTERVALS + kept_times[MAX_KEPT_TIMES] = 0; + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + if (kept_times[time_index] > 0) { + total_times += kept_times[time_index]; + } + else + unused_buckets++; + time_index += 1; + } + total_times /= (MAX_KEPT_TIMES-unused_buckets); + fprintf(where, + "Average response time %d usecs\n", + total_times); +#endif + } +} + +int + recv_dlpi_cl_rr() +{ + + char *message; + int data_descriptor; + int flags = 0; + int measure_cpu; + + char *recv_message_ptr; + char *send_message_ptr; + char sctl_data[BUFSIZ]; + char rctl_data[BUFSIZ]; + char dlsap[BUFSIZ]; + struct strbuf send_message; + struct strbuf recv_message; + struct strbuf sctl_message; + struct strbuf rctl_message; + + /* these are to make reading some of the DLPI control messages easier */ + dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; + dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; + dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; + + int trans_received; + int trans_remaining; + float elapsed_time; + + struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; + struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; + struct dlpi_cl_rr_results_struct *dlpi_cl_rr_results; + + dlpi_cl_rr_request = + (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_cl_rr_response = + (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_cl_rr_results = + (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_cl_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen descriptor with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the descriptor sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CL_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: requested recv alignment of %d offset %d\n", + dlpi_cl_rr_request->recv_alignment, + dlpi_cl_rr_request->recv_offset); + fprintf(where, + "recv_dlpi_cl_rr: requested send alignment of %d offset %d\n", + dlpi_cl_rr_request->send_alignment, + dlpi_cl_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->recv_alignment, dlpi_cl_rr_request->recv_offset); + recv_message.maxlen = dlpi_cl_rr_request->request_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + send_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->send_alignment, dlpi_cl_rr_request->send_offset); + send_message.maxlen = dlpi_cl_rr_request->response_size; + send_message.len = dlpi_cl_rr_request->response_size; + send_message.buf = send_message_ptr; + + sctl_message.maxlen = BUFSIZ; + sctl_message.len = 0; + sctl_message.buf = sctl_data; + + rctl_message.maxlen = BUFSIZ; + rctl_message.len = 0; + rctl_message.buf = rctl_data; + + if (debug) { + fprintf(where,"recv_dlpi_cl_rr: receive alignment and offset set...\n"); + fprintf(where,"recv_dlpi_cl_rr: grabbing a socket...\n"); + fflush(where); + } + + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_cl_rr_request->dlpi_device; + lastword = initword + ((dlpi_cl_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + data_descriptor = dl_open(dlpi_cl_rr_request->dlpi_device, + dlpi_cl_rr_request->ppa); + if (data_descriptor < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + + /* The initiator may have wished-us to modify the window */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the sizes by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_cl_rr_request->recv_win_size) { + } + + if (dlpi_cl_rr_request->send_win_size) { + } + + /* Now, we will find-out what the sizes actually became, and report */ + /* them back to the user. If the calls fail, we will just report a -1 */ + /* back to the initiator for the buffer size. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_cl_rr_response->recv_win_size = -1; + dlpi_cl_rr_response->send_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* bind the sap and retrieve the dlsap assigned by the system */ + dlpi_cl_rr_response->station_addr_len = 14; /* arbitrary */ + if (dl_bind(data_descriptor, + dlpi_cl_rr_request->sap, + DL_CLDLS, + (char *)dlpi_cl_rr_response->station_addr, + &dlpi_cl_rr_response->station_addr_len) != 0) { + fprintf(where,"send_dlpi_cl_rr: bind failure\n"); + fflush(where); + exit(1); + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + dlpi_cl_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_cl_rr_request->measure_cpu) { + dlpi_cl_rr_response->measure_cpu = 1; + dlpi_cl_rr_response->cpu_rate = calibrate_local_cpu(dlpi_cl_rr_request->cpu_rate); + } + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start receiving. */ + + cpu_start(dlpi_cl_rr_request->measure_cpu); + + if (dlpi_cl_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(dlpi_cl_rr_request->test_length + PAD_TIME); + } +else { + times_up = 1; + trans_remaining = dlpi_cl_rr_request->test_length * -1; +} + + while ((!times_up) || (trans_remaining > 0)) { + + /* receive the request from the other side. at some point we need */ + /* to handle "logical" requests and responses which are larger */ + /* than the data link MTU */ + + if((getmsg(data_descriptor, + &rctl_message, + &recv_message, + &flags) != 0) || + (data_ind->dl_primitive != DL_UNITDATA_IND)) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "dlpi_recv_cl_rr: getmsg failure: errno %d primitive 0x%x\n", + errno, + data_ind->dl_primitive); + fprintf(where, + " recevied %u transactions\n", + trans_received); + fflush(where); + netperf_response.content.serv_errno = 995; + send_response(); + exit(1); + } + + /* Now, send the response to the remote. first copy the dlsap */ + /* information from the receive to the sending control message */ + + data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); + bcopy((char *)data_ind + data_ind->dl_src_addr_offset, + (char *)data_req + data_req->dl_dest_addr_offset, + data_ind->dl_src_addr_length); + data_req->dl_dest_addr_length = data_ind->dl_src_addr_length; + data_req->dl_primitive = DL_UNITDATA_REQ; + /* be sure to initialize the priority fields. fix from Nicholas + Thomas */ + data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; + data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; + + sctl_message.len = sizeof(dl_unitdata_req_t) + + data_ind->dl_src_addr_length; + if(putmsg(data_descriptor, + &sctl_message, + &send_message, + 0) != 0) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + /* there is more we could do here, but it can wait */ + fprintf(where, + "dlpi_recv_cl_rr: putmsg failure: errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = 993; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: Transaction %d complete.\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(dlpi_cl_rr_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + dlpi_cl_rr_results->bytes_received = (trans_received * + (dlpi_cl_rr_request->request_size + + dlpi_cl_rr_request->response_size)); + dlpi_cl_rr_results->trans_received = trans_received; + dlpi_cl_rr_results->elapsed_time = elapsed_time; + if (dlpi_cl_rr_request->measure_cpu) { + dlpi_cl_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_dlpi_cl_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +int + recv_dlpi_co_rr() +{ + + char *message; + SOCKET s_listen,data_descriptor; + + int measure_cpu; + + int flags = 0; + char *recv_message_ptr; + char *send_message_ptr; + struct strbuf send_message; + struct strbuf recv_message; + + int trans_received; + int trans_remaining; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct dlpi_co_rr_request_struct *dlpi_co_rr_request; + struct dlpi_co_rr_response_struct *dlpi_co_rr_response; + struct dlpi_co_rr_results_struct *dlpi_co_rr_results; + + dlpi_co_rr_request = (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; + dlpi_co_rr_response = (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; + dlpi_co_rr_results = (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dlpi_co_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DLPI_CO_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: requested recv alignment of %d offset %d\n", + dlpi_co_rr_request->recv_alignment, + dlpi_co_rr_request->recv_offset); + fprintf(where, + "recv_dlpi_co_rr: requested send alignment of %d offset %d\n", + dlpi_co_rr_request->send_alignment, + dlpi_co_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->recv_alignment, dlpi_co_rr_request->recv_offset); + recv_message.maxlen = dlpi_co_rr_request->request_size; + recv_message.len = 0; + recv_message.buf = recv_message_ptr; + + send_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->send_alignment, dlpi_co_rr_request->send_offset); + send_message.maxlen = dlpi_co_rr_request->response_size; + send_message.len = dlpi_co_rr_request->response_size; + send_message.buf = send_message_ptr; + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: receive alignment and offset set...\n"); + fprintf(where,"recv_dlpi_co_rr: send_message.buf %x .len %d .maxlen %d\n", + send_message.buf,send_message.len,send_message.maxlen); + fprintf(where,"recv_dlpi_co_rr: recv_message.buf %x .len %d .maxlen %d\n", + recv_message.buf,recv_message.len,recv_message.maxlen); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_dlpi_co_rr: grabbing a socket...\n"); + fflush(where); + } + + /* lets grab a file descriptor for a particular link */ + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) dlpi_co_rr_request->dlpi_device; + lastword = initword + ((dlpi_co_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } +#endif /* __alpha */ + + if ((data_descriptor = dl_open(dlpi_co_rr_request->dlpi_device, + dlpi_co_rr_request->ppa)) < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* bind the file descriptor to a sap and get the resultant dlsap */ + dlpi_co_rr_response->station_addr_len = 14; /*arbitrary needs fixing */ + if (dl_bind(data_descriptor, + dlpi_co_rr_request->sap, + DL_CODLS, + (char *)dlpi_co_rr_response->station_addr, + &dlpi_co_rr_response->station_addr_len) != 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* The initiator may have wished-us to modify the socket buffer */ + /* sizes. We should give it a shot. If he didn't ask us to change the */ + /* sizes, we should let him know what sizes were in use at this end. */ + /* If none of this code is compiled-in, then we will tell the */ + /* initiator that we were unable to play with the socket buffer by */ + /* setting the size in the response to -1. */ + +#ifdef DL_HP_SET_LOCAL_WIN_REQ + + if (dlpi_co_rr_request->recv_win_size) { + /* SMOP */ + } + + if (dlpi_co_rr_request->send_win_size) { + /* SMOP */ + } + + /* Now, we will find-out what the sizes actually became, and report */ + /* them back to the user. If the calls fail, we will just report a -1 */ + /* back to the initiator for the buffer size. */ + +#else /* the system won't let us play with the buffers */ + + dlpi_co_rr_response->recv_win_size = -1; + dlpi_co_rr_response->send_win_size = -1; + +#endif /* DL_HP_SET_LOCAL_WIN_REQ */ + + /* we may have been requested to enable the copy avoidance features. */ + /* can we actually do this with DLPI, the world wonders */ + + if (dlpi_co_rr_request->so_rcvavoid) { +#ifdef SO_RCV_COPYAVOID + dlpi_co_rr_response->so_rcvavoid = 0; +#else + /* it wasn't compiled in... */ + dlpi_co_rr_response->so_rcvavoid = 0; +#endif + } + + if (dlpi_co_rr_request->so_sndavoid) { +#ifdef SO_SND_COPYAVOID + dlpi_co_rr_response->so_sndavoid = 0; +#else + /* it wasn't compiled in... */ + dlpi_co_rr_response->so_sndavoid = 0; +#endif + } + + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + dlpi_co_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (dlpi_co_rr_request->measure_cpu) { + dlpi_co_rr_response->measure_cpu = 1; + dlpi_co_rr_response->cpu_rate = calibrate_local_cpu(dlpi_co_rr_request->cpu_rate); + } + + send_response(); + + /* accept a connection on this file descriptor. at some point, */ + /* dl_accept will "do the right thing" with the last two parms, but */ + /* for now it ignores them, so we will pass zeros. */ + + if(dl_accept(data_descriptor, 0, 0) != 0) { + fprintf(where, + "recv_dlpi_co_rr: error in accept, errno %d\n", + errno); + fflush(where); + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dlpi_co_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (dlpi_co_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(dlpi_co_rr_request->test_length + PAD_TIME); + } +else { + times_up = 1; + trans_remaining = dlpi_co_rr_request->test_length * -1; +} + + while ((!times_up) || (trans_remaining > 0)) { + request_bytes_remaining = dlpi_co_rr_request->request_size; + + /* receive the request from the other side. there needs to be some */ + /* more login in place for handling messages larger than link mtu, */ + /* but that can wait for later */ + while(request_bytes_remaining > 0) { + if((getmsg(data_descriptor, + 0, + &recv_message, + &flags)) < 0) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + + if (debug) { + fprintf(where,"failed getmsg call errno %d\n",errno); + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= recv_message.len; + } + } + + if (timed_out) { + /* we hit the end of the test based on time - lets bail out of */ + /* here now... */ + break; + } + + if (debug) { + fprintf(where,"recv_message.len %d\n",recv_message.len); + fprintf(where,"send_message.len %d\n",send_message.len); + fflush(where); + } + + /* Now, send the response to the remote */ + if((putmsg(data_descriptor, + 0, + &send_message, + 0)) != 0) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = 994; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(dlpi_co_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + dlpi_co_rr_results->bytes_received = (trans_received * + (dlpi_co_rr_request->request_size + + dlpi_co_rr_request->response_size)); + dlpi_co_rr_results->trans_received = trans_received; + dlpi_co_rr_results->elapsed_time = elapsed_time; + if (dlpi_co_rr_request->measure_cpu) { + dlpi_co_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_dlpi_co_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +/* this routine will display the usage string for the DLPI tests */ +void + print_dlpi_usage() + +{ + fwrite(dlpi_usage, sizeof(char), strlen(dlpi_usage), stdout); +} + + +/* this routine will scan the command line for DLPI test arguments */ +void + scan_dlpi_args(int argc, char *argv[]) +{ + extern int optind, opterrs; /* index of first unused arg */ + extern char *optarg; /* pointer to option string */ + + int c; + + char arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (no_control) { + fprintf(where, + "The DLPI tests do not know how to run with no control connection\n"); + exit(-1); + } + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form first, (see the routine break_args.. */ + +#define DLPI_ARGS "D:hM:m:p:r:s:W:w:" + + while ((c= getopt(argc, argv, DLPI_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_dlpi_usage(); + exit(1); + case 'D': + /* set the dlpi device file name(s) */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strcpy(loc_dlpi_device,arg1); + if (arg2[0]) + strcpy(rem_dlpi_device,arg2); + break; + case 'm': + /* set the send size */ + send_size = atoi(optarg); + break; + case 'M': + /* set the recv size */ + recv_size = atoi(optarg); + break; + case 'p': + /* set the local/remote ppa */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + loc_ppa = atoi(arg1); + if (arg2[0]) + rem_ppa = atoi(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = atoi(arg1); + if (arg2[0]) + rsp_size = atoi(arg2); + break; + case 's': + /* set the 802.2 sap for the test */ + dlpi_sap = atoi(optarg); + break; + case 'w': + /* set local window sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lsw_size = atoi(arg1); + if (arg2[0]) + lrw_size = atoi(arg2); + break; + case 'W': + /* set remote window sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rsw_size = atoi(arg1); + if (arg2[0]) + rrw_size = atoi(arg2); + break; + }; + } +} + + +#endif /* WANT_DLPI */ diff --git a/nettest_dlpi.h b/nettest_dlpi.h new file mode 100644 index 0000000..8169237 --- /dev/null +++ b/nettest_dlpi.h @@ -0,0 +1,215 @@ +/* + Copyright (C) 1993, Hewlett-Packard Company +*/ + + /* This file contains the test-specific definitions for netperf's */ + /* DLPI tests */ + + +struct dlpi_co_stream_request_struct { + int recv_win_size; + int send_win_size; + int receive_size; /* how many bytes do we want to */ + /* receive at one time? */ + int recv_alignment; /* what is the alignment of the */ + /* receive buffer? */ + int recv_offset; /* and at what offset from that */ + /* alignment? */ + int measure_cpu; /* does the client want server cpu */ + /* utilization measured? */ + float cpu_rate; /* do we know how fast the cpu is */ + /* already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid */ + /* copies on receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int sap; /* */ + int ppa; /* which device do we wish to use? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char dlpi_device[32]; /* the path to the dlpi device */ +}; + +struct dlpi_co_stream_response_struct { + int recv_win_size; /* how big does the client want it */ + int send_win_size; + int receive_size; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int station_addr_len; + int station_addr[1];/* what is the station address for the */ + /* specified ppa? */ +}; + +struct dlpi_co_stream_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was CPU util measured? */ + int num_cpus; /* how many CPUs were there? */ +}; + +struct dlpi_co_rr_request_struct { + int recv_win_size; /* how big does the client want it */ + int send_win_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int ppa; /* which device do we wish to use? */ + int sap; /* which sap should be used? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char dlpi_device[32]; /* the path to the dlpi device */ +}; + +struct dlpi_co_rr_response_struct { + int recv_win_size; /* how big does the client want it */ + int send_win_size; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int station_addr_len; /* the length of the station address */ + int station_addr[1]; /* the remote's station address */ +}; + +struct dlpi_co_rr_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was CPU util measured? */ + int num_cpus; /* how many CPUs were there? */ +}; + +struct dlpi_cl_stream_request_struct { + int recv_win_size; + int message_size; + int recv_alignment; + int recv_offset; + int checksum_off; + int measure_cpu; + float cpu_rate; + int test_length; + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int ppa; /* which device do we wish to use? */ + int sap; + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char dlpi_device[32]; /* the path to the dlpi device */ +}; + +struct dlpi_cl_stream_response_struct { + int recv_win_size; + int send_win_size; + int measure_cpu; + int test_length; + int data_port_number; + float cpu_rate; + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int station_addr_len; /* the length of the station address */ + int station_addr[1]; /* the remote's station address */ +}; + +struct dlpi_cl_stream_results_struct { + int messages_recvd; + int bytes_received; + float elapsed_time; + float cpu_util; + int num_cpus; +}; + + +struct dlpi_cl_rr_request_struct { + int recv_win_size; /* how big does the client want it */ + int send_win_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int ppa; /* which device do we wish to use? */ + int sap; /* which sap? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char dlpi_device[32]; /* the path to the dlpi device */ +}; + +struct dlpi_cl_rr_response_struct { + int recv_win_size; /* how big does the client want it */ + int send_win_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int station_addr_len; /* the length of the station address */ + int station_addr[1]; /* the remote's station address */ +}; + +struct dlpi_cl_rr_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was CPU util measured? */ + int num_cpus; /* how many CPUs were there? */ +}; + +extern void send_dlpi_co_stream(); + +extern int recv_dlpi_co_stream(); + +extern int send_dlpi_co_rr(char remote_host[]); + +extern void send_dlpi_cl_stream(char remote_host[]); + +extern int recv_dlpi_cl_stream(); + +extern int send_dlpi_cl_rr(char remote_host[]); + +extern int recv_dlpi_cl_rr(); + +extern int recv_dlpi_co_rr(); + +extern void scan_dlpi_args(int argc, char *argv[]); diff --git a/nettest_sctp.c b/nettest_sctp.c new file mode 100644 index 0000000..7cfcd9f --- /dev/null +++ b/nettest_sctp.c @@ -0,0 +1,4869 @@ +#ifndef lint +char nettest_sctp[]="\ +@(#)nettest_sctp.c (c) Copyright 2005-2007 Hewlett-Packard Co. Version 2.4.3"; +#else +#define DIRTY +#define WANT_HISTOGRAM +#define WANT_INTERVALS +#endif /* lint */ + +/****************************************************************/ +/* */ +/* nettest_sctp.c */ +/* */ +/* */ +/* scan_sctp_args() get the sctp command line args */ +/* */ +/* the actual test routines... */ +/* */ +/* send_sctp_stream() perform a sctp stream test */ +/* recv_sctp_stream() */ +/* send_sctp_rr() perform a sctp request/response */ +/* recv_sctp_rr() */ +/* send_sctp_stream_udp() perform a sctp request/response */ +/* recv_sctp_stream_upd() using UDP style API */ +/* send_sctp_rr_udp() perform a sctp request/response */ +/* recv_sctp_rr_upd() using UDP style API */ +/* */ +/* relies on create_data_socket in nettest_bsd.c */ +/****************************************************************/ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined(WANT_SCTP) + +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#ifdef NOSTDLIBH +#include <malloc.h> +#else /* NOSTDLIBH */ +#include <stdlib.h> +#endif /* NOSTDLIBH */ + +#if !defined(__VMS) +#include <sys/ipc.h> +#endif /* !defined(__VMS) */ +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netinet/sctp.h> +#include <arpa/inet.h> +#include <netdb.h> + +/* would seem that not all sctp.h files define a MSG_EOF, but that + MSG_EOF can be the same as MSG_FIN so lets work with that + assumption. initial find by Jon Pedersen. raj 2006-02-01 */ +#ifndef MSG_EOF +#ifdef MSG_FIN +#define MSG_EOF MSG_FIN +#else +#error Must have either MSG_EOF or MSG_FIN defined +#endif +#endif + +#include "netlib.h" +#include "netsh.h" +/* get some of the functions from nettest_bsd.c */ +#include "nettest_bsd.h" +#include "nettest_sctp.h" + +#ifdef WANT_HISTOGRAM +#ifdef __sgi +#include <sys/time.h> +#endif /* __sgi */ +#include "hist.h" +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_FIRST_BURST +extern int first_burst_size; +#endif /* WANT_FIRST_BURST */ + + + +/* these variables are specific to SCTP tests. declare */ +/* them static to make them global only to this file. */ + +static int + msg_count = 0, /* number of messages to transmit on association */ + non_block = 0, /* default to blocking sockets */ + num_associations = 1; /* number of associations on the endpoint */ + +static int confidence_iteration; +static char local_cpu_method; +static char remote_cpu_method; + +#ifdef WANT_HISTOGRAM +static struct timeval time_one; +static struct timeval time_two; +static HIST time_hist; +#endif /* WANT_HISTOGRAM */ + + +char sctp_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +SCTP Sockets Test Options:\n\ + -b number Send number requests at the start of _RR tests\n\ + -D [L][,R] Set SCTP_NODELAY locally and/or remotely\n\ + -h Display this text\n\ + -H name,fam Use name (or IP) and family as target of data connection\n\ + -L name,fam Use name (or IP) and family as source of data connextion\n\ + -m bytes Set the size of each sent message\n\ + -M bytes Set the size of each received messages\n\ + -P local[,remote] Set the local/remote port for the data socket\n\ + -r req,[rsp] Set request/response sizes (_RR tests)\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ + -V Enable copy avoidance if supported\n\ + -N number Specifies the number of messages to send (_STREAM tests)\n\ + -B run the test in non-blocking mode\n\ + -T number Number of associations to create (_MANY tests)\n\ + -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\ + -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + + /* This routine is intended to retrieve interesting aspects of tcp */ + /* for the data connection. at first, it attempts to retrieve the */ + /* maximum segment size. later, it might be modified to retrieve */ + /* other information, but it must be information that can be */ + /* retrieved quickly as it is called during the timing of the test. */ + /* for that reason, a second routine may be created that can be */ + /* called outside of the timing loop */ +static +void +get_sctp_info(socket, mss) + int socket; + int *mss; +{ + + int sock_opt_len; + + if (sctp_opt_info(socket, + 0, + SCTP_MAXSEG, + mss, + &sock_opt_len) < 0) { + lss_size = -1; + } +} + + +static +void +sctp_enable_events(socket, ev_mask) + int socket; + int ev_mask; +{ + struct sctp_event_subscribe ev; + + bzero(&ev, sizeof(ev)); + + if (ev_mask & SCTP_SNDRCV_INFO_EV) + ev.sctp_data_io_event = 1; + + if (ev_mask & SCTP_ASSOC_CHANGE_EV) + ev.sctp_association_event = 1; + + if (ev_mask & SCTP_PEERADDR_CHANGE_EV) + ev.sctp_address_event = 1; + + if (ev_mask & SCTP_SND_FAILED_EV) + ev.sctp_send_failure_event = 1; + + if (ev_mask & SCTP_REMOTE_ERROR_EV) + ev.sctp_peer_error_event = 1; + + if (ev_mask & SCTP_SHUTDOWN_EV) + ev.sctp_shutdown_event = 1; + + if (ev_mask & SCTP_PD_EV) + ev.sctp_partial_delivery_event = 1; + + if (ev_mask & SCTP_ADAPT_EV) +#ifdef HAVE_SCTP_ADAPTATION_LAYER_EVENT + ev.sctp_adaptation_layer_event = 1; +#else + ev.sctp_adaption_layer_event = 1; +#endif + + if (setsockopt(socket, + IPPROTO_SCTP, +#ifdef SCTP_EVENTS + SCTP_EVENTS, +#else + SCTP_SET_EVENTS, +#endif + (const char*)&ev, + sizeof(ev)) != 0 ) { + fprintf(where, + "sctp_enable_event: could not set sctp events errno %d\n", + errno); + fflush(where); + exit(1); + } +} + + +static +sctp_disposition_t +sctp_process_event(socket, buf) + int socket; + void *buf; +{ + + struct sctp_assoc_change *sac; + struct sctp_send_failed *ssf; + struct sctp_paddr_change *spc; + struct sctp_remote_error *sre; + union sctp_notification *snp; + + snp = buf; + + switch (snp->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + if (debug) { + fprintf(where, "\tSCTP_ASSOC_CHANGE event, type:"); + fflush(where); + } + sac = &snp->sn_assoc_change; + switch (sac->sac_type) { + case SCTP_COMM_UP: + if (debug) { + fprintf(where, " SCTP_COMM_UP\n"); + fflush(where); + } + break; + case SCTP_RESTART: + if (debug) { + fprintf(where, " SCTP_RESTART\n"); + fflush(where); + } + break; + case SCTP_CANT_STR_ASSOC: + if (debug) { + fprintf(where, " SCTP_CANT_STR_ASSOC\n"); + fflush(where); + } + break; /* FIXME ignore above status changes */ + case SCTP_COMM_LOST: + if (debug) { + fprintf(where, " SCTP_COMM_LOST\n"); + fflush(where); + } + return SCTP_CLOSE; + case SCTP_SHUTDOWN_COMP: + if (debug) { + fprintf(where, " SCTP_SHUTDOWN_COMPLETE\n"); + fflush(where); + } + return SCTP_CLOSE; + break; + } + + case SCTP_SEND_FAILED: + if (debug) { + fprintf(where, "\tSCTP_SEND_FAILED event\n"); + fflush(where); + } + ssf = &snp->sn_send_failed; + break; /* FIXME ??? ignore this for now */ + + case SCTP_PEER_ADDR_CHANGE: + if (debug) { + fprintf(where, "\tSCTP_PEER_ADDR_CHANGE event\n"); + fflush(where); + } + spc = &snp->sn_paddr_change; + break; /* FIXME ??? ignore this for now */ + + case SCTP_REMOTE_ERROR: + if (debug) { + fprintf(where, "\tSCTP_REMOTE_ERROR event\n"); + fflush(where); + } + sre = &snp->sn_remote_error; + break; /* FIXME ??? ignore this for now */ + case SCTP_SHUTDOWN_EVENT: + if (debug) { + fprintf(where, "\tSCTP_SHUTDOWN event\n"); + fflush(where); + } + return SCTP_CLOSE; + default: + fprintf(where, "unknown type: %hu\n", snp->sn_header.sn_type); + fflush(where); + break; + } + return SCTP_OK; +} + + + +/* This routine implements the SCTP unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_sctp_stream(remote_host) +char remote_host[]; +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + +#ifdef DIRTY + int *message_int_ptr; +#endif + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + int send_socket; + int bytes_remaining; + int sctp_mss; + int timed_out; + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + double bytes_sent = 0.0; + +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + struct addrinfo *local_remote_res; + struct addrinfo *local_local_res; + + struct sctp_stream_request_struct *sctp_stream_request; + struct sctp_stream_response_struct *sctp_stream_response; + struct sctp_stream_results_struct *sctp_stream_result; + + sctp_stream_request = + (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data; + sctp_stream_response = + (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data; + sctp_stream_result = + (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + /* complete_addrinfos will either succede or exit the process */ + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_SCTP, + 0); + + if ( print_headers ) { + print_top_test_header("SCTP STREAM TEST", local_res, remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + timed_out = 0; + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_sctp_stream: sctp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sctp_stream: send_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 1, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SCTP_STREAM; + sctp_stream_request->send_buf_size = rss_size_req; + sctp_stream_request->recv_buf_size = rsr_size_req; + sctp_stream_request->receive_size = recv_size; + sctp_stream_request->no_delay = rem_nodelay; + sctp_stream_request->recv_alignment = remote_recv_align; + sctp_stream_request->recv_offset = remote_recv_offset; + sctp_stream_request->measure_cpu = remote_cpu_usage; + sctp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + sctp_stream_request->test_length = test_time; + } + else { + if (msg_count) + test_bytes = send_size * msg_count; + + sctp_stream_request->test_length = test_bytes; + } + sctp_stream_request->so_rcvavoid = rem_rcvavoid; + sctp_stream_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + sctp_stream_request->dirty_count = rem_dirty_count; + sctp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + sctp_stream_request->port = htonl(atoi(remote_data_port)); + sctp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + sctp_stream_request->non_blocking = non_block; + + + if (debug > 1) { + fprintf(where, + "netperf: send_sctp_stream: requesting sctp stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the sctp tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sctp_stream_response->recv_buf_size; + rss_size = sctp_stream_response->send_buf_size; + rem_nodelay = sctp_stream_response->no_delay; + remote_cpu_usage= sctp_stream_response->measure_cpu; + remote_cpu_rate = sctp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in */ + /* network order */ + set_port_number(remote_res, (short)sctp_stream_response->data_port_number); + + rem_rcvavoid = sctp_stream_response->so_rcvavoid; + rem_sndavoid = sctp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET) { + perror("netperf: send_sctp_stream: data socket connect failed"); + exit(1); + } + + sctp_enable_events(send_socket, SCTP_ASSOC_CHANGE_EV); + + if (non_block) { + /* now that we are connected, mark the socket as non-blocking */ + if (!set_nonblock(send_socket)) { + perror("netperf: fcntl"); + exit(1); + } + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_sctp_stream: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + +#ifdef DIRTY + /* initialize the random number generator for putting dirty stuff */ + /* into the send buffer. raj */ + srand((int) getpid()); +#endif + + /* before we start, initialize a few variables */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. at some point, we might want to replace */ + /* the rand() call with something from a table to reduce our call */ + /* overhead during the test, but it is not a high priority item. */ + message_int_ptr = (int *)(send_ring->buffer_ptr); + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + /* timestamp just before we go into send and then again just after */ + /* we come out raj 8/94 */ + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + while ((len=sctp_sendmsg(send_socket, + send_ring->buffer_ptr, send_size, + NULL, 0, + 0, 0, 0, 0, 0)) != send_size) { + if (non_block && errno == EAGAIN) + continue; + else if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + timed_out = 1; + break; + } + perror("netperf: data send error"); + printf("len was %d\n",len); + exit(1); + } + + if (timed_out) + break; /* we timed out durint sendmsg, done with test */ + +#ifdef WANT_HISTOGRAM + /* timestamp the exit from the send call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += send_size; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug > 1) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_sctp_stream: fault with sigsuspend.\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the sctp maximum segment_size was (if possible) */ + if (verbosity > 1) { + sctp_mss = -1; + get_sctp_info(send_socket, &sctp_mss); + } + + shutdown(send_socket, SHUT_WR); + + /* The test server will signal to us when it wants to shutdown. + * In blocking mode, we can call recvmsg. In non-blocking + * mode, we need to select on the socket for reading. + * We'll assume that all returns are succefull + */ + if (non_block) { + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(send_socket, &readfds); + select(send_socket+1, &readfds, NULL, NULL, NULL); + } else { + sctp_recvmsg(send_socket, send_ring->buffer_ptr, send_size, NULL, + 0, NULL, 0); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + close(send_socket); + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a sctp stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(sctp_stream_result->bytes_received); + + thruput = (double) calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = sctp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + sctp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sctp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* sctp statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)sctp_stream_result->recv_calls, + sctp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + sctp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + + + +/* This is the server-side routine for the sctp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_sctp_stream() +{ + + struct sockaddr_in myaddr_in; /* needed to get port number */ + struct sockaddr_storage peeraddr; /* used in accept */ + int s_listen,s_data; + int addrlen; + int len; + unsigned int receive_calls; + float elapsed_time; + double bytes_received; + + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + int msg_flags = 0; + +#ifdef DIRTY + int *message_int_ptr; + int dirty_count; + int clean_count; + int i; +#endif + +#ifdef DO_SELECT + fd_set readfds; + struct timeval timeout; +#endif /* DO_SELECT */ + + struct sctp_stream_request_struct *sctp_stream_request; + struct sctp_stream_response_struct *sctp_stream_response; + struct sctp_stream_results_struct *sctp_stream_results; + +#ifdef DO_SELECT + FD_ZERO(&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; +#endif /* DO_SELECT */ + + sctp_stream_request = + (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data; + sctp_stream_response = + (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data; + sctp_stream_results = + (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sctp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sctp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SCTP_STREAM_RESPONSE; + + if (debug) { + fprintf(where,"recv_sctp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_sctp_stream: requested alignment of %d\n", + sctp_stream_request->recv_alignment); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sctp_stream_request->send_buf_size; + lsr_size_req = sctp_stream_request->recv_buf_size; + loc_nodelay = sctp_stream_request->no_delay; + loc_rcvavoid = sctp_stream_request->so_rcvavoid; + loc_sndavoid = sctp_stream_request->so_sndavoid; + non_block = sctp_stream_request->non_blocking; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sctp_stream_request->ipfamily), + sctp_stream_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sctp_stream_request->ipfamily), + SOCK_STREAM, + IPPROTO_SCTP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* what sort of sizes did we end-up with? */ + if (sctp_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = sctp_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + sctp_stream_request->recv_alignment, + sctp_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_sctp_stream: set recv_size = %d, align = %d, offset = %d.\n", + recv_size, sctp_stream_request->recv_alignment, + sctp_stream_request->recv_offset); + fflush(where); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == -1){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sctp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + sctp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (sctp_stream_request->measure_cpu) { + sctp_stream_response->measure_cpu = 1; + sctp_stream_response->cpu_rate = + calibrate_local_cpu(sctp_stream_request->cpu_rate); + } + else { + sctp_stream_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sctp_stream_response->send_buf_size = lss_size; + sctp_stream_response->recv_buf_size = lsr_size; + sctp_stream_response->no_delay = loc_nodelay; + sctp_stream_response->so_rcvavoid = loc_rcvavoid; + sctp_stream_response->so_sndavoid = loc_sndavoid; + sctp_stream_response->receive_size = recv_size; + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == -1) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + send_response(); + + addrlen = sizeof(peeraddr); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + + sctp_enable_events(s_data, SCTP_ASSOC_CHANGE_EV | SCTP_SHUTDOWN_EV); + + /* now that we are connected, mark the socket as non-blocking */ + if (non_block) { + fprintf(where, "setting socket as nonblocking\n"); + fflush(where); + if (!set_nonblock(s_data)) { + close(s_data); + exit(1); + } + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sctp_stream_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + dirty_count = sctp_stream_request->dirty_count; + clean_count = sctp_stream_request->clean_count; + message_int_ptr = (int *)recv_ring->buffer_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + bytes_received = 0; + receive_calls = 0; + + while ((len = sctp_recvmsg(s_data, + recv_ring->buffer_ptr, recv_size, + NULL, 0, NULL, &msg_flags)) != 0) { + if (len == SOCKET_ERROR) { + if (non_block && errno == EAGAIN) { + if (debug){ + fprintf(where, + "recv_sctp_stream: sctp_recvmsg timed out, trying again\n"); + fflush(where); + } + Set_errno(0); + continue; + } + if (debug) { + fprintf(where, + "recv_sctp_stream: sctp_recvmsg error %d, exiting", + errno); + fflush(where); + } + netperf_response.content.serv_errno = errno; + send_response(); + close(s_data); + exit(1); + } + + if (msg_flags & MSG_NOTIFICATION) { + msg_flags = 0; + if (debug) { + fprintf(where, + "recv_sctp_stream: Got notification... processing\n"); + fflush(where); + } + if (sctp_process_event(s_data, recv_ring->buffer_ptr) == SCTP_CLOSE) + break; /* break out of the recvmsg loop */ + + continue; + } + + bytes_received += len; + receive_calls++; + + /* more to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef PAUSE + sleep(1); +#endif /* PAUSE */ + +#ifdef DIRTY + message_int_ptr = (int *)(recv_ring->buffer_ptr); + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + +#ifdef DO_SELECT + FD_SET(s_data,&readfds); + select(s_data+1,&readfds,NULL,NULL,&timeout); +#endif /* DO_SELECT */ + + } + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (close(s_data) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(sctp_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sctp_stream: got %g bytes\n", + bytes_received); + fprintf(where, + "recv_sctp_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + sctp_stream_results->bytes_received = htond(bytes_received); + sctp_stream_results->elapsed_time = elapsed_time; + sctp_stream_results->recv_calls = receive_calls; + + if (sctp_stream_request->measure_cpu) { + sctp_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_sctp_stream: test complete, sending results.\n"); + fprintf(where, + " bytes_received %g receive_calls %d\n", + bytes_received, + receive_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + sctp_stream_results->cpu_method = cpu_method; + sctp_stream_results->num_cpus = lib_num_loc_cpus; + send_response(); + + /* we are now done with the sockets */ + close(s_listen); + +} + + +/* This routine implements the SCTP unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_sctp_stream_1toMany(remote_host) +char remote_host[]; +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + +#ifdef DIRTY + int *message_int_ptr; +#endif + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + int *send_socket; + int bytes_remaining; + int sctp_mss; + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + double bytes_sent = 0.0; + +#ifdef DIRTY + int i; +#endif /* DIRTY */ + int j; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + struct addrinfo *last_remote_res; + struct addrinfo *last_local_res; + + struct sctp_stream_request_struct *sctp_stream_request; + struct sctp_stream_response_struct *sctp_stream_response; + struct sctp_stream_results_struct *sctp_stream_result; + + sctp_stream_request = + (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data; + sctp_stream_response = + (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data; + sctp_stream_result = + (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_SEQPACKET, + IPPROTO_SCTP, + 0); + + if ( print_headers ) { + print_top_test_header("SCTP 1-TO-MANY STREAM TEST",local_res,remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + send_socket = malloc(sizeof (int) * num_associations); + if (send_socket == NULL) { + fprintf(where, "send_sctp_stream_1toMany: failed to allocation sockets!\n"); + exit(1); + } + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + int j=0; + int timed_out = 0; + + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 1, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SCTP_STREAM_MANY; + sctp_stream_request->send_buf_size = rss_size_req; + sctp_stream_request->recv_buf_size = rsr_size_req; + sctp_stream_request->receive_size = recv_size; + sctp_stream_request->no_delay = rem_nodelay; + sctp_stream_request->recv_alignment = remote_recv_align; + sctp_stream_request->recv_offset = remote_recv_offset; + sctp_stream_request->measure_cpu = remote_cpu_usage; + sctp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + sctp_stream_request->test_length = test_time; + } + else { + if (msg_count) + test_bytes = send_size * msg_count; + + sctp_stream_request->test_length = test_bytes*num_associations; + } + sctp_stream_request->so_rcvavoid = rem_rcvavoid; + sctp_stream_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + sctp_stream_request->dirty_count = rem_dirty_count; + sctp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + sctp_stream_request->port = (atoi(remote_data_port)); + sctp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + sctp_stream_request->non_blocking = non_block; + + + if (debug > 1) { + fprintf(where, + "netperf: send_sctp_stream_1toMany: requesting sctp stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the sctp tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sctp_stream_response->recv_buf_size; + rss_size = sctp_stream_response->send_buf_size; + rem_nodelay = sctp_stream_response->no_delay; + remote_cpu_usage= sctp_stream_response->measure_cpu; + remote_cpu_rate = sctp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in */ + /* network order */ + set_port_number(remote_res, (unsigned short)sctp_stream_response->data_port_number); + rem_rcvavoid = sctp_stream_response->so_rcvavoid; + rem_sndavoid = sctp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /*set up the the array of data sockets and connect them to the server */ + + for (j = 0; j < num_associations; j++) { + send_socket[j] = create_data_socket(local_res); + + if (send_socket[j] < 0){ + perror("netperf: send_sctp_stream_1toMany: sctp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sctp_stream_1toMany: send_socket obtained...\n"); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket[j], + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_sctp_stream_1toMany: data socket connect failed"); + exit(1); + } + + /* Do it after connect is successfull, so that we don't see COMM_UP */ + sctp_enable_events(send_socket[j], SCTP_ASSOC_CHANGE_EV); + + if (non_block) { + /* now that we are connected, mark the socket as non-blocking */ + if (!set_nonblock(send_socket[j])) { + perror("netperf: fcntl"); + exit(1); + } + } + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes * num_associations; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_sctp_stream_1toMany: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + +#ifdef DIRTY + /* initialize the random number generator for putting dirty stuff */ + /* into the send buffer. raj */ + srand((int) getpid()); +#endif + + /* before we start, initialize a few variables */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. at some point, we might want to replace */ + /* the rand() call with something from a table to reduce our call */ + /* overhead during the test, but it is not a high priority item. */ + message_int_ptr = (int *)(send_ring->buffer_ptr); + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + /* timestamp just before we go into send and then again just after */ + /* we come out raj 8/94 */ + gettimeofday(&time_one,NULL); +#endif /* WANT_HISTOGRAM */ + + for (j = 0; j < num_associations; j++) { + + if((len=sctp_sendmsg(send_socket[j], + send_ring->buffer_ptr, + send_size, + (struct sockaddr *)remote_res->ai_addr, + remote_res->ai_addrlen, + 0, 0, 0, 0, 0)) != send_size) { + if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + j--; /* send again on the same socket */ + Set_errno(0); + continue; + } + perror("netperf: data send error"); + printf("len was %d\n",len); + exit(1); + } + } + + if (timed_out) + break; /* test is over, try next iteration */ + +#ifdef WANT_HISTOGRAM + /* timestamp the exit from the send call and update the histogram */ + gettimeofday(&time_two,NULL); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += send_size; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug > 1) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_sctp_stream_1toMany: fault with sigsuspend.\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the sctp maximum segment_size was (if possible) */ + if (verbosity > 1) { + sctp_mss = -1; + get_sctp_info(send_socket[0], &sctp_mss); + } + + /* signal the server that we are all done writing, this will + * initiate a shutdonw of one of the associations on the + * server and trigger an event telling the server it's all done + */ + sctp_sendmsg(send_socket[0], NULL, 0, remote_res->ai_addr, + remote_res->ai_addrlen, 0, MSG_EOF, 0, 0, 0); + + + /* The test server will initiate closure of all associations + * when it's done reading. We want a basic mechanism to catch this + * and are using SCTP events for this. + * In blocking mode, we can call recvmsg with the last socket we created. + * In non-blocking mode, we need to select on the socket for reading. + * We'll assume that all returns are succefull and signify + * closure. + * It is sufficient to do this on a single socket in the client. + * We choose to do it on a socket other then the one that send MSG_EOF. + * This means that anything comming in on that socket will be a shutdown. + */ + if (non_block) { + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(send_socket[num_associations-1], &readfds); + select(send_socket[num_associations-1]+1, &readfds, NULL, NULL, NULL); + } else { + sctp_recvmsg(send_socket[num_associations], send_ring->buffer_ptr, + send_size, NULL, 0, NULL, 0); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with our sockets, so close them to prevent hitting */ + /* the limit on maximum open files. */ + for (j = 0; j < num_associations; j++) + close(send_socket[j]); + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a sctp stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(sctp_stream_result->bytes_received); + + thruput = (double) calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = sctp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + sctp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sctp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* sctp statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)sctp_stream_result->recv_calls, + sctp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + sctp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + + +/* This is the server-side routine for the sctp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_sctp_stream_1toMany() +{ + + struct sockaddr_in myaddr_in; + int s_recv; + int addrlen; + int len; + unsigned int receive_calls; + float elapsed_time; + double bytes_received; + int msg_flags = 0; + + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + +#ifdef DIRTY + int *message_int_ptr; + int dirty_count; + int clean_count; + int i; +#endif + +#ifdef DO_SELECT + fd_set readfds; + struct timeval timeout; +#endif + + struct sctp_stream_request_struct *sctp_stream_request; + struct sctp_stream_response_struct *sctp_stream_response; + struct sctp_stream_results_struct *sctp_stream_results; + +#ifdef DO_SELECT + FD_ZERO(&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; +#endif + + sctp_stream_request = + (struct sctp_stream_request_struct *)netperf_request.content.test_specific_data; + sctp_stream_response = + (struct sctp_stream_response_struct *)netperf_response.content.test_specific_data; + sctp_stream_results = + (struct sctp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sctp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sctp_stream_1toMany: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SCTP_STREAM_MANY_RESPONSE; + + if (debug) { + fprintf(where,"recv_sctp_stream_1toMany: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_sctp_stream_1toMany: requested alignment of %d\n", + sctp_stream_request->recv_alignment); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sctp_stream_request->send_buf_size; + lsr_size_req = sctp_stream_request->recv_buf_size; + loc_nodelay = sctp_stream_request->no_delay; + loc_rcvavoid = sctp_stream_request->so_rcvavoid; + loc_sndavoid = sctp_stream_request->so_sndavoid; + non_block = sctp_stream_request->non_blocking; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sctp_stream_request->ipfamily), + sctp_stream_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sctp_stream_request->ipfamily), + SOCK_SEQPACKET, + IPPROTO_SCTP, + 0); + + s_recv = create_data_socket(local_res); + + if (s_recv < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* what sort of sizes did we end-up with? */ + if (sctp_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = sctp_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + sctp_stream_request->recv_alignment, + sctp_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_sctp_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_recv, 5) == -1) { + netperf_response.content.serv_errno = errno; + close(s_recv); + send_response(); + + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_recv, + (struct sockaddr *)&myaddr_in, + &addrlen) == -1){ + netperf_response.content.serv_errno = errno; + close(s_recv); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sctp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + sctp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (sctp_stream_request->measure_cpu) { + sctp_stream_response->measure_cpu = 1; + sctp_stream_response->cpu_rate = + calibrate_local_cpu(sctp_stream_request->cpu_rate); + } + else { + sctp_stream_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sctp_stream_response->send_buf_size = lss_size; + sctp_stream_response->recv_buf_size = lsr_size; + sctp_stream_response->no_delay = loc_nodelay; + sctp_stream_response->so_rcvavoid = loc_rcvavoid; + sctp_stream_response->so_sndavoid = loc_sndavoid; + sctp_stream_response->receive_size = recv_size; + + send_response(); + + + sctp_enable_events(s_recv, SCTP_ASSOC_CHANGE_EV | SCTP_SHUTDOWN_EV); + + /* now that we are connected, mark the socket as non-blocking */ + if (non_block) { + if (!set_nonblock(s_recv)) { + close(s_recv); + exit(1); + } + } + + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sctp_stream_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + dirty_count = sctp_stream_request->dirty_count; + clean_count = sctp_stream_request->clean_count; + message_int_ptr = (int *)recv_ring->buffer_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + bytes_received = 0; + receive_calls = 0; + + while ((len = sctp_recvmsg(s_recv, recv_ring->buffer_ptr, recv_size, + NULL, 0, /* we don't care who it's from */ + NULL, &msg_flags)) != 0) { + if (len < 0) { + if (non_block && errno == EAGAIN) { + Set_errno(0); + continue; + } + netperf_response.content.serv_errno = errno; + send_response(); + close(s_recv); + exit(1); + } + + if (msg_flags & MSG_NOTIFICATION) { + if (sctp_process_event(s_recv, recv_ring->buffer_ptr) == SCTP_CLOSE) + break; + + continue; + } + + bytes_received += len; + receive_calls++; + + /* more to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef PAUSE + sleep(1); +#endif /* PAUSE */ + +#ifdef DIRTY + message_int_ptr = (int *)(recv_ring->buffer_ptr); + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + +#ifdef DO_SELECT + FD_SET(s_recv,&readfds); + select(s_recv+1,&readfds,NULL,NULL,&timeout); +#endif /* DO_SELECT */ + + } + + /* perform a shutdown to signal the sender. in this case, sctp + * will close all associations on this socket + */ + if (close(s_recv) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(sctp_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sctp_stream: got %g bytes\n", + bytes_received); + fprintf(where, + "recv_sctp_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + sctp_stream_results->bytes_received = htond(bytes_received); + sctp_stream_results->elapsed_time = elapsed_time; + sctp_stream_results->recv_calls = receive_calls; + + if (sctp_stream_request->measure_cpu) { + sctp_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_sctp_stream: test complete, sending results.\n"); + fprintf(where, + " bytes_received %g receive_calls %d\n", + bytes_received, + receive_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + sctp_stream_results->cpu_method = cpu_method; + sctp_stream_results->num_cpus = lib_num_loc_cpus; + send_response(); +} + + + /* this routine implements the sending (netperf) side of the SCTP_RR */ + /* test. */ + +void +send_sctp_rr(remote_host) + char remote_host[]; +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + int send_socket; + int trans_remaining; + int msg_flags = 0; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct sockaddr_storage peer; + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct sctp_rr_request_struct *sctp_rr_request; + struct sctp_rr_response_struct *sctp_rr_response; + struct sctp_rr_results_struct *sctp_rr_result; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif /* WANT_INTERVALS */ + + sctp_rr_request = + (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data; + sctp_rr_response = + (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data; + sctp_rr_result = + (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + /* complete_addrinfos will either succede or exit the process */ + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_SCTP, + 0); + + if ( print_headers ) { + print_top_test_header("SCTP REQUEST/RESPONSE TEST", local_res, remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + send_socket = create_data_socket(local_res); + + if (send_socket < 0){ + perror("netperf: send_sctp_rr: sctp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sctp_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SCTP_RR; + sctp_rr_request->recv_buf_size = rsr_size_req; + sctp_rr_request->send_buf_size = rss_size_req; + sctp_rr_request->recv_alignment = remote_recv_align; + sctp_rr_request->recv_offset = remote_recv_offset; + sctp_rr_request->send_alignment = remote_send_align; + sctp_rr_request->send_offset = remote_send_offset; + sctp_rr_request->request_size = req_size; + sctp_rr_request->response_size = rsp_size; + sctp_rr_request->no_delay = rem_nodelay; + sctp_rr_request->measure_cpu = remote_cpu_usage; + sctp_rr_request->cpu_rate = remote_cpu_rate; + sctp_rr_request->so_rcvavoid = rem_rcvavoid; + sctp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + sctp_rr_request->test_length = test_time; + } + else { + sctp_rr_request->test_length = test_trans * -1; + } + sctp_rr_request->non_blocking = non_block; + sctp_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_sctp_rr: requesting SCTP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the sctp tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sctp_rr_response->recv_buf_size; + rss_size = sctp_rr_response->send_buf_size; + rem_nodelay = sctp_rr_response->no_delay; + remote_cpu_usage = sctp_rr_response->measure_cpu; + remote_cpu_rate = sctp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res, + (unsigned short)sctp_rr_response->data_port_number); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) <0){ + perror("netperf: send_sctp_rr data socket connect failed"); + exit(1); + } + + /* don't need events for 1-to-1 API with request-response tests */ + sctp_enable_events(send_socket, 0); + + /* set non-blocking if needed */ + if (non_block) { + if (!set_nonblock(send_socket)) { + close(send_socket); + exit(1); + } + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_sctp_rr: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + +#ifdef WANT_FIRST_BURST + { + int i; + for (i = 0; i < first_burst_size; i++) { + if((len=sctp_sendmsg(send_socket, + send_ring->buffer_ptr, req_size, + NULL, 0, /* don't need addrs with 1-to-1 */ + 0, 0, 0, 0, 0)) != req_size) { + /* we should never hit the end of the test in the first burst */ + perror("send_sctp_rr: initial burst data send error"); + exit(1); + } + } + } +#endif /* WANT_FIRST_BURST */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + +#ifdef WANT_HISTOGRAM + /* timestamp just before our call to send, and then again just */ + /* after the receive raj 8/94 */ + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + while ((len=sctp_sendmsg(send_socket, + send_ring->buffer_ptr, req_size, + NULL, 0, /* don't need addrs with 1-to-1 */ + 0, 0, 0, 0, 0)) != req_size) { + if (non_block && errno == EAGAIN) { + /* try sending again */ + continue; + } else if (SOCKET_EINTR(len) || (errno == 0)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_sctp_rr: data send error"); + exit(1); + } + + if (timed_out) { + /* we timed out while sending. break out another level */ + break; + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + do { + msg_flags = 0; + if ((rsp_bytes_recvd=sctp_recvmsg(send_socket, + temp_message_ptr, rsp_bytes_left, + NULL, 0, + NULL, &msg_flags)) < 0) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + continue; + } + perror("send_sctp_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } while (!(msg_flags & MSG_EOR)); + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += 1; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug > 1) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_sctp_rr: fault with signal set!\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + + /* At this point we used to call shutdown on the data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated CPU utilization. If it wasn't supposed to care, it */ + /* will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where,"netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + + /* We now calculate what our throughput was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = sctp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + sctp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + close(send_socket); + + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sctp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + + /* this routine implements the receive (netserver) side of a TCP_RR */ + /* test */ +void +recv_sctp_rr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_in myaddr_in, peeraddr_in; + int s_listen, s_data; + int addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct sctp_rr_request_struct *sctp_rr_request; + struct sctp_rr_response_struct *sctp_rr_response; + struct sctp_rr_results_struct *sctp_rr_results; + + sctp_rr_request = + (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data; + sctp_rr_response = + (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data; + sctp_rr_results = + (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sctp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sctp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SCTP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_sctp_rr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_sctp_rr: requested recv alignment of %d offset %d\n", + sctp_rr_request->recv_alignment, + sctp_rr_request->recv_offset); + fprintf(where,"recv_sctp_rr: requested send alignment of %d offset %d\n", + sctp_rr_request->send_alignment, + sctp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + sctp_rr_request->response_size, + sctp_rr_request->send_alignment, + sctp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + sctp_rr_request->request_size, + sctp_rr_request->recv_alignment, + sctp_rr_request->recv_offset); + + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_sctp_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sctp_rr_request->send_buf_size; + lsr_size_req = sctp_rr_request->recv_buf_size; + loc_nodelay = sctp_rr_request->no_delay; + loc_rcvavoid = sctp_rr_request->so_rcvavoid; + loc_sndavoid = sctp_rr_request->so_sndavoid; + non_block = sctp_rr_request->non_blocking; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sctp_rr_request->ipfamily), + sctp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sctp_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_SCTP, + 0); + + s_listen = create_data_socket(local_res); + + if (s_listen < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == -1) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, &addrlen) == -1){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sctp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + sctp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + sctp_rr_response->measure_cpu = 0; + + if (sctp_rr_request->measure_cpu) { + sctp_rr_response->measure_cpu = 1; + sctp_rr_response->cpu_rate = calibrate_local_cpu(sctp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sctp_rr_response->send_buf_size = lss_size; + sctp_rr_response->recv_buf_size = lsr_size; + sctp_rr_response->no_delay = loc_nodelay; + sctp_rr_response->so_rcvavoid = loc_rcvavoid; + sctp_rr_response->so_sndavoid = loc_sndavoid; + sctp_rr_response->test_length = sctp_rr_request->test_length; + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == -1) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + + exit(1); + } + + /* we do not need events on a 1-to-1 RR test. The test will finish + * once all transactions are done. + */ + + /* now that we are connected, mark the socket as non-blocking */ + if (non_block) { + if (!set_nonblock(s_data)) { + perror("netperf: set_nonblock"); + exit(1); + } + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + if (debug) { + fprintf(where,"recv_sctp_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sctp_rr_request->measure_cpu); + + /* The loop will exit when we hit the end of the test time, or when */ + /* we have exchanged the requested number of transactions. */ + + if (sctp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(sctp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = sctp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + int msg_flags = 0; + + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = sctp_rr_request->request_size; + while(!(msg_flags & MSG_EOR)) { + if((request_bytes_recvd=sctp_recvmsg(s_data, + temp_message_ptr, + request_bytes_remaining, + NULL, 0, + NULL, &msg_flags)) < 0) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + continue; /* while request_bytes_remaining */ + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + if (debug) { + fprintf(where,"yo55\n"); + fflush(where); + } + break; + } + + + /* Now, send the response to the remote + * In 1-to-1 API destination addr is not needed. + */ + while ((bytes_sent=sctp_sendmsg(s_data, + send_ring->buffer_ptr, + sctp_rr_request->response_size, + NULL, 0, + 0, 0, 0, 0, 0)) == -1) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + continue; + } + + netperf_response.content.serv_errno = 982; + send_response(); + exit(1); + } + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + if (debug) { + fprintf(where,"yo6\n"); + fflush(where); + } + break; + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(sctp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sctp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + sctp_rr_results->bytes_received = (trans_received * + (sctp_rr_request->request_size + + sctp_rr_request->response_size)); + sctp_rr_results->trans_received = trans_received; + sctp_rr_results->elapsed_time = elapsed_time; + sctp_rr_results->cpu_method = cpu_method; + sctp_rr_results->num_cpus = lib_num_loc_cpus; + if (sctp_rr_request->measure_cpu) { + sctp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_sctp_rr: test complete, sending results.\n"); + fflush(where); + } + + /* we are now done with the sockets */ + send_response(); + + close(s_data); + close(s_listen); + +} + + + +/* this routine implements the sending (netperf) side of the + SCTP_RR_1TOMANY test */ + +void +send_sctp_rr_1toMany(remote_host) + char remote_host[]; +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len, j = 0; + char *temp_message_ptr; + int nummessages; + int *send_socket; + int trans_remaining; + double bytes_xferd; + int msg_flags = 0; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct sockaddr_storage peer; + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct sctp_rr_request_struct *sctp_rr_request; + struct sctp_rr_response_struct *sctp_rr_response; + struct sctp_rr_results_struct *sctp_rr_result; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif /* WANT_INTERVALS */ + + sctp_rr_request = + (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data; + sctp_rr_response = + (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data; + sctp_rr_result = + (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_SEQPACKET, + IPPROTO_SCTP, + 0); + + if ( print_headers ) { + print_top_test_header("SCTP 1-TO-MANY REQUEST/RESPONSE TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + send_socket = malloc(sizeof(int) * num_associations); + if (send_socket == NULL) { + fprintf(where, + "Could not create the socket array for %d associations", + num_associations); + fflush(where); + exit(1); + } + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SCTP_RR_MANY; + sctp_rr_request->recv_buf_size = rsr_size_req; + sctp_rr_request->send_buf_size = rss_size_req; + sctp_rr_request->recv_alignment = remote_recv_align; + sctp_rr_request->recv_offset = remote_recv_offset; + sctp_rr_request->send_alignment = remote_send_align; + sctp_rr_request->send_offset = remote_send_offset; + sctp_rr_request->request_size = req_size; + sctp_rr_request->response_size = rsp_size; + sctp_rr_request->no_delay = rem_nodelay; + sctp_rr_request->measure_cpu = remote_cpu_usage; + sctp_rr_request->cpu_rate = remote_cpu_rate; + sctp_rr_request->so_rcvavoid = rem_rcvavoid; + sctp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + sctp_rr_request->test_length = test_time; + } + else { + sctp_rr_request->test_length = test_trans * num_associations + * -1; + } + sctp_rr_request->non_blocking = non_block; + sctp_rr_request->port = atoi(remote_data_port); + sctp_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where,"netperf: send_sctp_rr_1toMany: requesting SCTP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the sctp tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + rsr_size = sctp_rr_response->recv_buf_size; + rss_size = sctp_rr_response->send_buf_size; + rem_nodelay = sctp_rr_response->no_delay; + remote_cpu_usage = sctp_rr_response->measure_cpu; + remote_cpu_rate = sctp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res, + (unsigned short)sctp_rr_response->data_port_number); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /*set up the data socket list */ + for (j = 0; j < num_associations; j++) { + send_socket[j] = create_data_socket(local_res); + + if (send_socket < 0){ + perror("netperf: send_sctp_rr_1toMany: sctp stream data socket"); + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket[j], + remote_res->ai_addr, + remote_res->ai_addrlen) < 0){ + perror("netperf: data socket connect failed"); + + exit(1); + } + + /* The client end of the 1-to-Many test uses 1-to-1 sockets. + * it doesn't need events. + */ + sctp_enable_events(send_socket[j], 0); + + if (non_block) { + if (!set_nonblock(send_socket[j])) { + close(send_socket[j]); + exit(1); + } + } + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes * num_associations; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_sctp_rr_1toMany: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + +#ifdef WANT_FIRST_BURST + { + int i; + for (j = 0; j < num_associations; j++) { + for (i = 0; i < first_burst_size; i++) { + if((len=sctp_sendmsg(send_socket[j], + send_ring->buffer_ptr, send_size, + remote_res->ai_addr, + remote_res->ai_addrlen, + 0, 0, 0, 0, 0)) != req_size) { + /* we should never hit the end of the test in the first burst */ + perror("send_sctp_rr_1toMany: initial burst data send error"); + exit(1); + } + } + } + } +#endif /* WANT_FIRST_BURST */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + + /* this is a fairly poor way of testing 1toMany connections. + * For each association we measure round trip time to account for + * any delay in lookups and delivery. To stress the server a bit + * more we would need a distributed client test, or at least multiple + * processes. I want to force as much paralellism as possible, but + * this will do for the fist take. vlad + */ + for (j = 0; j < num_associations; j++) { +#ifdef WANT_HISTOGRAM + /* timestamp just before our call to send, and then again just */ + /* after the receive raj 8/94 */ + gettimeofday(&time_one,NULL); +#endif /* WANT_HISTOGRAM */ + + while ((len=sctp_sendmsg(send_socket[j], + send_ring->buffer_ptr, send_size, + remote_res->ai_addr, + remote_res->ai_addrlen, + 0, 0, 0, 0, 0)) != req_size) { + if (non_block && errno == EAGAIN) { + /* try sending again */ + continue; + } else if ((errno == EINTR) || (errno == 0)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_sctp_rr_1toMany: data send error"); + exit(1); + } + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + /* setup for the next time */ + send_ring = send_ring->next; + + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while (!(msg_flags & MSG_EOR)) { + if((rsp_bytes_recvd = sctp_recvmsg(send_socket[j], + temp_message_ptr, + rsp_bytes_left, + NULL, 0, + NULL, &msg_flags)) < 0) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + continue; + } + perror("send_sctp_rr_1toMany: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + gettimeofday(&time_two,NULL); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + } + + /* At this point we used to call shutdown on the data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated CPU utilization. If it wasn't supposed to care, it */ + /* will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where,"netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + + /* We now calculate what our throughput was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = sctp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + sctp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + for (j = 0; j < num_associations; j++) + close(send_socket[j]); + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sctp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + + + /* this routine implements the receive (netserver) side of a TCP_RR */ + /* test */ +void +recv_sctp_rr_1toMany() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + + struct sockaddr_in myaddr_in; /* needed to get the port number */ + struct sockaddr_storage peeraddr; /* to communicate with peer */ + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + int msg_flags; + + int s_rcv; + int addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int bytes_recvd; + int recv_buf_size; + int timed_out = 0; + float elapsed_time; + + struct sctp_rr_request_struct *sctp_rr_request; + struct sctp_rr_response_struct *sctp_rr_response; + struct sctp_rr_results_struct *sctp_rr_results; + + sctp_rr_request = + (struct sctp_rr_request_struct *)netperf_request.content.test_specific_data; + sctp_rr_response = + (struct sctp_rr_response_struct *)netperf_response.content.test_specific_data; + sctp_rr_results = + (struct sctp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sctp_rr_1toMany: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sctp_rr_1toMany: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SCTP_RR_MANY_RESPONSE; + + if (debug) { + fprintf(where,"recv_sctp_rr_1toMany: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_sctp_rr_1toMany: requested recv alignment of %d offset %d\n", + sctp_rr_request->recv_alignment, + sctp_rr_request->recv_offset); + fprintf(where,"recv_sctp_rr_1toMany: requested send alignment of %d offset %d\n", + sctp_rr_request->send_alignment, + sctp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + sctp_rr_request->response_size, + sctp_rr_request->send_alignment, + sctp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + sctp_rr_request->request_size, + sctp_rr_request->recv_alignment, + sctp_rr_request->recv_offset); + + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sctp_rr_request->send_buf_size; + lsr_size_req = sctp_rr_request->recv_buf_size; + loc_nodelay = sctp_rr_request->no_delay; + loc_rcvavoid = sctp_rr_request->so_rcvavoid; + loc_sndavoid = sctp_rr_request->so_sndavoid; + non_block = sctp_rr_request->non_blocking; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sctp_rr_request->ipfamily), + sctp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sctp_rr_request->ipfamily), + SOCK_SEQPACKET, + IPPROTO_SCTP, + 0); + + /* Grab a socket to listen on, and then listen on it. */ + if (debug) { + fprintf(where,"recv_sctp_rr_1toMany: grabbing a socket...\n"); + fflush(where); + } + + s_rcv = create_data_socket(local_res); + + if (s_rcv < 0) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_rcv, 5) == -1) { + netperf_response.content.serv_errno = errno; + close(s_rcv); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_rcv, + (struct sockaddr *)&myaddr_in, &addrlen) == -1){ + netperf_response.content.serv_errno = errno; + close(s_rcv); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sctp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + sctp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + sctp_rr_response->measure_cpu = 0; + + if (sctp_rr_request->measure_cpu) { + sctp_rr_response->measure_cpu = 1; + sctp_rr_response->cpu_rate = calibrate_local_cpu(sctp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sctp_rr_response->send_buf_size = lss_size; + sctp_rr_response->recv_buf_size = lsr_size; + sctp_rr_response->no_delay = loc_nodelay; + sctp_rr_response->so_rcvavoid = loc_rcvavoid; + sctp_rr_response->so_sndavoid = loc_sndavoid; + sctp_rr_response->test_length = sctp_rr_request->test_length; + send_response(); + + /* Don't need events */ + sctp_enable_events(s_rcv, 0); + + /* now that we are connected, mark the socket as non-blocking */ + if (non_block) { + if (!set_nonblock(s_rcv)) { + perror("netperf: set_nonblock"); + exit(1); + } + } + + /* FIXME: The way 1-to-Many test operates right now, we are including + * association setup time into our measurements. The reason for this + * is that the client creates multiple endpoints and connects each + * endpoint to us using the connect call. On this end we simply call + * recvmsg() to get data becuase there is no equivalen of accept() for + * 1-to-Many API. + * I think this is OK, but if it were to be fixed, the server side + * would need to know how many associations are being setup and + * have a recvmsg() loop with SCTP_ASSOC_CHANGE events waiting for + * all the associations to be be established. + * I am punting on this for now. + */ + + + addrlen = sizeof(peeraddr); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sctp_rr_request->measure_cpu); + + /* The loop will exit when we hit the end of the test time, or when */ + /* we have exchanged the requested number of transactions. */ + + if (sctp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(sctp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = sctp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + recv_buf_size = sctp_rr_request->request_size; + + /* Receive the data. We don't particularly care which association + * the data came in on. We'll simply be doing a receive untill + * we get and MSG_EOR flag (meaning that a single transmission was + * received) and a send to the same address, so the RR would be for + * the same associations. + * We can get away with this because the client will establish all + * the associations before transmitting any data. Any partial data + * will not have EOR thus will we will not send a response untill + * we get everything. + */ + + do { + msg_flags = 0; + if((bytes_recvd = sctp_recvmsg(s_rcv, + recv_ring->buffer_ptr, + recv_buf_size, + (struct sockaddr *)&peeraddr, &addrlen, + 0, &msg_flags)) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_recvd)) { + /* the timer popped */ + timed_out = 1; + break; + } else if (non_block & errno == EAGAIN) { + /* do recvmsg again */ + continue; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + } while(!(msg_flags & MSG_EOR)); + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + if (debug) { + fprintf(where,"yo5\n"); + fflush(where); + } + break; + } + + /* Now, send the response to the remote */ + while ((bytes_sent=sctp_sendmsg(s_rcv, + send_ring->buffer_ptr, + sctp_rr_request->response_size, + (struct sockaddr *)&peeraddr, addrlen, + 0, 0, 0, 0, 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_sent)) { + /* the test timer has popped */ + timed_out = 1; + break; + } else if (non_block && errno == EAGAIN) { + continue; + } + + netperf_response.content.serv_errno = 992; + send_response(); + exit(1); + } + + if (timed_out) { + if (debug) { + fprintf(where,"yo6\n"); + fflush(where); + } + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + break; + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(sctp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sctp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + sctp_rr_results->bytes_received = (trans_received * + (sctp_rr_request->request_size + + sctp_rr_request->response_size)); + sctp_rr_results->trans_received = trans_received; + sctp_rr_results->elapsed_time = elapsed_time; + sctp_rr_results->cpu_method = cpu_method; + sctp_rr_results->num_cpus = lib_num_loc_cpus; + if (sctp_rr_request->measure_cpu) { + sctp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_sctp_rr: test complete, sending results.\n"); + fflush(where); + } + + /* we are now done with the sockets */ + close(s_rcv); + + send_response(); + +} + + +void +print_sctp_usage() +{ + + printf("%s",sctp_usage); + exit(1); + +} +void +scan_sctp_args(argc, argv) + int argc; + char *argv[]; + +{ + +#define SOCKETS_ARGS "BDhH:I:L:m:M:P:r:s:S:VN:T:46" + + extern char *optarg; /* pointer to option string */ + + int c; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (no_control) { + fprintf(where, + "The SCTP tests do not know how to deal with no control tests\n"); + exit(-1); + } + + strncpy(local_data_port,"0",sizeof(local_data_port)); + strncpy(remote_data_port,"0",sizeof(remote_data_port)); + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form "first," (see the routine break_args.. */ + + while ((c= getopt(argc, argv, SOCKETS_ARGS)) != EOF) { + switch (c) { + case '?': + case '4': + remote_data_family = AF_INET; + local_data_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + remote_data_family = AF_INET6; + local_data_family = AF_INET6; +#else + fprintf(stderr, + "This netperf was not compiled on an IPv6 capable host!\n"); + fflush(stderr); + exit(-1); +#endif + break; + case 'h': + print_sctp_usage(); + exit(1); + case 'b': +#ifdef WANT_FIRST_BURST + first_burst_size = atoi(optarg); +#else /* WANT_FIRST_BURST */ + printf("Initial request burst functionality not compiled-in!\n"); +#endif /* WANT_FIRST_BURST */ + break; + case 'D': + /* set the nodelay flag */ + loc_nodelay = 1; + rem_nodelay = 1; + break; + case 'H': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + remote_data_address = malloc(strlen(arg1)+1); + strncpy(remote_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + remote_data_family = parse_address_family(arg2); + break; + case 'L': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + local_data_address = malloc(strlen(arg1)+1); + strncpy(local_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + local_data_family = parse_address_family(arg2); + break; + case 'P': + /* set the local and remote data port numbers for the tests to + allow them to run through those blankety blank end-to-end + breaking firewalls. raj 2004-06-15 */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_data_port,arg1,sizeof(local_data_port)); + if (arg2[0]) + strncpy(remote_data_port,arg2,sizeof(remote_data_port)); + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size_req = convert(arg1); + if (arg2[0]) + lsr_size_req = convert(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size_req = convert(arg1); + if (arg2[0]) + rsr_size_req = convert(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = convert(arg1); + if (arg2[0]) + rsp_size = convert(arg2); + break; + case 'm': + /* set size of the buffer for each sent message */ + send_size = convert(optarg); + break; + case 'M': + /* set the size of the buffer for each received message */ + recv_size = convert(optarg); + break; + case 't': + /* set the test name */ + strcpy(test_name,optarg); + break; + case 'W': + /* set the "width" of the user space data */ + /* buffer. This will be the number of */ + /* send_size buffers malloc'd in the */ + /* *_STREAM test. It may be enhanced to set */ + /* both send and receive "widths" but for now */ + /* it is just the sending *_STREAM. */ + send_width = convert(optarg); + break; + case 'V': + /* we want to do copy avoidance and will set */ + /* it for everything, everywhere, if we really */ + /* can. of course, we don't know anything */ + /* about the remote... */ +#ifdef SO_SND_COPYAVOID + loc_sndavoid = 1; +#else + loc_sndavoid = 0; + printf("Local send copy avoidance not available.\n"); +#endif +#ifdef SO_RCV_COPYAVOID + loc_rcvavoid = 1; +#else + loc_rcvavoid = 0; + printf("Local recv copy avoidance not available.\n"); +#endif + rem_sndavoid = 1; + rem_rcvavoid = 1; + break; + case 'N': + /* this opton allows the user to set the number of + * messages to send. This in effect modifies the test + * time. If we know the message size, then the we can + * express the test time as message_size * number_messages + */ + msg_count = convert (optarg); + if (msg_count > 0) + test_time = 0; + break; + case 'B': + non_block = 1; + break; + case 'T': + num_associations = atoi(optarg); + if (num_associations <= 1) { + printf("Number of SCTP associations must be >= 1\n"); + exit(1); + } + break; + }; + } +} + +#endif /* WANT_SCTP */ diff --git a/nettest_sctp.h b/nettest_sctp.h new file mode 100644 index 0000000..5297853 --- /dev/null +++ b/nettest_sctp.h @@ -0,0 +1,128 @@ +/* + Copyright (C) 1993-2003 Hewlett-Packard Company +*/ + + /* This file contains the test-specific definitions for netperf's BSD */ + /* sockets tests */ + + +struct sctp_stream_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int receive_size; /* how many bytes do we want to receive at one */ + /* time? */ + int recv_alignment; /* what is the alignment of the receive */ + /* buffer? */ + int recv_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int port; /* the to port to which recv side should bind + to allow netperf to run through firewalls */ + int ipfamily; /* address family of ipaddress */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sctp_stream_response_struct { + int recv_buf_size; /* how big does the client want it */ + int receive_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sctp_stream_results_struct { + double bytes_received; + unsigned int recv_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct sctp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the to port to which recv side should bind + to allow netperf to run through firewalls */ + int ipfamily; /* address family of ipaddress */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sctp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sctp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +#define SCTP_SNDRCV_INFO_EV 0x01 +#define SCTP_ASSOC_CHANGE_EV 0x02 +#define SCTP_PEERADDR_CHANGE_EV 0x04 +#define SCTP_SND_FAILED_EV 0x08 +#define SCTP_REMOTE_ERROR_EV 0x10 +#define SCTP_SHUTDOWN_EV 0x20 +#define SCTP_PD_EV 0x40 +#define SCTP_ADAPT_EV 0x80 + +typedef enum sctp_disposition { + SCTP_OK = 1, + SCTP_CLOSE, +} sctp_disposition_t; + +extern void send_sctp_stream(); +extern void send_sctp_rr(); + +extern void recv_sctp_stream(); +extern void recv_sctp_rr(); + +extern void loc_cpu_rate(); +extern void rem_cpu_rate(); diff --git a/nettest_sdp.c b/nettest_sdp.c new file mode 100644 index 0000000..696fd3e --- /dev/null +++ b/nettest_sdp.c @@ -0,0 +1,3553 @@ +#ifndef lint +char nettest_sdp[]="\ +@(#)nettest_sdp.c (c) Copyright 2007 Hewlett-Packard Co. Version 2.4.4"; +#else +#define DIRTY +#define WANT_HISTOGRAM +#define WANT_INTERVALS +#endif /* lint */ + +/****************************************************************/ +/* */ +/* nettest_sdp.c */ +/* */ +/* */ +/* scan_sdp_args() get the sdp command line args */ +/* */ +/* the actual test routines... */ +/* */ +/* send_sdp_stream() perform a sdp stream test */ +/* recv_sdp_stream() */ +/* send_sdp_rr() perform a sdp request/response */ +/* recv_sdp_rr() */ +/* */ +/* relies on create_data_socket in nettest_bsd.c */ +/****************************************************************/ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined(WANT_SDP) + +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#ifdef NOSTDLIBH +#include <malloc.h> +#else /* NOSTDLIBH */ +#include <stdlib.h> +#endif /* NOSTDLIBH */ + +#if !defined(__VMS) +#include <sys/ipc.h> +#endif /* !defined(__VMS) */ +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> + +/* would seem that not all sdp.h files define a MSG_EOF, but that + MSG_EOF can be the same as MSG_FIN so lets work with that + assumption. initial find by Jon Pedersen. raj 2006-02-01 */ +#ifndef MSG_EOF +#ifdef MSG_FIN +#define MSG_EOF MSG_FIN +#else +#error Must have either MSG_EOF or MSG_FIN defined +#endif +#endif + +#include "netlib.h" +#include "netsh.h" +/* get some of the functions from nettest_bsd.c */ +#include "nettest_bsd.h" +#include "nettest_sdp.h" + +#ifdef WANT_HISTOGRAM +#ifdef __sgi +#include <sys/time.h> +#endif /* __sgi */ +#include "hist.h" +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_FIRST_BURST +extern int first_burst_size; +#endif /* WANT_FIRST_BURST */ + + + +/* these variables are specific to SDP tests. declare */ +/* them static to make them global only to this file. */ + +static int + msg_count = 0, /* number of messages to transmit on association */ + non_block = 0, /* default to blocking sockets */ + num_associations = 1; /* number of associations on the endpoint */ + +static int confidence_iteration; +static char local_cpu_method; +static char remote_cpu_method; + +#ifdef WANT_HISTOGRAM +static struct timeval time_one; +static struct timeval time_two; +static HIST time_hist; +#endif /* WANT_HISTOGRAM */ + + +char sdp_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +SDP Sockets Test Options:\n\ + -b number Send number requests at the start of _RR tests\n\ + -D [L][,R] Set SDP_NODELAY locally and/or remotely\n\ + -h Display this text\n\ + -H name,fam Use name (or IP) and family as target of data connection\n\ + -L name,fam Use name (or IP) and family as source of data connextion\n\ + -m bytes Set the size of each sent message\n\ + -M bytes Set the size of each received messages\n\ + -P local[,remote] Set the local/remote port for the data socket\n\ + -r req,[rsp] Set request/response sizes (_RR tests)\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ + -V Enable copy avoidance if supported\n\ + -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\ + -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + + /* This routine is intended to retrieve interesting aspects of sdp */ + /* for the data connection. at first, it attempts to retrieve the */ + /* maximum segment size. later, it might be modified to retrieve */ + /* other information, but it must be information that can be */ + /* retrieved quickly as it is called during the timing of the test. */ + /* for that reason, a second routine may be created that can be */ + /* called outside of the timing loop */ +static +void +get_sdp_info(int socket, int * mss) +{ + +#ifdef TCP_MAXSEG + netperf_socklen_t sock_opt_len; + + sock_opt_len = sizeof(netperf_socklen_t); + if (getsockopt(socket, + getprotobyname("tcp")->p_proto, + TCP_MAXSEG, + (char *)mss, + &sock_opt_len) == SOCKET_ERROR) { + fprintf(where, + "netperf: get_sdp_info: getsockopt TCP_MAXSEG: errno %d\n", + errno); + fflush(where); + *mss = -1; + } +#else + *mss = -1; +#endif /* TCP_MAXSEG */ + +} + +void +send_sdp_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %s\n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages = 0; + SOCKET send_socket; + int bytes_remaining; + int sdp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + + unsigned long long local_bytes_sent = 0; + double bytes_sent = 0.0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct sdp_stream_request_struct *sdp_stream_request; + struct sdp_stream_response_struct *sdp_stream_response; + struct sdp_stream_results_struct *sdp_stream_result; + + sdp_stream_request = + (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data; + sdp_stream_response = + (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data; + sdp_stream_result = + (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + /* complete_addrinfos will either succede or exit the process */ + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("SDP STREAM TEST",local_res,remote_res); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_sdp_stream: sdp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sdp_stream: send_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 1, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SDP_STREAM; + sdp_stream_request->send_buf_size = rss_size_req; + sdp_stream_request->recv_buf_size = rsr_size_req; + sdp_stream_request->receive_size = recv_size; + sdp_stream_request->no_delay = rem_nodelay; + sdp_stream_request->recv_alignment = remote_recv_align; + sdp_stream_request->recv_offset = remote_recv_offset; + sdp_stream_request->measure_cpu = remote_cpu_usage; + sdp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + sdp_stream_request->test_length = test_time; + } + else { + sdp_stream_request->test_length = test_bytes; + } + sdp_stream_request->so_rcvavoid = rem_rcvavoid; + sdp_stream_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + sdp_stream_request->dirty_count = rem_dirty_count; + sdp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + sdp_stream_request->port = atoi(remote_data_port); + sdp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where, + "netperf: send_sdp_stream: requesting SDP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the SDP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sdp_stream_response->recv_buf_size; + rss_size = sdp_stream_response->send_buf_size; + rem_nodelay = sdp_stream_response->no_delay; + remote_cpu_usage= sdp_stream_response->measure_cpu; + remote_cpu_rate = sdp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in + network order */ + set_port_number(remote_res, + (short)sdp_stream_response->data_port_number); + + rem_rcvavoid = sdp_stream_response->so_rcvavoid; + rem_sndavoid = sdp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lss_size,rsr_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_sdp_stream: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* we only start the interval timer if we are using the + timer-timed intervals rather than the sit and spin ones. raj + 2006-02-06 */ +#if defined(WANT_INTERVALS) + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before we go into send and then again just + after we come out raj 8/94 */ + /* but lets only do this if there is going to be a histogram + displayed */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + if((len=send(send_socket, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + break; + } + perror("netperf: data send error"); + printf("len was %d\n",len); + exit(1); + } + + local_bytes_sent += send_size; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp the exit from the send call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(send_size) +#endif + +#if defined(WANT_INTERVALS) + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the SDP maximum segment_size was (if possible) */ + if (verbosity > 1) { + sdp_mss = -1; + get_sdp_info(send_socket,&sdp_mss); + } + + if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown sdp stream socket"); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has */ + /* brought all the data up into the application. it will do a */ + /* shutdown to cause a FIN to be sent our way. We will assume that */ + /* any exit from the recv() call is good... raj 4/93 */ + + recv(send_socket, send_ring->buffer_ptr, send_size, 0); + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(send_socket); + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting + things. If it wasn't supposed to care, it will return obvious + values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the + future, we may want to include a calculation of the thruput + measured by the remote, but it should be the case that for a + SDP stream test, that the two numbers should be *very* + close... We calculate bytes_sent regardless of the way the + test length was controlled. If it was time, we needed to, + and if it was by bytes, the user may have specified a number + of bytes that wasn't a multiple of the send_size, so we + really didn't send what he asked for ;-) */ + + bytes_sent = ntohd(sdp_stream_result->bytes_received); + } + else { + bytes_sent = (double)local_bytes_sent; + } + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = sdp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + sdp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sdp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* SDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)sdp_stream_result->recv_calls, + sdp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + sdp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + + +/* This routine implements the netperf-side SDP unidirectional data + transfer test (a.k.a. stream) for the sockets interface where the + data flow is from the netserver to the netperf. It receives its + parameters via global variables from the shell and writes its + output to the standard output. */ + + +void +send_sdp_maerts(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n %s"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Recvs %-8.8s Sends\n\ +Local Remote Local Remote Xfered Per Per\n\ +Recv Send Recv Send Recv (avg) Send (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + + /* what we want is to have a buffer space that is at least one */ + /* recv-size greater than our recv window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + struct ring_elt *recv_ring; + + int len; + unsigned int nummessages = 0; + SOCKET recv_socket; + int bytes_remaining; + int sdp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can recv > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + double bytes_sent = 0.0; + unsigned long long local_bytes_recvd = 0; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + struct addrinfo *remote_res; + struct addrinfo *local_res; + + struct sdp_maerts_request_struct *sdp_maerts_request; + struct sdp_maerts_response_struct *sdp_maerts_response; + struct sdp_maerts_results_struct *sdp_maerts_result; + + sdp_maerts_request = + (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data; + sdp_maerts_response = + (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data; + sdp_maerts_result = + (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("SDP MAERTS TEST",local_res,remote_res); + } + + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + recv_socket = create_data_socket(local_res); + + if (recv_socket == INVALID_SOCKET){ + perror("netperf: send_sdp_maerts: sdp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sdp_maerts: recv_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the recv */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the recv size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (recv_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one recv-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* recv_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our recv */ + /* buffers, we should respect that wish... */ + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + if (recv_ring == NULL) { + /* only allocate the recv ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + local_recv_align, + local_recv_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 1, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SDP_MAERTS; + sdp_maerts_request->send_buf_size = rss_size_req; + sdp_maerts_request->recv_buf_size = rsr_size_req; + sdp_maerts_request->send_size = send_size; + sdp_maerts_request->no_delay = rem_nodelay; + sdp_maerts_request->send_alignment = remote_send_align; + sdp_maerts_request->send_offset = remote_send_offset; + sdp_maerts_request->measure_cpu = remote_cpu_usage; + sdp_maerts_request->cpu_rate = remote_cpu_rate; + if (test_time) { + sdp_maerts_request->test_length = test_time; + } + else { + sdp_maerts_request->test_length = test_bytes; + } + sdp_maerts_request->so_rcvavoid = rem_rcvavoid; + sdp_maerts_request->so_sndavoid = rem_sndavoid; +#ifdef DIRTY + sdp_maerts_request->dirty_count = rem_dirty_count; + sdp_maerts_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + sdp_maerts_request->port = atoi(remote_data_port); + sdp_maerts_request->ipfamily = af_to_nf(remote_res->ai_family); + if (debug > 1) { + fprintf(where, + "netperf: send_sdp_maerts: requesting SDP maerts test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the SDP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sdp_maerts_response->recv_buf_size; + rss_size = sdp_maerts_response->send_buf_size; + rem_nodelay = sdp_maerts_response->no_delay; + remote_cpu_usage= sdp_maerts_response->measure_cpu; + remote_cpu_rate = sdp_maerts_response->cpu_rate; + send_size = sdp_maerts_response->send_size; + + /* we have to make sure that the server port number is in + network order */ + set_port_number(remote_res, + (short)sdp_maerts_response->data_port_number); + rem_rcvavoid = sdp_maerts_response->so_rcvavoid; + rem_sndavoid = sdp_maerts_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_STREAM_SETUP(lsr_size,rss_size) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(recv_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: send_sdp_maerts: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a maerts test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + if (!no_control) { + /* this is a netperf to netserver test, netserver will close + to tell us the test is over, so use PAD_TIME to avoid + causing the netserver fits. */ + start_timer(test_time + PAD_TIME); + } + else { + /* this is a netperf to data source test, no PAD_TIME */ + start_timer(test_time); + } + } + else { + /* The tester wanted to recv a number of bytes. we don't do that + in a SDP_MAERTS test. sorry. raj 2002-06-21 */ + printf("netperf: send_sdp_maerts: test must be timed\n"); + exit(1); + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + /* the test will continue until we either get a zero-byte recv() + on the socket or our failsafe timer expires. most of the time + we trust that we get a zero-byte recieve from the socket. raj + 2002-06-21 */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before we go into recv and then again just + after we come out raj 8/94 */ + /* but only if we are actually going to display a histogram. raj + 2006-02-07 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + while ((!times_up) && (len=recv(recv_socket, + recv_ring->buffer_ptr, + recv_size, + 0)) > 0 ) { + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp the exit from the recv call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef DIRTY + access_buffer(recv_ring->buffer_ptr, + recv_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_DEMO + DEMO_STREAM_INTERVAL(len); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the recv width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + recv_ring = recv_ring->next; + if (bytes_remaining) { + bytes_remaining -= len; + } + + local_bytes_recvd += len; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* make sure we timestamp just before we go into recv */ + /* raj 2004-06-15 */ + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + } + + /* an EINTR is to be expected when this is a no_control test */ + if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) { + perror("send_sdp_maerts: data recv error"); + printf("len was %d\n",len); + exit(1); + } + + /* if we get here, it must mean we had a recv return of 0 before + the watchdog timer expired, or the watchdog timer expired and + this was a no_control test */ + + /* The test is over. Flush the buffers to the remote end. We do a + graceful release to tell the remote we have all the data. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the SDP maximum segment_size was (if possible) */ + if (verbosity > 1) { + sdp_mss = -1; + get_sdp_info(recv_socket,&sdp_mss); + } + + if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) { + perror("netperf: cannot shutdown sdp maerts socket"); + exit(1); + } + + stop_timer(); + + /* this call will always give us the local elapsed time for the + test, and will also store-away the necessaries for cpu + utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* we are finished with the socket, so close it to prevent hitting */ + /* the limit on maximum open files. */ + + close(recv_socket); + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated service demand and all those interesting + things. If it wasn't supposed to care, it will return obvious + values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the + future, we may want to include a calculation of the thruput + measured by the remote, but it should be the case that for a + SDP maerts test, that the two numbers should be *very* + close... We calculate bytes_sent regardless of the way the + test length was controlled. If it was time, we needed to, + and if it was by bytes, the user may have specified a number + of bytes that wasn't a multiple of the recv_size, so we + really didn't recv what he asked for ;-) */ + + bytes_sent = ntohd(sdp_maerts_result->bytes_sent); + } + else { + bytes_sent = (double)local_bytes_recvd; + } + + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = sdp_maerts_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + sdp_maerts_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sdp_maerts_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the recvs */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lsr_size, /* local recvbuf size */ + rss_size, /* remot sendbuf size */ + send_size, /* how large were the recvs */ + elapsed_time, /* how long did it take */ + thruput, /* how fast did it go */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* SDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_recv_align, + remote_recv_align, + local_recv_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)sdp_maerts_result->send_calls, + sdp_maerts_result->send_calls); + fprintf(where, + ksink_fmt2, + sdp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in recv() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} +/* This is the server-side routine for the sdp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_sdp_stream() +{ + + struct sockaddr_in myaddr_in, peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + int len; + unsigned int receive_calls; + float elapsed_time; + double bytes_received; + + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + +#ifdef DO_SELECT + fd_set readfds; + struct timeval timeout; +#endif /* DO_SELECT */ + + struct sdp_stream_request_struct *sdp_stream_request; + struct sdp_stream_response_struct *sdp_stream_response; + struct sdp_stream_results_struct *sdp_stream_results; + +#ifdef DO_SELECT + FD_ZERO(&readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; +#endif /* DO_SELECT */ + + sdp_stream_request = + (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data; + sdp_stream_response = + (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data; + sdp_stream_results = + (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sdp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sdp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SDP_STREAM_RESPONSE; + + if (debug) { + fprintf(where,"recv_sdp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_sdp_stream: requested alignment of %d\n", + sdp_stream_request->recv_alignment); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sdp_stream_request->send_buf_size; + lsr_size_req = sdp_stream_request->recv_buf_size; + loc_nodelay = sdp_stream_request->no_delay; + loc_rcvavoid = sdp_stream_request->so_rcvavoid; + loc_sndavoid = sdp_stream_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sdp_stream_request->ipfamily), + sdp_stream_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sdp_stream_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + /* what sort of sizes did we end-up with? */ + if (sdp_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = sdp_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + sdp_stream_request->recv_alignment, + sdp_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_sdp_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sdp_stream_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + sdp_stream_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (sdp_stream_request->measure_cpu) { + sdp_stream_response->measure_cpu = 1; + sdp_stream_response->cpu_rate = + calibrate_local_cpu(sdp_stream_request->cpu_rate); + } + else { + sdp_stream_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sdp_stream_response->send_buf_size = lss_size; + sdp_stream_response->recv_buf_size = lsr_size; + sdp_stream_response->no_delay = loc_nodelay; + sdp_stream_response->so_rcvavoid = loc_rcvavoid; + sdp_stream_response->so_sndavoid = loc_sndavoid; + sdp_stream_response->receive_size = recv_size; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sdp_stream_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + /* there used to be an #ifdef DIRTY call to access_buffer() here, + but we have switched from accessing the buffer before the recv() + call to accessing the buffer after the recv() call. The + accessing before was, IIRC, related to having dirty data when + doing page-flipping copy avoidance. */ + + bytes_received = 0; + receive_calls = 0; + + while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) { + if (len == SOCKET_ERROR ) + { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + bytes_received += len; + receive_calls++; + +#ifdef DIRTY + /* we access the buffer after the recv() call now, rather than before */ + access_buffer(recv_ring->buffer_ptr, + recv_size, + sdp_stream_request->dirty_count, + sdp_stream_request->clean_count); +#endif /* DIRTY */ + + + /* move to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef PAUSE + sleep(1); +#endif /* PAUSE */ + +#ifdef DO_SELECT + FD_SET(s_data,&readfds); + select(s_data+1,&readfds,NULL,NULL,&timeout); +#endif /* DO_SELECT */ + + } + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(sdp_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sdp_stream: got %g bytes\n", + bytes_received); + fprintf(where, + "recv_sdp_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + sdp_stream_results->bytes_received = htond(bytes_received); + sdp_stream_results->elapsed_time = elapsed_time; + sdp_stream_results->recv_calls = receive_calls; + + sdp_stream_results->cpu_method = cpu_method; + sdp_stream_results->num_cpus = lib_num_loc_cpus; + + if (sdp_stream_request->measure_cpu) { + sdp_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_sdp_stream: test complete, sending results.\n"); + fprintf(where, + " bytes_received %g receive_calls %d\n", + bytes_received, + receive_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + send_response(); + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + } + +/* This is the server-side routine for the sdp maerts test. It is + implemented as one routine. I could break things-out somewhat, but + didn't feel it was necessary. */ + +void +recv_sdp_maerts() +{ + + struct sockaddr_in myaddr_in, peeraddr_in; + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + int len; + unsigned int send_calls; + float elapsed_time; + double bytes_sent = 0.0 ; + + struct ring_elt *send_ring; + + struct sdp_maerts_request_struct *sdp_maerts_request; + struct sdp_maerts_response_struct *sdp_maerts_response; + struct sdp_maerts_results_struct *sdp_maerts_results; + + sdp_maerts_request = + (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data; + sdp_maerts_response = + (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data; + sdp_maerts_results = + (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sdp_maerts: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired + parameters and then let the initiator know that all is ready. If + socket size defaults are to be used, then the initiator will have + sent us 0's. If the socket sizes cannot be changed, then we will + send-back what they are. If that information cannot be + determined, then we send-back -1's for the sizes. If things go + wrong for any reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It + would be best if the error that the remote reports to the user is + the actual error we encountered, rather than some bogus + unexpected response type message. */ + + if (debug) { + fprintf(where,"recv_sdp_maerts: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SDP_MAERTS_RESPONSE; + + if (debug) { + fprintf(where,"recv_sdp_maerts: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_sdp_maerts: requested alignment of %d\n", + sdp_maerts_request->send_alignment); + fflush(where); + } + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_sdp_maerts: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sdp_maerts_request->send_buf_size; + lsr_size_req = sdp_maerts_request->recv_buf_size; + loc_nodelay = sdp_maerts_request->no_delay; + loc_rcvavoid = sdp_maerts_request->so_rcvavoid; + loc_sndavoid = sdp_maerts_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sdp_maerts_request->ipfamily), + sdp_maerts_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sdp_maerts_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* what sort of sizes did we end-up with? */ + if (sdp_maerts_request->send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + else { + send_size = sdp_maerts_request->send_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the recving side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (send_width == 0) { + send_width = (lsr_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + send_ring = allocate_buffer_ring(send_width, + send_size, + sdp_maerts_request->send_alignment, + sdp_maerts_request->send_offset); + + if (debug) { + fprintf(where,"recv_sdp_maerts: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sdp_maerts_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + sdp_maerts_response->cpu_rate = (float)0.0; /* assume no cpu */ + if (sdp_maerts_request->measure_cpu) { + sdp_maerts_response->measure_cpu = 1; + sdp_maerts_response->cpu_rate = + calibrate_local_cpu(sdp_maerts_request->cpu_rate); + } + else { + sdp_maerts_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sdp_maerts_response->send_buf_size = lss_size; + sdp_maerts_response->recv_buf_size = lsr_size; + sdp_maerts_response->no_delay = loc_nodelay; + sdp_maerts_response->so_rcvavoid = loc_rcvavoid; + sdp_maerts_response->so_sndavoid = loc_sndavoid; + sdp_maerts_response->send_size = send_size; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* we will start the timer before the accept() to be somewhat + analagous to the starting of the timer before the connect() call + in the SDP_STREAM test. raj 2002-06-21 */ + + start_timer(sdp_maerts_request->test_length); + + /* Now it's time to start receiving data on the connection. We will + first grab the apropriate counters and then start grabbing. */ + + cpu_start(sdp_maerts_request->measure_cpu); + + + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + + /* this is for those systems which *INCORRECTLY* fail to pass + attributes across an accept() call. Including this goes against + my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + bytes_sent = 0.0; + send_calls = 0; + + len = 0; /* nt-lint; len is not initialized (printf far below) if + times_up initially true.*/ + times_up = 0; /* must remember to initialize this little beauty */ + while (!times_up) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + access_buffer(send_ring->buffer_ptr, + send_size, + sdp_maerts_request->dirty_count, + sdp_maerts_request->clean_count); + +#endif /* DIRTY */ + + if((len=send(s_data, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || SOCKET_EINTR(len)) { + /* the test was interrupted, must be the end of test */ + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + bytes_sent += len; + send_calls++; + + /* more to the next buffer in the send_ring */ + send_ring = send_ring->next; + + } + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* hang a recv() off the socket to block until the remote has + brought all the data up into the application. it will do a + shutdown to cause a FIN to be sent our way. We will assume that + any exit from the recv() call is good... raj 4/93 */ + + recv(s_data, send_ring->buffer_ptr, send_size, 0); + + + cpu_stop(sdp_maerts_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sdp_maerts: got %g bytes\n", + bytes_sent); + fprintf(where, + "recv_sdp_maerts: got %d sends\n", + send_calls); + fflush(where); + } + + sdp_maerts_results->bytes_sent = htond(bytes_sent); + sdp_maerts_results->elapsed_time = elapsed_time; + sdp_maerts_results->send_calls = send_calls; + + if (sdp_maerts_request->measure_cpu) { + sdp_maerts_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_sdp_maerts: test complete, sending results.\n"); + fprintf(where, + " bytes_sent %g send_calls %d\n", + bytes_sent, + send_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + sdp_maerts_results->cpu_method = cpu_method; + sdp_maerts_results->num_cpus = lib_num_loc_cpus; + send_response(); + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + } + + + /* this routine implements the sending (netperf) side of the SDP_RR */ + /* test. */ + +void +send_sdp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f %s\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f %s\n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c %s\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct addrinfo *local_res; + struct addrinfo *remote_res; + + struct sdp_rr_request_struct *sdp_rr_request; + struct sdp_rr_response_struct *sdp_rr_response; + struct sdp_rr_results_struct *sdp_rr_result; + +#ifdef WANT_FIRST_BURST +#define REQUEST_CWND_INITIAL 2 + /* "in the beginning..." the WANT_FIRST_BURST stuff was like both + Unix and the state of New Jersey - both were simple an unspoiled. + then it was realized that some stacks are quite picky about + initial congestion windows and a non-trivial initial burst of + requests would not be individual segments even with TCP_NODELAY + set. so, we have to start tracking a poor-man's congestion window + up here in window space because we want to try to make something + happen that frankly, we cannot guarantee with the specification + of SDP. ain't that grand?-) raj 2006-01-30 */ + int requests_outstanding = 0; + int request_cwnd = REQUEST_CWND_INITIAL; /* we ass-u-me that having + three requests + outstanding at the + beginning of the test + is ok with SDP stacks + of interest. the first + two will come from our + first_burst loop, and + the third from our + regularly scheduled + send */ +#endif + + sdp_rr_request = + (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data; + sdp_rr_response= + (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data; + sdp_rr_result = + (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + time_hist = HIST_new(); + } +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + complete_addrinfos(&remote_res, + &local_res, + remote_host, + SOCK_STREAM, + IPPROTO_TCP, + 0); + + if ( print_headers ) { + print_top_test_header("SDP REQUEST/RESPONSE TEST",local_res,remote_res); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + +#ifdef WANT_FIRST_BURST + /* we have to remember to reset the number of transactions + outstanding and the "congestion window for each new + iteration. raj 2006-01-31 */ + requests_outstanding = 0; + request_cwnd = REQUEST_CWND_INITIAL; +#endif + + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + send_socket = create_data_socket(local_res); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_sdp_rr: sdp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_sdp_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + if (!no_control) { + /* Tell the remote end to do a listen. The server alters the + socket paramters on the other side at this point, hence the + reason for all the values being passed in the setup + message. If the user did not specify any of the parameters, + they will be passed as 0, which will indicate to the remote + that no changes beyond the system's default should be + used. Alignment is the exception, it will default to 8, which + will be no alignment alterations. */ + + netperf_request.content.request_type = DO_SDP_RR; + sdp_rr_request->recv_buf_size = rsr_size_req; + sdp_rr_request->send_buf_size = rss_size_req; + sdp_rr_request->recv_alignment = remote_recv_align; + sdp_rr_request->recv_offset = remote_recv_offset; + sdp_rr_request->send_alignment = remote_send_align; + sdp_rr_request->send_offset = remote_send_offset; + sdp_rr_request->request_size = req_size; + sdp_rr_request->response_size = rsp_size; + sdp_rr_request->no_delay = rem_nodelay; + sdp_rr_request->measure_cpu = remote_cpu_usage; + sdp_rr_request->cpu_rate = remote_cpu_rate; + sdp_rr_request->so_rcvavoid = rem_rcvavoid; + sdp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + sdp_rr_request->test_length = test_time; + } + else { + sdp_rr_request->test_length = test_trans * -1; + } + sdp_rr_request->port = atoi(remote_data_port); + sdp_rr_request->ipfamily = af_to_nf(remote_res->ai_family); + + if (debug > 1) { + fprintf(where,"netperf: send_sdp_rr: requesting SDP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant + socket parameters for this test type. We will put them back + into the variables here so they can be displayed if desired. + The remote will have calibrated CPU if necessary, and will + have done all the needed set-up we will have calibrated the + cpu locally before sending the request, and will grab the + counter value right after the connect returns. The remote + will grab the counter right after the accept call. This saves + the hassle of extra messages being sent for the SDP + tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = sdp_rr_response->recv_buf_size; + rss_size = sdp_rr_response->send_buf_size; + rem_nodelay = sdp_rr_response->no_delay; + remote_cpu_usage = sdp_rr_response->measure_cpu; + remote_cpu_rate = sdp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + set_port_number(remote_res,(short)sdp_rr_response->data_port_number); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where, + "netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + + exit(1); + } + } + +#ifdef WANT_DEMO + DEMO_RR_SETUP(1000) +#endif + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + remote_res->ai_addr, + remote_res->ai_addrlen) == INVALID_SOCKET){ + perror("netperf: data socket connect failed"); + + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + INTERVALS_INIT(); +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + +#ifdef WANT_DEMO + if (demo_mode) { + HIST_timestamp(demo_one_ptr); + } +#endif + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + +#ifdef WANT_FIRST_BURST + /* we can inject no more than request_cwnd, which will grow with + time, and no more than first_burst_size. we don't use <= to + account for the "regularly scheduled" send call. of course + that makes it more a "max_outstanding_ than a + "first_burst_size" but for now we won't fix the names. also, + I suspect the extra check against < first_burst_size is + redundant since later I expect to make sure that request_cwnd + can never get larger than first_burst_size, but just at the + moment I'm feeling like a belt and suspenders kind of + programmer. raj 2006-01-30 */ + while ((first_burst_size > 0) && + (requests_outstanding < request_cwnd) && + (requests_outstanding < first_burst_size)) { + if (debug) { + fprintf(where, + "injecting, req_outstndng %d req_cwnd %d burst %d\n", + requests_outstanding, + request_cwnd, + first_burst_size); + } + if ((len = send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + /* we should never hit the end of the test in the first burst */ + perror("send_sdp_rr: initial burst data send error"); + exit(-1); + } + requests_outstanding += 1; + } + +#endif /* WANT_FIRST_BURST */ + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + /* timestamp just before our call to send, and then again just + after the receive raj 8/94 */ + /* but only if we are actually going to display one. raj + 2007-02-07 */ + + HIST_timestamp(&time_one); + } +#endif /* WANT_HISTOGRAM */ + + if ((len = send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (SOCKET_EINTR(len) || (errno == 0)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_sdp_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + +#ifdef WANT_FIRST_BURST + requests_outstanding += 1; +#endif + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if ( SOCKET_EINTR(rsp_bytes_recvd) ) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_sdp_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + +#ifdef WANT_FIRST_BURST + /* so, since we've gotten a response back, update the + bookkeeping accordingly. there is one less request + outstanding and we can put one more out there than before. */ + requests_outstanding -= 1; + if (request_cwnd < first_burst_size) { + request_cwnd += 1; + if (debug) { + fprintf(where, + "incr req_cwnd to %d first_burst %d reqs_outstndng %d\n", + request_cwnd, + first_burst_size, + requests_outstanding); + } + } +#endif + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + } +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_DEMO + DEMO_RR_INTERVAL(1); +#endif + +#ifdef WANT_INTERVALS + INTERVALS_WAIT(); +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + + /* At this point we used to call shutdown on the data socket to be + sure all the data was delivered, but this was not germane in a + request/response test, and it was causing the tests to "hang" + when they were being controlled by time. So, I have replaced + this shutdown call with a call to close that can be found later + in the procedure. */ + + /* this call will always give us the elapsed time for the test, + and will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + if (!no_control) { + /* Get the statistics from the remote end. The remote will have + calculated CPU utilization. If it wasn't supposed to care, it + will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + fprintf(where,"netperf: remote error %d", + netperf_response.content.serv_errno); + perror(""); + fflush(where); + exit(1); + } + } + + /* We now calculate what our throughput was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu + utilization for the system(s) Of course, some of the + information might be bogus because there was no idle counter in + the kernel(s). We need to make a note of this for the user's + benefit... */ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will + multiply the number of transaction by 1024 to get "good" + numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = sdp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will + multiply the number of transaction by 1024 to get "good" + numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + sdp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = (float) -1.0; + local_service_demand = (float) -1.0; + remote_cpu_utilization = (float) -1.0; + remote_service_demand = (float) -1.0; + } + + /* at this point, we want to calculate the confidence information. + if debugging is on, calculate_confidence will print-out the + parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + close(send_socket); + + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user has + specified zero-level verbosity, we will just print the local + service demand, or the remote service demand. If the user has + requested verbosity level 1, he will get the basic "streamperf" + numbers. If the user has specified a verbosity of greater than 1, + we will display a veritable plethora of background information + from outside of this block as it it not cpu_measurement + specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(sdp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand, /* remote service demand */ + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput, + ((print_headers) || + (result_brand == NULL)) ? "" : result_brand); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* SDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + /* this routine implements the receive (netserver) side of a SDP_RR */ + /* test */ +void +recv_sdp_rr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct addrinfo *local_res; + char local_name[BUFSIZ]; + char port_buffer[PORTBUFSIZE]; + + struct sockaddr_in myaddr_in, + peeraddr_in; + SOCKET s_listen,s_data; + netperf_socklen_t addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + int sock_closed = 0; + float elapsed_time; + + struct sdp_rr_request_struct *sdp_rr_request; + struct sdp_rr_response_struct *sdp_rr_response; + struct sdp_rr_results_struct *sdp_rr_results; + + sdp_rr_request = + (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data; + sdp_rr_response = + (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data; + sdp_rr_results = + (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_sdp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_sdp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = SDP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_sdp_rr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_sdp_rr: requested recv alignment of %d offset %d\n", + sdp_rr_request->recv_alignment, + sdp_rr_request->recv_offset); + fprintf(where,"recv_sdp_rr: requested send alignment of %d offset %d\n", + sdp_rr_request->send_alignment, + sdp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + sdp_rr_request->response_size, + sdp_rr_request->send_alignment, + sdp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + sdp_rr_request->request_size, + sdp_rr_request->recv_alignment, + sdp_rr_request->recv_offset); + + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_sdp_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_data_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = sdp_rr_request->send_buf_size; + lsr_size_req = sdp_rr_request->recv_buf_size; + loc_nodelay = sdp_rr_request->no_delay; + loc_rcvavoid = sdp_rr_request->so_rcvavoid; + loc_sndavoid = sdp_rr_request->so_sndavoid; + + set_hostname_and_port(local_name, + port_buffer, + nf_to_af(sdp_rr_request->ipfamily), + sdp_rr_request->port); + + local_res = complete_addrinfo(local_name, + local_name, + port_buffer, + nf_to_af(sdp_rr_request->ipfamily), + SOCK_STREAM, + IPPROTO_TCP, + 0); + + /* fake things out by changing local_res->ai_family to AF_INET_SDP */ + local_res->ai_family = AF_INET_SDP; + local_res->ai_protocol = 0; + s_listen = create_data_socket(local_res); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + +#ifdef WIN32 + /* The test timer can fire during operations on the listening socket, + so to make the start_timer below work we have to move + it to close s_listen while we are blocked on accept. */ + win_kludge_socket2 = s_listen; +#endif + + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + sdp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + sdp_rr_response->cpu_rate = (float)0.0; /* assume no cpu */ + sdp_rr_response->measure_cpu = 0; + + if (sdp_rr_request->measure_cpu) { + sdp_rr_response->measure_cpu = 1; + sdp_rr_response->cpu_rate = calibrate_local_cpu(sdp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + sdp_rr_response->send_buf_size = lss_size; + sdp_rr_response->recv_buf_size = lsr_size; + sdp_rr_response->no_delay = loc_nodelay; + sdp_rr_response->so_rcvavoid = loc_rcvavoid; + sdp_rr_response->so_sndavoid = loc_sndavoid; + sdp_rr_response->test_length = sdp_rr_request->test_length; + send_response(); + + addrlen = sizeof(peeraddr_in); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + + exit(1); + } + +#ifdef KLUDGE_SOCKET_OPTIONS + /* this is for those systems which *INCORRECTLY* fail to pass */ + /* attributes across an accept() call. Including this goes against */ + /* my better judgement :( raj 11/95 */ + + kludge_socket_options(s_data); + +#endif /* KLUDGE_SOCKET_OPTIONS */ + +#ifdef WIN32 + /* this is used so the timer thread can close the socket out from */ + /* under us, which to date is the easiest/cleanest/least */ + /* Windows-specific way I can find to force the winsock calls to */ + /* return WSAEINTR with the test is over. anything that will run on */ + /* 95 and NT and is closer to what netperf expects from Unix signals */ + /* and such would be appreciated raj 1/96 */ + win_kludge_socket = s_data; +#endif /* WIN32 */ + + if (debug) { + fprintf(where,"recv_sdp_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(sdp_rr_request->measure_cpu); + + /* The loop will exit when we hit the end of the test time, or when */ + /* we have exchanged the requested number of transactions. */ + + if (sdp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(sdp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = sdp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = sdp_rr_request->request_size; + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(request_bytes_recvd)) + { + timed_out = 1; + break; + } + + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else if( request_bytes_recvd == 0 ) { + if (debug) { + fprintf(where,"zero is my hero\n"); + fflush(where); + } + sock_closed = 1; + break; + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + recv_ring = recv_ring->next; + + if ((timed_out) || (sock_closed)) { + /* we hit the end of the test based on time - or the socket + closed on us along the way. bail out of here now... */ + if (debug) { + fprintf(where,"yo5\n"); + fflush(where); + } + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=send(s_data, + send_ring->buffer_ptr, + sdp_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (SOCKET_EINTR(bytes_sent)) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 992; + send_response(); + exit(1); + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(sdp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_sdp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + sdp_rr_results->bytes_received = (trans_received * + (sdp_rr_request->request_size + + sdp_rr_request->response_size)); + sdp_rr_results->trans_received = trans_received; + sdp_rr_results->elapsed_time = elapsed_time; + sdp_rr_results->cpu_method = cpu_method; + sdp_rr_results->num_cpus = lib_num_loc_cpus; + if (sdp_rr_request->measure_cpu) { + sdp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_sdp_rr: test complete, sending results.\n"); + fflush(where); + } + + /* we are now done with the sockets */ + close(s_data); + close(s_listen); + + send_response(); + +} + + + +void +print_sdp_usage() +{ + + printf("%s",sdp_usage); + exit(1); + +} +void +scan_sdp_args(argc, argv) + int argc; + char *argv[]; + +{ + +#define SOCKETS_ARGS "b:DhH:I:L:m:M:P:r:s:S:V46" + + extern char *optarg; /* pointer to option string */ + + int c; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (no_control) { + fprintf(where, + "The SDP tests do not know how to deal with no control tests\n"); + exit(-1); + } + + strncpy(local_data_port,"0",sizeof(local_data_port)); + strncpy(remote_data_port,"0",sizeof(remote_data_port)); + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form "first," (see the routine break_args.. */ + + while ((c= getopt(argc, argv, SOCKETS_ARGS)) != EOF) { + switch (c) { + case '?': + case '4': + remote_data_family = AF_INET; + local_data_family = AF_INET; + break; + case '6': +#if defined(AF_INET6) + remote_data_family = AF_INET6; + local_data_family = AF_INET6; +#else + fprintf(stderr, + "This netperf was not compiled on an IPv6 capable host!\n"); + fflush(stderr); + exit(-1); +#endif + break; + case 'h': + print_sdp_usage(); + exit(1); + case 'b': +#ifdef WANT_FIRST_BURST + first_burst_size = atoi(optarg); +#else /* WANT_FIRST_BURST */ + printf("Initial request burst functionality not compiled-in!\n"); +#endif /* WANT_FIRST_BURST */ + break; + case 'D': + /* set the nodelay flag */ + loc_nodelay = 1; + rem_nodelay = 1; + break; + case 'H': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + remote_data_address = malloc(strlen(arg1)+1); + strncpy(remote_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + remote_data_family = parse_address_family(arg2); + break; + case 'L': + break_args_explicit(optarg,arg1,arg2); + if (arg1[0]) { + /* make sure we leave room for the NULL termination boys and + girls. raj 2005-02-82 */ + local_data_address = malloc(strlen(arg1)+1); + strncpy(local_data_address,arg1,strlen(arg1)); + } + if (arg2[0]) + local_data_family = parse_address_family(arg2); + break; + case 'P': + /* set the local and remote data port numbers for the tests to + allow them to run through those blankety blank end-to-end + breaking firewalls. raj 2004-06-15 */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strncpy(local_data_port,arg1,sizeof(local_data_port)); + if (arg2[0]) + strncpy(remote_data_port,arg2,sizeof(remote_data_port)); + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size_req = convert(arg1); + if (arg2[0]) + lsr_size_req = convert(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size_req = convert(arg1); + if (arg2[0]) + rsr_size_req = convert(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = convert(arg1); + if (arg2[0]) + rsp_size = convert(arg2); + break; + case 'm': + /* set size of the buffer for each sent message */ + send_size = convert(optarg); + break; + case 'M': + /* set the size of the buffer for each received message */ + recv_size = convert(optarg); + break; + case 't': + /* set the test name */ + strcpy(test_name,optarg); + break; + case 'W': + /* set the "width" of the user space data */ + /* buffer. This will be the number of */ + /* send_size buffers malloc'd in the */ + /* *_STREAM test. It may be enhanced to set */ + /* both send and receive "widths" but for now */ + /* it is just the sending *_STREAM. */ + send_width = convert(optarg); + break; + case 'V': + /* we want to do copy avoidance and will set */ + /* it for everything, everywhere, if we really */ + /* can. of course, we don't know anything */ + /* about the remote... */ +#ifdef SO_SND_COPYAVOID + loc_sndavoid = 1; +#else + loc_sndavoid = 0; + printf("Local send copy avoidance not available.\n"); +#endif +#ifdef SO_RCV_COPYAVOID + loc_rcvavoid = 1; +#else + loc_rcvavoid = 0; + printf("Local recv copy avoidance not available.\n"); +#endif + rem_sndavoid = 1; + rem_rcvavoid = 1; + break; + case 'N': + /* this opton allows the user to set the number of + * messages to send. This in effect modifies the test + * time. If we know the message size, then the we can + * express the test time as message_size * number_messages + */ + msg_count = convert (optarg); + if (msg_count > 0) + test_time = 0; + break; + case 'B': + non_block = 1; + break; + case 'T': + num_associations = atoi(optarg); + if (num_associations <= 1) { + printf("Number of SDP associations must be >= 1\n"); + exit(1); + } + break; + }; + } +} + +#endif /* WANT_SDP */ diff --git a/nettest_sdp.h b/nettest_sdp.h new file mode 100644 index 0000000..31d76bc --- /dev/null +++ b/nettest_sdp.h @@ -0,0 +1,170 @@ +/* + Copyright (C) 2007 Hewlett-Packard Company +*/ + + /* This file contains the test-specific definitions for netperf's SDP */ + /* sockets tests */ + +/* 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 + +struct sdp_stream_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int receive_size; /* how many bytes do we want to receive at one */ + /* time? */ + int recv_alignment; /* what is the alignment of the receive */ + /* buffer? */ + int recv_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int port; /* the to port to which recv side should bind + to allow netperf to run through firewalls */ + int ipfamily; /* address family of ipaddress */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sdp_stream_response_struct { + int recv_buf_size; /* how big does the client want it */ + int receive_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sdp_stream_results_struct { + double bytes_received; + unsigned int recv_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct sdp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int port; /* the to port to which recv side should bind + to allow netperf to run through firewalls */ + int ipfamily; /* address family of ipaddress */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sdp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int non_blocking; /* run the test in non-blocking mode */ +}; + +struct sdp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +struct sdp_maerts_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int send_size; /* how many bytes do we want netserver to send + at one time? */ + int send_alignment; /* what is the alignment of the send */ + /* buffer? */ + int send_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the send buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int port; /* the port to which the recv side should bind + to allow netperf to run through those evil + firewall things */ + int ipfamily; +}; + +struct sdp_maerts_response_struct { + int recv_buf_size; /* how big does the client want it */ + int send_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct sdp_maerts_results_struct { + double bytes_sent; + unsigned int send_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs had the remote? */ +}; + +extern void send_sdp_stream(); +extern void send_sdp_rr(); + +extern void recv_sdp_stream(); +extern void recv_sdp_rr(); + +extern void loc_cpu_rate(); +extern void rem_cpu_rate(); diff --git a/nettest_unix.c b/nettest_unix.c new file mode 100644 index 0000000..e4716a4 --- /dev/null +++ b/nettest_unix.c @@ -0,0 +1,3431 @@ +#ifdef lint +#define WANT_UNIX +#define DIRTY +#define WANT_INTERVALS +#endif /* lint */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WANT_UNIX +char nettest_unix_id[]="\ +@(#)nettest_unix.c (c) Copyright 1994-2007 Hewlett-Packard Co. Version 2.4.3"; + +/****************************************************************/ +/* */ +/* nettest_bsd.c */ +/* */ +/* the BSD sockets parsing routine... */ +/* */ +/* scan_unix_args() */ +/* */ +/* the actual test routines... */ +/* */ +/* send_stream_stream() perform a stream stream test */ +/* recv_stream_stream() */ +/* send_stream_rr() perform a stream request/response */ +/* recv_stream_rr() */ +/* send_dg_stream() perform a dg stream test */ +/* recv_dg_stream() */ +/* send_dg_rr() perform a dg request/response */ +/* recv_dg_rr() */ +/* loc_cpu_rate() determine the local cpu maxrate */ +/* rem_cpu_rate() find the remote cpu maxrate */ +/* */ +/****************************************************************/ + + /* at some point, I might want to go-in and see if I really need all */ + /* these includes, but for the moment, we'll let them all just sit */ + /* there. raj 8/94 */ +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#ifndef WIN32 +#include <sys/ipc.h> +#include <sys/socket.h> +#include <errno.h> +#include <signal.h> +#include <sys/un.h> +#include <unistd.h> +#else /* WIN32 */ +#include <process.h> +#include <winsock2.h> +#include <windows.h> +#endif /* WIN32 */ +#include <string.h> +#include <time.h> +#include <sys/time.h> + +#ifdef NOSTDLIBH +#include <malloc.h> +#else /* NOSTDLIBH */ +#include <stdlib.h> +#endif /* NOSTDLIBH */ + +#include <sys/stat.h> + + +#include "netlib.h" +#include "netsh.h" +#include "nettest_unix.h" + + + + /* these variables are specific to the UNIX sockets tests. declare */ + /* them static to make them global only to this file. */ + +#define UNIX_PRFX "netperf." +#define UNIX_LENGTH_MAX 0xFFFF - 28 + +static char + path_prefix[32]; + +static int + rss_size, /* remote socket send buffer size */ + rsr_size, /* remote socket recv buffer size */ + lss_size_req, /* requested local socket send buffer size */ + lsr_size_req, /* requested local socket recv buffer size */ + lss_size, /* local socket send buffer size */ + lsr_size, /* local socket recv buffer size */ + req_size = 1, /* request size */ + rsp_size = 1, /* response size */ + send_size, /* how big are individual sends */ + recv_size; /* how big are individual receives */ + + /* different options for the sockets */ + + +char unix_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +STREAM/DG UNIX Sockets Test Options:\n\ + -h Display this text\n\ + -m bytes Set the send size (STREAM_STREAM, DG_STREAM)\n\ + -M bytes Set the recv size (STREAM_STREAM, DG_STREAM)\n\ + -p dir Set the directory where pipes are created\n\ + -r req,res Set request,response size (STREAM_RR, DG_RR)\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + /* this routing initializes all the test specific variables */ + +static void +init_test_vars() +{ + rss_size = 0; + rsr_size = 0; + lss_size_req = 0; + lsr_size_req = 0; + lss_size = 0; + lsr_size = 0; + req_size = 1; + rsp_size = 1; + send_size = 0; + recv_size = 0; + + strcpy(path_prefix,"/tmp"); + +} + + /* This routine will create a data (listen) socket with the apropriate */ + /* options set and return it to the caller. this replaces all the */ + /* duplicate code in each of the test routines and should help make */ + /* things a little easier to understand. since this routine can be */ + /* called by either the netperf or netserver programs, all output */ + /* should be directed towards "where." family is generally AF_UNIX, */ + /* and type will be either SOCK_STREAM or SOCK_DGRAM */ +SOCKET +create_unix_socket(int family, int type) +{ + + SOCKET temp_socket; + int sock_opt_len; + + /*set up the data socket */ + temp_socket = socket(family, + type, + 0); + + if (temp_socket == INVALID_SOCKET){ + fprintf(where, + "netperf: create_unix_socket: socket: %d\n", + errno); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where,"create_unix_socket: socket %d obtained...\n",temp_socket); + fflush(where); + } + + /* Modify the local socket size. The reason we alter the send buffer */ + /* size here rather than when the connection is made is to take care */ + /* of decreases in buffer size. Decreasing the window size after */ + /* connection establishment is a STREAM no-no. Also, by setting the */ + /* buffer (window) size before the connection is established, we can */ + /* control the STREAM MSS (segment size). The MSS is never more that 1/2 */ + /* the minimum receive buffer size at each half of the connection. */ + /* This is why we are altering the receive buffer size on the sending */ + /* size of a unidirectional transfer. If the user has not requested */ + /* that the socket buffers be altered, we will try to find-out what */ + /* their values are. If we cannot touch the socket buffer in any way, */ + /* we will set the values to -1 to indicate that. */ + + set_sock_buffer(temp_socket, SEND_BUFFER, lss_size_req, &lss_size); + set_sock_buffer(temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size); + + return(temp_socket); + +} + + +/* This routine implements the STREAM unidirectional data transfer test */ +/* (a.k.a. stream) for the sockets interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_stream_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %% us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1 = + "%5d %5d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; +#endif + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + +#ifdef DIRTY + int *message_int_ptr; +#endif +#include <sys/stat.h> + + struct ring_elt *send_ring; + + int len = 0; + int nummessages; + SOCKET send_socket; + int bytes_remaining; + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) */ + double bytes_sent; + +#ifdef DIRTY + int i; +#endif /* DIRTY */ + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct sockaddr_un server; + + struct stream_stream_request_struct *stream_stream_request; + struct stream_stream_response_struct *stream_stream_response; + struct stream_stream_results_struct *stream_stream_result; + + stream_stream_request = + (struct stream_stream_request_struct *)netperf_request.content.test_specific_data; + stream_stream_response = + (struct stream_stream_response_struct *)netperf_response.content.test_specific_data; + stream_stream_result = + (struct stream_stream_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + server.sun_family = AF_UNIX; + + + if ( print_headers ) { + fprintf(where,"STREAM STREAM TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + send_socket = create_unix_socket(AF_UNIX, + SOCK_STREAM); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_stream_stream: stream stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_stream_stream: send_socket obtained...\n"); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 1, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_STREAM_STREAM; + stream_stream_request->send_buf_size = rss_size; + stream_stream_request->recv_buf_size = rsr_size; + stream_stream_request->receive_size = recv_size; + stream_stream_request->recv_alignment = remote_recv_align; + stream_stream_request->recv_offset = remote_recv_offset; + stream_stream_request->measure_cpu = remote_cpu_usage; + stream_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + stream_stream_request->test_length = test_time; + } + else { + stream_stream_request->test_length = test_bytes; + } +#ifdef DIRTY + stream_stream_request->dirty_count = rem_dirty_count; + stream_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + + + if (debug > 1) { + fprintf(where, + "netperf: send_stream_stream: requesting STREAM stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the STREAM tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = stream_stream_response->recv_buf_size; + rss_size = stream_stream_response->send_buf_size; + remote_cpu_usage = stream_stream_response->measure_cpu; + remote_cpu_rate = stream_stream_response->cpu_rate; + strcpy(server.sun_path,stream_stream_response->unix_path); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: send_stream_stream: remote error"); + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + (struct sockaddr *)&server, + sizeof(server)) == INVALID_SOCKET){ + perror("netperf: send_stream_stream: data socket connect failed"); + printf(" path: %s\n",server.sun_path); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + +#ifdef DIRTY + /* initialize the random number generator for putting dirty stuff */ + /* into the send buffer. raj */ + srand((int) getpid()); +#endif + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. at some point, we might want to replace */ + /* the rand() call with something from a table to reduce our call */ + /* overhead during the test, but it is not a high priority item. */ + message_int_ptr = (int *)(send_ring->buffer_ptr); + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + if((len=send(send_socket, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || (errno == EINTR)) { + /* the test was interrupted, must be the end of test */ + break; + } + perror("netperf: data send error"); + printf("len was %d\n",len); + exit(1); + } +#ifdef WANT_INTERVALS + for (interval_count = 0; + interval_count < interval_wate; + interval_count++); +#endif + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + if (close(send_socket) == -1) { + perror("netperf: send_stream_stream: cannot close socket"); + exit(1); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a STREAM stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = ((double) send_size * (double) nummessages) + len; + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = stream_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + stream_stream_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* STREAM statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)stream_stream_result->recv_calls, + stream_stream_result->recv_calls); + } + +} + + +/* This is the server-side routine for the stream stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_stream_stream() +{ + + struct sockaddr_un myaddr_un, peeraddr_un; + SOCKET s_listen,s_data; + int addrlen; + int len; + int receive_calls = 0; + float elapsed_time; + int bytes_received; + + struct ring_elt *recv_ring; + +#ifdef DIRTY + char *message_ptr; + int *message_int_ptr; + int dirty_count; + int clean_count; + int i; +#endif + + struct stream_stream_request_struct *stream_stream_request; + struct stream_stream_response_struct *stream_stream_response; + struct stream_stream_results_struct *stream_stream_results; + + stream_stream_request = + (struct stream_stream_request_struct *)netperf_request.content.test_specific_data; + stream_stream_response = + (struct stream_stream_response_struct *)netperf_response.content.test_specific_data; + stream_stream_results = + (struct stream_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_stream_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_stream_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = STREAM_STREAM_RESPONSE; + + if (debug) { + fprintf(where,"recv_stream_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_stream_stream: requested alignment of %d\n", + stream_stream_request->recv_alignment); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_un, + sizeof(myaddr_un)); + myaddr_un.sun_family = AF_UNIX; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_stream_stream: grabbing a socket...\n"); + fflush(where); + } + + /* create_unix_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = stream_stream_request->send_buf_size; + lsr_size_req = stream_stream_request->recv_buf_size; + + s_listen = create_unix_socket(AF_UNIX, + SOCK_STREAM); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + strcpy(myaddr_un.sun_path,tempnam(path_prefix,"netperf.")); + if (debug) { + fprintf(where,"selected a path of %s\n",myaddr_un.sun_path); + fflush(where); + } + if (bind(s_listen, + (struct sockaddr *)&myaddr_un, + sizeof(myaddr_un)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + fprintf(where,"could not bind to path\n"); + close(s_listen); + send_response(); + + exit(1); + } + + chmod(myaddr_un.sun_path, 0666); + + /* what sort of sizes did we end-up with? */ + if (stream_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = stream_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + stream_stream_request->recv_alignment, + stream_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_stream_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_un); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_un, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_un contains the path */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + strcpy(stream_stream_response->unix_path,myaddr_un.sun_path); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + stream_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (stream_stream_request->measure_cpu) { + stream_stream_response->measure_cpu = 1; + stream_stream_response->cpu_rate = + calibrate_local_cpu(stream_stream_request->cpu_rate); + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + stream_stream_response->send_buf_size = lss_size; + stream_stream_response->recv_buf_size = lsr_size; + stream_stream_response->receive_size = recv_size; + + send_response(); + + addrlen = sizeof(peeraddr_un); + + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_un, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + exit(1); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(stream_stream_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + dirty_count = stream_stream_request->dirty_count; + clean_count = stream_stream_request->clean_count; + message_int_ptr = (int *)recv_ring->buffer_ptr; + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + bytes_received = 0; + + while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) { + if (len == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + bytes_received += len; + receive_calls++; + + /* more to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef DIRTY + message_int_ptr = (int *)(recv_ring->buffer_ptr); + for (i = 0; i < dirty_count; i++) { + *message_int_ptr = rand(); + message_int_ptr++; + } + for (i = 0; i < clean_count; i++) { + dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + } + + /* The loop now exits due to zero bytes received. we will have */ + /* counted one too many messages received, so decrement the */ + /* receive_calls counter by one. raj 7/94 */ + receive_calls--; + + /* perform a shutdown to signal the sender that */ + /* we have received all the data sent. raj 4/93 */ + + if (shutdown(s_data,1) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + cpu_stop(stream_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_stream_stream: got %d bytes\n", + bytes_received); + fprintf(where, + "recv_stream_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + stream_stream_results->bytes_received = bytes_received; + stream_stream_results->elapsed_time = elapsed_time; + stream_stream_results->recv_calls = receive_calls; + + if (stream_stream_request->measure_cpu) { + stream_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug > 1) { + fprintf(where, + "recv_stream_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + unlink(myaddr_un.sun_path); +} + + + /* this routine implements the sending (netperf) side of the STREAM_RR */ + /* test. */ + +void +send_stream_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct sockaddr_un server; + + struct stream_rr_request_struct *stream_rr_request; + struct stream_rr_response_struct *stream_rr_response; + struct stream_rr_results_struct *stream_rr_result; + + stream_rr_request = + (struct stream_rr_request_struct *)netperf_request.content.test_specific_data; + stream_rr_response= + (struct stream_rr_response_struct *)netperf_response.content.test_specific_data; + stream_rr_result = + (struct stream_rr_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + server.sun_family = AF_UNIX; + + + if ( print_headers ) { + fprintf(where,"STREAM REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + + /*set up the data socket */ + send_socket = create_unix_socket(AF_UNIX, + SOCK_STREAM); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_stream_rr: stream stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_stream_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_STREAM_RR; + stream_rr_request->recv_buf_size = rsr_size; + stream_rr_request->send_buf_size = rss_size; + stream_rr_request->recv_alignment= remote_recv_align; + stream_rr_request->recv_offset = remote_recv_offset; + stream_rr_request->send_alignment= remote_send_align; + stream_rr_request->send_offset = remote_send_offset; + stream_rr_request->request_size = req_size; + stream_rr_request->response_size = rsp_size; + stream_rr_request->measure_cpu = remote_cpu_usage; + stream_rr_request->cpu_rate = remote_cpu_rate; + if (test_time) { + stream_rr_request->test_length = test_time; + } + else { + stream_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_stream_rr: requesting STREAM rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the STREAM tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = stream_rr_response->recv_buf_size; + rss_size = stream_rr_response->send_buf_size; + remote_cpu_usage= stream_rr_response->measure_cpu; + remote_cpu_rate = stream_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + strcpy(server.sun_path,stream_rr_response->unix_path); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /*Connect up to the remote port on the data socket */ + if (connect(send_socket, + (struct sockaddr *)&server, + sizeof(server)) == INVALID_SOCKET){ + perror("netperf: data socket connect failed"); + + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (errno == EINTR) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_stream_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_stream_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + + /* At this point we used to call shutdown on the data socket to be */ + /* sure all the data was delivered, but this was not germane in a */ + /* request/response test, and it was causing the tests to "hang" when */ + /* they were being controlled by time. So, I have replaced this */ + /* shutdown call with a call to close that can be found later in the */ + /* procedure. */ + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a STREAM stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a STREAM stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = stream_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + stream_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* STREAM statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt); + } + /* The test is over. Kill the data socket */ + + if (close(send_socket) == -1) { + perror("send_stream_rr: cannot shutdown stream stream socket"); + } + +} + +void +send_dg_stream(char remote_host[]) +{ + /************************************************************************/ + /* */ + /* DG Unidirectional Send Test */ + /* */ + /************************************************************************/ + char *tput_title = + "Socket Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +bytes bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.2f\n\ +%5d %-7.2f %7d %7.2f\n\n"; + + + char *cpu_title = + "Socket Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +bytes bytes secs # # %s/sec %% us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f\n"; + + char *cpu_fmt_1 = + "%5d %5d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ +%5d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; + + int messages_recvd; + float elapsed_time, + local_cpu_utilization, + remote_cpu_utilization; + + float local_service_demand, remote_service_demand; + double local_thruput, remote_thruput; + double bytes_sent; + double bytes_recvd; + + + int len; + struct ring_elt *send_ring; + int failed_sends; + int failed_cows; + int messages_sent; + SOCKET data_socket; + + +#ifdef WANT_INTERVALS + int interval_count; +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + int *message_int_ptr; + int i; +#endif /* DIRTY */ + + struct sockaddr_un server; + + struct dg_stream_request_struct *dg_stream_request; + struct dg_stream_response_struct *dg_stream_response; + struct dg_stream_results_struct *dg_stream_results; + + dg_stream_request = (struct dg_stream_request_struct *)netperf_request.content.test_specific_data; + dg_stream_response = (struct dg_stream_response_struct *)netperf_response.content.test_specific_data; + dg_stream_results = (struct dg_stream_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + server.sun_family = AF_UNIX; + + if ( print_headers ) { + printf("DG UNIDIRECTIONAL SEND TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + printf(cpu_title,format_units()); + else + printf(tput_title,format_units()); + } + + failed_sends = 0; + failed_cows = 0; + messages_sent = 0; + times_up = 0; + + /*set up the data socket */ + data_socket = create_unix_socket(AF_UNIX, + SOCK_DGRAM); + + if (data_socket == INVALID_SOCKET){ + perror("dg_send: data socket"); + exit(1); + } + + /* now, we want to see if we need to set the send_size */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = (lss_size < UNIX_LENGTH_MAX ? lss_size : UNIX_LENGTH_MAX); + } + else { + send_size = 4096; + } + } + + + /* set-up the data buffer with the requested alignment and offset, */ + /* most of the numbers here are just a hack to pick something nice */ + /* and big in an attempt to never try to send a buffer a second time */ + /* before it leaves the node...unless the user set the width */ + /* explicitly. */ + if (send_width == 0) send_width = 32; + + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + + /* At this point, we want to do things like disable DG checksumming */ + /* and measure the cpu rate and all that so we are ready to go */ + /* immediately after the test response message is delivered. */ + + /* if the user supplied a cpu rate, this call will complete rather */ + /* quickly, otherwise, the cpu rate will be retured to us for */ + /* possible display. The Library will keep it's own copy of this data */ + /* for use elsewhere. We will only display it. (Does that make it */ + /* "opaque" to us?) */ + + if (local_cpu_usage) + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + + /* Tell the remote end to set up the data connection. The server */ + /* sends back the port number and alters the socket parameters there. */ + /* Of course this is a datagram service so no connection is actually */ + /* set up, the server just sets up the socket and binds it. */ + + netperf_request.content.request_type = DO_DG_STREAM; + dg_stream_request->recv_buf_size = rsr_size; + dg_stream_request->message_size = send_size; + dg_stream_request->recv_alignment = remote_recv_align; + dg_stream_request->recv_offset = remote_recv_offset; + dg_stream_request->measure_cpu = remote_cpu_usage; + dg_stream_request->cpu_rate = remote_cpu_rate; + dg_stream_request->test_length = test_time; + + send_request(); + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dg_stream: remote data connection done.\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dg_stream: error on remote"); + exit(1); + } + + /* Place the port number returned by the remote into the sockaddr */ + /* structure so our sends can be sent to the correct place. Also get */ + /* some of the returned socket buffer information for user display. */ + + /* make sure that port numbers are in the proper order */ + strcpy(server.sun_path,dg_stream_response->unix_path); + rsr_size = dg_stream_response->recv_buf_size; + rss_size = dg_stream_response->send_buf_size; + remote_cpu_rate = dg_stream_response->cpu_rate; + + /* We "connect" up to the remote post to allow is to use the send */ + /* call instead of the sendto call. Presumeably, this is a little */ + /* simpler, and a little more efficient. I think that it also means */ + /* that we can be informed of certain things, but am not sure yet... */ + + if (connect(data_socket, + (struct sockaddr *)&server, + sizeof(server)) == INVALID_SOCKET){ + perror("send_dg_stream: data socket connect failed"); + exit(1); + } + + /* set up the timer to call us after test_time */ + start_timer(test_time); + + /* Get the start count for the idle counter and the start time */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + interval_count = interval_burst; +#endif + + /* Send datagrams like there was no tomorrow. at somepoint it might */ + /* be nice to set this up so that a quantity of bytes could be sent, */ + /* but we still need some sort of end of test trigger on the receive */ + /* side. that could be a select with a one second timeout, but then */ + /* if there is a test where none of the data arrives for awile and */ + /* then starts again, we would end the test too soon. something to */ + /* think about... */ + while (!times_up) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + message_int_ptr = (int *)(send_ring->buffer_ptr); + for (i = 0; i < loc_dirty_count; i++) { + *message_int_ptr = 4; + message_int_ptr++; + } + for (i = 0; i < loc_clean_count; i++) { + loc_dirty_count = *message_int_ptr; + message_int_ptr++; + } +#endif /* DIRTY */ + + if ((len=send(data_socket, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >= 0) || (errno == EINTR)) + break; + if (errno == ENOBUFS) { + failed_sends++; + continue; + } + perror("dg_send: data send error"); + exit(1); + } + messages_sent++; + + /* now we want to move our pointer to the next position in the */ + /* data buffer... */ + + send_ring = send_ring->next; + + +#ifdef WANT_INTERVALS + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call the sleep routine for some milliseconds, if our */ + /* timer popped while we were in there, we want to */ + /* break out of the loop. */ + if (msec_sleep(interval_wate)) { + break; + } + interval_count = interval_burst; + } + +#endif + + } + + /* This is a timed test, so the remote will be returning to us after */ + /* a time. We should not need to send any "strange" messages to tell */ + /* the remote that the test is completed, unless we decide to add a */ + /* number of messages to the test. */ + + /* the test is over, so get stats and stuff */ + cpu_stop(local_cpu_usage, + &elapsed_time); + + /* Get the statistics from the remote end */ + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_dg_stream: remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_dg_stream: error on remote"); + exit(1); + } + + bytes_sent = send_size * messages_sent; + local_thruput = calc_thruput(bytes_sent); + + messages_recvd = dg_stream_results->messages_recvd; + bytes_recvd = send_size * messages_recvd; + + /* we asume that the remote ran for as long as we did */ + + remote_thruput = calc_thruput(bytes_recvd); + + /* print the results for this socket and message size */ + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) We pass zeros for the local */ + /* cpu utilization and elapsed time to tell the routine to use */ + /* the libraries own values for those. */ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + /* The local calculations could use variables being kept by */ + /* the local netlib routines. The remote calcuations need to */ + /* have a few things passed to them. */ + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"REMOTE CPU usage numbers based on process information only!\n"); + fflush(where); + } + + remote_cpu_utilization = dg_stream_results->cpu_util; + remote_service_demand = calc_service_demand(bytes_recvd, + 0.0, + remote_cpu_utilization, + dg_stream_results->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + messages_sent, + failed_sends, + local_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rsr_size, + elapsed_time, + messages_recvd, + remote_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_thruput); + break; + case 1: + fprintf(where, + tput_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + messages_sent, + failed_sends, + local_thruput, + rsr_size, /* remote recvbuf size */ + elapsed_time, + messages_recvd, + remote_thruput + ); + break; + } + } +} + + + /* this routine implements the receive side (netserver) of the */ + /* DG_STREAM performance test. */ + +void +recv_dg_stream() +{ + struct ring_elt *recv_ring; + + struct sockaddr_un myaddr_un; + SOCKET s_data; + int len = 0; + int bytes_received = 0; + float elapsed_time; + + int message_size; + int messages_recvd = 0; + + struct dg_stream_request_struct *dg_stream_request; + struct dg_stream_response_struct *dg_stream_response; + struct dg_stream_results_struct *dg_stream_results; + + dg_stream_request = + (struct dg_stream_request_struct *)netperf_request.content.test_specific_data; + dg_stream_response = + (struct dg_stream_response_struct *)netperf_response.content.test_specific_data; + dg_stream_results = + (struct dg_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dg_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug > 1) { + fprintf(where,"recv_dg_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DG_STREAM_RESPONSE; + + if (debug > 2) { + fprintf(where,"recv_dg_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_dg_stream: requested alignment of %d\n", + dg_stream_request->recv_alignment); + fflush(where); + } + + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + dg_stream_request->message_size, + dg_stream_request->recv_alignment, + dg_stream_request->recv_offset); + + if (debug > 1) { + fprintf(where,"recv_dg_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_un, + sizeof(myaddr_un)); + myaddr_un.sun_family = AF_UNIX; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug > 1) { + fprintf(where,"recv_dg_stream: grabbing a socket...\n"); + fflush(where); + } + + /* create_unix_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lsr_size = dg_stream_request->recv_buf_size; + + s_data = create_unix_socket(AF_UNIX, + SOCK_DGRAM); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + strcpy(myaddr_un.sun_path,tempnam(path_prefix,"netperf.")); + if (bind(s_data, + (struct sockaddr *)&myaddr_un, + sizeof(myaddr_un)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + chmod(myaddr_un.sun_path, 0666); + + dg_stream_response->test_length = dg_stream_request->test_length; + + /* Now myaddr_un contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + strcpy(dg_stream_response->unix_path,myaddr_un.sun_path); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + dg_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (dg_stream_request->measure_cpu) { + /* We will pass the rate into the calibration routine. If the */ + /* user did not specify one, it will be 0.0, and we will do a */ + /* "real" calibration. Otherwise, all it will really do is */ + /* store it away... */ + dg_stream_response->measure_cpu = 1; + dg_stream_response->cpu_rate = + calibrate_local_cpu(dg_stream_request->cpu_rate); + } + + message_size = dg_stream_request->message_size; + test_time = dg_stream_request->test_length; + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + dg_stream_response->send_buf_size = lss_size; + dg_stream_response->recv_buf_size = lsr_size; + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dg_stream_request->measure_cpu); + + /* The loop will exit when the timer pops, or if we happen to recv a */ + /* message of less than send_size bytes... */ + + times_up = 0; + start_timer(test_time + PAD_TIME); + + if (debug) { + fprintf(where,"recv_dg_stream: about to enter inner sanctum.\n"); + fflush(where); + } + + while (!times_up) { + if ((len = recv(s_data, + recv_ring->buffer_ptr, + message_size, + 0)) != message_size) { + if ((len == SOCKET_ERROR) && (errno != EINTR)) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + break; + } + messages_recvd++; + recv_ring = recv_ring->next; + } + + if (debug) { + fprintf(where,"recv_dg_stream: got %d messages.\n",messages_recvd); + fflush(where); + } + + + /* The loop now exits due timer or < send_size bytes received. */ + + cpu_stop(dg_stream_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended on a timer, subtract the PAD_TIME */ + elapsed_time -= (float)PAD_TIME; + } + else { + stop_timer(); + } + + if (debug) { + fprintf(where,"recv_dg_stream: test ended in %f seconds.\n",elapsed_time); + fflush(where); + } + + + /* We will count the "off" message that got us out of the loop */ + bytes_received = (messages_recvd * message_size) + len; + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dg_stream: got %d bytes\n", + bytes_received); + fflush(where); + } + + netperf_response.content.response_type = DG_STREAM_RESULTS; + dg_stream_results->bytes_received = bytes_received; + dg_stream_results->messages_recvd = messages_recvd; + dg_stream_results->elapsed_time = elapsed_time; + if (dg_stream_request->measure_cpu) { + dg_stream_results->cpu_util = calc_cpu_util(elapsed_time); + } + else { + dg_stream_results->cpu_util = -1.0; + } + + if (debug > 1) { + fprintf(where, + "recv_dg_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +void +send_dg_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + float elapsed_time; + + /* we add MAXALIGNMENT and MAXOFFSET to insure that there is enough */ + /* space for a maximally aligned, maximally sized message. At some */ + /* point, we may want to actually make this even larger and cycle */ + /* through the thing one piece at a time.*/ + + int len; + char *send_message_ptr; + char *recv_message_ptr; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + int bytes_xferd; + + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + +#ifdef WANT_INTERVALS + /* timing stuff */ +#define MAX_KEPT_TIMES 1024 + int time_index = 0; + int unused_buckets; + int kept_times[MAX_KEPT_TIMES]; + int sleep_usecs; + unsigned int total_times=0; + struct timezone dummy_zone; + struct timeval send_time; + struct timeval recv_time; + struct timeval sleep_timeval; +#endif + + struct sockaddr_un server, myaddr_un; + + struct dg_rr_request_struct *dg_rr_request; + struct dg_rr_response_struct *dg_rr_response; + struct dg_rr_results_struct *dg_rr_result; + + dg_rr_request = + (struct dg_rr_request_struct *)netperf_request.content.test_specific_data; + dg_rr_response= + (struct dg_rr_response_struct *)netperf_response.content.test_specific_data; + dg_rr_result = + (struct dg_rr_results_struct *)netperf_response.content.test_specific_data; + + /* we want to zero out the times, so we can detect unused entries. */ +#ifdef WANT_INTERVALS + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + kept_times[time_index] = 0; + time_index += 1; + } + time_index = 0; +#endif + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + server.sun_family = AF_UNIX; + + bzero((char *)&myaddr_un, + sizeof(myaddr_un)); + myaddr_un.sun_family = AF_UNIX; + + strcpy(myaddr_un.sun_path,tempnam(path_prefix,"netperf.")); + + if ( print_headers ) { + fprintf(where,"DG REQUEST/RESPONSE TEST\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + + /* set-up the data buffer with the requested alignment and offset */ + temp_message_ptr = (char *)malloc(DATABUFFERLEN); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + send_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_send_align - 1) & + ~((long) local_send_align - 1)); + send_message_ptr = send_message_ptr + local_send_offset; + temp_message_ptr = (char *)malloc(DATABUFFERLEN); + if (temp_message_ptr == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + recv_message_ptr = (char *)(( (long)temp_message_ptr + + (long) local_recv_align - 1) & + ~((long) local_recv_align - 1)); + recv_message_ptr = recv_message_ptr + local_recv_offset; + + /*set up the data socket */ + send_socket = create_unix_socket(AF_UNIX, + SOCK_DGRAM); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_dg_rr: dg rr data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_dg_rr: send_socket obtained...\n"); + } + + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. If */ + /* there is no idle counter in the kernel idle loop, the */ + /* local_cpu_rate will be set to -1. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_DG_RR; + dg_rr_request->recv_buf_size = rsr_size; + dg_rr_request->send_buf_size = rss_size; + dg_rr_request->recv_alignment = remote_recv_align; + dg_rr_request->recv_offset = remote_recv_offset; + dg_rr_request->send_alignment = remote_send_align; + dg_rr_request->send_offset = remote_send_offset; + dg_rr_request->request_size = req_size; + dg_rr_request->response_size = rsp_size; + dg_rr_request->measure_cpu = remote_cpu_usage; + dg_rr_request->cpu_rate = remote_cpu_rate; + if (test_time) { + dg_rr_request->test_length = test_time; + } + else { + dg_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_dg_rr: requesting DG request/response test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the DG tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = dg_rr_response->recv_buf_size; + rss_size = dg_rr_response->send_buf_size; + remote_cpu_usage= dg_rr_response->measure_cpu; + remote_cpu_rate = dg_rr_response->cpu_rate; + /* port numbers in proper order */ + strcpy(server.sun_path,dg_rr_response->unix_path); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* Connect up to the remote port on the data socket. This will set */ + /* the default destination address on this socket. we need to bind */ + /* out socket so that the remote gets something from a recvfrom */ + if (bind(send_socket, + (struct sockaddr *)&myaddr_un, + sizeof(myaddr_un)) == SOCKET_ERROR) { + perror("netperf: send_dg_rr"); + unlink(myaddr_un.sun_path); + close(send_socket); + exit(1); + } + + if (connect(send_socket, + (struct sockaddr *)&server, + sizeof(server)) == INVALID_SOCKET ) { + perror("netperf: data socket connect failed"); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ +#ifdef WANT_INTERVALS + gettimeofday(&send_time,&dummy_zone); +#endif + if((len=send(send_socket, + send_message_ptr, + req_size, + 0)) != req_size) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + perror("send_dg_rr: data send error"); + exit(1); + } + + /* receive the response. with DG we will get it all, or nothing */ + + if((rsp_bytes_recvd=recv(send_socket, + recv_message_ptr, + rsp_size, + 0)) != rsp_size) { + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + perror("send_dg_rr: data recv error"); + exit(1); + } +#ifdef WANT_INTERVALS + gettimeofday(&recv_time,&dummy_zone); + + /* now we do some arithmatic on the two timevals */ + if (recv_time.tv_usec < send_time.tv_usec) { + /* we wrapped around a second */ + recv_time.tv_usec += 1000000; + recv_time.tv_sec -= 1; + } + + /* and store it away */ + kept_times[time_index] = (recv_time.tv_sec - send_time.tv_sec) * 1000000; + kept_times[time_index] += (recv_time.tv_usec - send_time.tv_usec); + + /* at this point, we may wish to sleep for some period of */ + /* time, so we see how long that last transaction just took, */ + /* and sleep for the difference of that and the interval. We */ + /* will not sleep if the time would be less than a */ + /* millisecond. */ + if (interval_usecs > 0) { + sleep_usecs = interval_usecs - kept_times[time_index]; + if (sleep_usecs > 1000) { + /* we sleep */ + sleep_timeval.tv_sec = sleep_usecs / 1000000; + sleep_timeval.tv_usec = sleep_usecs % 1000000; + select(0, + 0, + 0, + 0, + &sleep_timeval); + } + } + + /* now up the time index */ + time_index = (time_index +1)%MAX_KEPT_TIMES; +#endif + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where,"Transaction %d completed\n",nummessages); + fflush(where); + } + + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. Of course, since this was a request/response test, there */ + /* should be no data outstanding on the socket ;-) */ + + if (shutdown(send_socket,1) == SOCKET_ERROR) { + perror("netperf: cannot shutdown dg stream socket"); + + exit(1); + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a DG stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = dg_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + dg_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + case 2: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* DG statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_INTERVALS + kept_times[MAX_KEPT_TIMES] = 0; + time_index = 0; + while (time_index < MAX_KEPT_TIMES) { + if (kept_times[time_index] > 0) { + total_times += kept_times[time_index]; + } + else + unused_buckets++; + time_index += 1; + } + total_times /= (MAX_KEPT_TIMES-unused_buckets); + fprintf(where, + "Average response time %d usecs\n", + total_times); +#endif + } + unlink(myaddr_un.sun_path); +} + + /* this routine implements the receive side (netserver) of a DG_RR */ + /* test. */ +void +recv_dg_rr() +{ + + struct ring_elt *recv_ring; + struct ring_elt *send_ring; + + struct sockaddr_un myaddr_un, + peeraddr_un; + SOCKET s_data; + int addrlen; + int trans_received = 0; + int trans_remaining; + float elapsed_time; + + struct dg_rr_request_struct *dg_rr_request; + struct dg_rr_response_struct *dg_rr_response; + struct dg_rr_results_struct *dg_rr_results; + + dg_rr_request = + (struct dg_rr_request_struct *)netperf_request.content.test_specific_data; + dg_rr_response = + (struct dg_rr_response_struct *)netperf_response.content.test_specific_data; + dg_rr_results = + (struct dg_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_dg_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_dg_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = DG_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_dg_rr: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where,"recv_dg_rr: requested recv alignment of %d offset %d\n", + dg_rr_request->recv_alignment, + dg_rr_request->recv_offset); + fprintf(where,"recv_dg_rr: requested send alignment of %d offset %d\n", + dg_rr_request->send_alignment, + dg_rr_request->send_offset); + fflush(where); + } + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + dg_rr_request->request_size, + dg_rr_request->recv_alignment, + dg_rr_request->recv_offset); + + send_ring = allocate_buffer_ring(send_width, + dg_rr_request->response_size, + dg_rr_request->send_alignment, + dg_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_dg_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_un, + sizeof(myaddr_un)); + myaddr_un.sun_family = AF_UNIX; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_dg_rr: grabbing a socket...\n"); + fflush(where); + } + + + /* create_unix_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = dg_rr_request->send_buf_size; + lsr_size_req = dg_rr_request->recv_buf_size; + + s_data = create_unix_socket(AF_UNIX, + SOCK_DGRAM); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + strcpy(myaddr_un.sun_path,tempnam(path_prefix,"netperf.")); + if (bind(s_data, + (struct sockaddr *)&myaddr_un, + sizeof(myaddr_un)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + unlink(myaddr_un.sun_path); + close(s_data); + send_response(); + + exit(1); + } + + /* Now myaddr_un contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + strcpy(dg_rr_response->unix_path,myaddr_un.sun_path); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + dg_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (dg_rr_request->measure_cpu) { + dg_rr_response->measure_cpu = 1; + dg_rr_response->cpu_rate = calibrate_local_cpu(dg_rr_request->cpu_rate); + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + dg_rr_response->send_buf_size = lss_size; + dg_rr_response->recv_buf_size = lsr_size; + + send_response(); + + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(dg_rr_request->measure_cpu); + + if (dg_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(dg_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = dg_rr_request->test_length * -1; + } + + addrlen = sizeof(peeraddr_un); + bzero((char *)&peeraddr_un, addrlen); + + while ((!times_up) || (trans_remaining > 0)) { + + /* receive the request from the other side */ + fprintf(where,"socket %d ptr %p size %d\n", + s_data, + recv_ring->buffer_ptr, + dg_rr_request->request_size); + fflush(where); + if (recvfrom(s_data, + recv_ring->buffer_ptr, + dg_rr_request->request_size, + 0, + (struct sockaddr *)&peeraddr_un, + &addrlen) != dg_rr_request->request_size) { + if (errno == EINTR) { + /* we must have hit the end of test time. */ + break; + } + netperf_response.content.serv_errno = errno; + fprintf(where,"error on recvfrom errno %d\n",errno); + fflush(where); + send_response(); + unlink(myaddr_un.sun_path); + exit(1); + } + recv_ring = recv_ring->next; + + /* Now, send the response to the remote */ + if (sendto(s_data, + send_ring->buffer_ptr, + dg_rr_request->response_size, + 0, + (struct sockaddr *)&peeraddr_un, + addrlen) != dg_rr_request->response_size) { + if (errno == EINTR) { + /* we have hit end of test time. */ + break; + } + netperf_response.content.serv_errno = errno; + fprintf(where,"error on recvfrom errno %d\n",errno); + fflush(where); + unlink(myaddr_un.sun_path); + send_response(); + exit(1); + } + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_dg_rr: Transaction %d complete.\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(dg_rr_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_dg_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + dg_rr_results->bytes_received = (trans_received * + (dg_rr_request->request_size + + dg_rr_request->response_size)); + dg_rr_results->trans_received = trans_received; + dg_rr_results->elapsed_time = elapsed_time; + if (dg_rr_request->measure_cpu) { + dg_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_dg_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + unlink(myaddr_un.sun_path); + +} + /* this routine implements the receive (netserver) side of a STREAM_RR */ + /* test */ + +void +recv_stream_rr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct sockaddr_un myaddr_un, + peeraddr_un; + SOCKET s_listen,s_data; + int addrlen; + char *temp_message_ptr; + int trans_received = 0; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct stream_rr_request_struct *stream_rr_request; + struct stream_rr_response_struct *stream_rr_response; + struct stream_rr_results_struct *stream_rr_results; + + stream_rr_request = + (struct stream_rr_request_struct *)netperf_request.content.test_specific_data; + stream_rr_response = + (struct stream_rr_response_struct *)netperf_response.content.test_specific_data; + stream_rr_results = + (struct stream_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_stream_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_stream_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = STREAM_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_stream_rr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_stream_rr: requested recv alignment of %d offset %d\n", + stream_rr_request->recv_alignment, + stream_rr_request->recv_offset); + fprintf(where,"recv_stream_rr: requested send alignment of %d offset %d\n", + stream_rr_request->send_alignment, + stream_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + stream_rr_request->response_size, + stream_rr_request->send_alignment, + stream_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + stream_rr_request->request_size, + stream_rr_request->recv_alignment, + stream_rr_request->recv_offset); + + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_un, + sizeof(myaddr_un)); + myaddr_un.sun_family = AF_UNIX; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_stream_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_unix_socket expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size_req = stream_rr_request->send_buf_size; + lsr_size_req = stream_rr_request->recv_buf_size; + + s_listen = create_unix_socket(AF_UNIX, + SOCK_STREAM); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + strcpy(myaddr_un.sun_path,tempnam(path_prefix,"netperf.")); + if (bind(s_listen, + (struct sockaddr *)&myaddr_un, + sizeof(myaddr_un)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + unlink(myaddr_un.sun_path); + close(s_listen); + send_response(); + + exit(1); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + + exit(1); + } + + /* Now myaddr_un contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + strcpy(stream_rr_response->unix_path,myaddr_un.sun_path); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + stream_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (stream_rr_request->measure_cpu) { + stream_rr_response->measure_cpu = 1; + stream_rr_response->cpu_rate = calibrate_local_cpu(stream_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + stream_rr_response->send_buf_size = lss_size; + stream_rr_response->recv_buf_size = lsr_size; + + send_response(); + + addrlen = sizeof(peeraddr_un); + + if ((s_data = accept(s_listen, + (struct sockaddr *)&peeraddr_un, + &addrlen)) == INVALID_SOCKET) { + /* Let's just punt. The remote will be given some information */ + close(s_listen); + + exit(1); + } + + if (debug) { + fprintf(where,"recv_stream_rr: accept completes on the data connection.\n"); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(stream_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (stream_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(stream_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = stream_rr_request->test_length * -1; + } + + while ((!times_up) || (trans_remaining > 0)) { + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = stream_rr_request->request_size; + + /* receive the request from the other side */ + if (debug) { + fprintf(where,"about to receive for trans %d\n",trans_received); + fprintf(where,"temp_message_ptr is %p\n",temp_message_ptr); + fflush(where); + } + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + if (debug) { + fprintf(where,"just received for trans %d\n",trans_received); + fflush(where); + } + } + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + fprintf(where,"yo5\n"); + fflush(where); + break; + } + + /* Now, send the response to the remote */ + if (debug) { + fprintf(where,"about to send for trans %d\n",trans_received); + fflush(where); + } + if((bytes_sent=send(s_data, + send_ring->buffer_ptr, + stream_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 997; + send_response(); + exit(1); + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_stream_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(stream_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_stream_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + stream_rr_results->bytes_received = (trans_received * + (stream_rr_request->request_size + + stream_rr_request->response_size)); + stream_rr_results->trans_received = trans_received; + stream_rr_results->elapsed_time = elapsed_time; + if (stream_rr_request->measure_cpu) { + stream_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_stream_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + unlink(myaddr_un.sun_path); +} + +void +print_unix_usage() +{ + + fwrite(unix_usage, sizeof(char), strlen(unix_usage), stdout); + exit(1); + +} +void +scan_unix_args(int argc, char *argv[]) +{ +#define UNIX_ARGS "hm:M:p:r:s:S:" + extern char *optarg; /* pointer to option string */ + + int c; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + init_test_vars(); + + if (no_control) { + fprintf(where, + "The UNIX tests do not know how to run with no control connection\n"); + exit(-1); + } + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form "first," (see the routine break_args.. */ + + while ((c= getopt(argc, argv, UNIX_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_unix_usage(); + exit(1); + case 'p': + /* set the path prefix (directory) that should be used for the */ + /* pipes. at some point, there should be some error checking. */ + strcpy(path_prefix,optarg); + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size_req = atoi(arg1); + if (arg2[0]) + lsr_size_req = atoi(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size = atoi(arg1); + if (arg2[0]) + rsr_size = atoi(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = atoi(arg1); + if (arg2[0]) + rsp_size = atoi(arg2); + break; + case 'm': + /* set the send size */ + send_size = atoi(optarg); + break; + case 'M': + /* set the recv size */ + recv_size = atoi(optarg); + break; + }; + } +} +#endif /* WANT_UNIX */ diff --git a/nettest_unix.h b/nettest_unix.h new file mode 100644 index 0000000..8eb393b --- /dev/null +++ b/nettest_unix.h @@ -0,0 +1,198 @@ +/* + Copyright (C) 1993-2004 Hewlett-Packard Company +*/ + + /* This file contains the test-specific definitions for netperf's */ + /* DLPI tests */ + +struct stream_stream_request_struct { + int recv_buf_size; + int send_buf_size; + int receive_size; /* how many bytes do we want to */ + /* receive at one time? */ + int recv_alignment; /* what is the alignment of the */ + /* receive buffer? */ + int recv_offset; /* and at what offset from that */ + /* alignment? */ + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int measure_cpu; /* does the client want server cpu */ + /* utilization measured? */ + float cpu_rate; /* do we know how fast the cpu is */ + /* already? */ + int test_length; /* how long is the test? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct stream_stream_response_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int receive_size; + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct stream_stream_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int num_cpus; +}; + +struct stream_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct stream_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + float cpu_rate; /* could we measure */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path to the dlpi device */ +}; + +struct stream_rr_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int num_cpus; +}; + +struct dg_stream_request_struct { + int recv_buf_size; + int message_size; + int recv_alignment; + int recv_offset; + int measure_cpu; + float cpu_rate; + int test_length; + int so_rcvavoid; /* do we want the remote to avoid receive copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct dg_stream_response_struct { + int recv_buf_size; + int send_buf_size; + int measure_cpu; + int test_length; + float cpu_rate; + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct dg_stream_results_struct { + int messages_recvd; + int bytes_received; + float elapsed_time; + float cpu_util; + int num_cpus; +}; + + +struct dg_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct dg_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ + int path_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char unix_path[32]; /* the path */ +}; + +struct dg_rr_results_struct { + int bytes_received; /* ignored initially */ + int recv_calls; /* ignored initially */ + int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int num_cpus; +}; + +extern void scan_unix_args(int argc, char *argv[]); + +extern void send_stream_stream(char remote_host[]); +extern void send_stream_rr(char remote_host[]); +extern void send_dg_stream(char remote_host[]); +extern void send_dg_rr(char remote_host[]); + +extern void recv_stream_stream(); +extern void recv_stream_rr(); +extern void recv_dg_stream(); +extern void recv_dg_rr(); diff --git a/nettest_xti.c b/nettest_xti.c new file mode 100644 index 0000000..9d27f25 --- /dev/null +++ b/nettest_xti.c @@ -0,0 +1,6026 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WANT_XTI +#ifndef lint +char nettest_xti_id[]="\ +@(#)nettest_xti.c (c) Copyright 1995-2007 Hewlett-Packard Co. Version 2.4.3"; +#else +#define DIRTY +#define WANT_HISTOGRAM +#define WANT_INTERVALS +#endif /* lint */ +/****************************************************************/ +/* */ +/* nettest_xti.c */ +/* */ +/* the XTI args parsing routine... */ +/* */ +/* scan_xti_args() */ +/* */ +/* the actual test routines... */ +/* */ +/* send_xti_tcp_stream() perform a tcp stream test */ +/* recv_xti_tcp_stream() */ +/* send_xti_tcp_rr() perform a tcp request/response */ +/* recv_xti_tcp_rr() */ +/* send_xti_tcp_conn_rr() an RR test including connect */ +/* recv_xti_tcp_conn_rr() */ +/* send_xti_udp_stream() perform a udp stream test */ +/* recv_xti_udp_stream() */ +/* send_xti_udp_rr() perform a udp request/response */ +/* recv_xti_udp_rr() */ +/* */ +/****************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <fcntl.h> +#ifndef WIN32 +#include <sys/ipc.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#include <signal.h> +#else /* WIN32 */ +#include <process.h> +#include <winsock2.h> +#include <windows.h> +#endif /* WIN32 */ +#include <stdio.h> +#include <time.h> +#include <malloc.h> + /* xti.h should be included *after* in.h because there are name */ + /* conflicts!( Silly standards people... raj 2/95 fortuenately, the */ + /* confilcts are on IP_TOP and IP_TTL, whcih netperf does not yet use */ +#include <xti.h> + +#include "netlib.h" +#include "netsh.h" +#include "nettest_xti.h" + +#ifdef WANT_HISTOGRAM +#ifdef __sgi +#include <sys/time.h> +#endif /* __sgi */ +#include "hist.h" +#endif /* WANT_HISTOGRAM */ + + + + /* these variables are specific to the XTI sockets tests. declare */ + /* them static to make them global only to this file. */ + +static int + rss_size, /* remote socket send buffer size */ + rsr_size, /* remote socket recv buffer size */ + lss_size, /* local socket send buffer size */ + lsr_size, /* local socket recv buffer size */ + req_size = 1, /* request size */ + rsp_size = 1, /* response size */ + send_size, /* how big are individual sends */ + recv_size; /* how big are individual receives */ + +static int confidence_iteration; +static char local_cpu_method; +static char remote_cpu_method; + + /* different options for the xti */ + +static int + loc_nodelay, /* don't/do use NODELAY locally */ + rem_nodelay, /* don't/do use NODELAY remotely */ + loc_sndavoid, /* avoid send copies locally */ + loc_rcvavoid, /* avoid recv copies locally */ + rem_sndavoid, /* avoid send copies remotely */ + rem_rcvavoid; /* avoid recv_copies remotely */ + +static struct t_info info_struct; + +#ifdef WANT_HISTOGRAM +#ifdef HAVE_GETHRTIME +hrtime_t time_one; +hrtime_t time_two; +#else +static struct timeval time_one; +static struct timeval time_two; +#endif /* HAVE_GETHRTIME */ +static HIST time_hist; +#endif /* WANT_HISTOGRAM */ + +static char loc_xti_device[32] = "/dev/tcp"; +static char rem_xti_device[32] = "/dev/tcp"; + +static int xti_flags = 0; + +char xti_usage[] = "\n\ +Usage: netperf [global options] -- [test options] \n\ +\n\ +TCP/UDP XTI API Test Options:\n\ + -D [L][,R] Set XTI_TCP_NODELAY locally and/or remotely (XTI_TCP_*)\n\ + -h Display this text\n\ + -m bytes Set the send size (XTI_TCP_STREAM, XTI_UDP_STREAM)\n\ + -M bytes Set the recv size (XTI_TCP_STREAM, XTI_UDP_STREAM)\n\ + -r bytes Set request size (XTI_TCP_RR, XTI_UDP_RR)\n\ + -R bytes Set response size (XTI_TCP_RR, XTI_UDP_RR)\n\ + -s send[,recv] Set local socket send/recv buffer sizes\n\ + -S send[,recv] Set remote socket send/recv buffer sizes\n\ + -X dev[,dev] Set the local/remote XTI device file name\n\ +\n\ +For those options taking two parms, at least one must be specified;\n\ +specifying one value without a comma will set both parms to that\n\ +value, specifying a value with a leading comma will set just the second\n\ +parm, a value with a trailing comma will set just the first. To set\n\ +each parm to unique values, specify both and separate them with a\n\ +comma.\n"; + + + /* This routine is intended to retrieve interesting aspects of tcp */ + /* for the data connection. at first, it attempts to retrieve the */ + /* maximum segment size. later, it might be modified to retrieve */ + /* other information, but it must be information that can be */ + /* retrieved quickly as it is called during the timing of the test. */ + /* for that reason, a second routine may be created that can be */ + /* called outside of the timing loop */ +void +get_xti_info(socket, info_struct) + int socket; + struct t_info *info_struct; +{ + +} + + + /* This routine will create a data (listen) socket with the apropriate */ + /* options set and return it to the caller. this replaces all the */ + /* duplicate code in each of the test routines and should help make */ + /* things a little easier to understand. since this routine can be */ + /* called by either the netperf or netserver programs, all output */ + /* should be directed towards "where." family is generally AF_INET, */ + /* and type will be either SOCK_STREAM or SOCK_DGRAM */ +SOCKET +create_xti_endpoint(char *name) +{ + + SOCKET temp_socket; + + struct t_optmgmt *opt_req; /* we request an option */ + struct t_optmgmt *opt_ret; /* it tells us what we got */ + + /* we use this to pass-in BSD-like socket options through t_optmgmt. */ + /* it ends up being about as clear as mud. raj 2/95 */ + struct sock_option { + struct t_opthdr myopthdr; + long value; + } *sock_option; + + if (debug) { + fprintf(where,"create_xti_endpoint: attempting to open %s\n", + name); + fflush(where); + } + + /*set up the data socket */ + temp_socket = t_open(name,O_RDWR,NULL); + + if (temp_socket == INVALID_SOCKET){ + fprintf(where, + "netperf: create_xti_endpoint: t_open %s: errno %d t_errno %d\n", + name, + errno, + t_errno); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where,"create_xti_endpoint: socket %d obtained...\n",temp_socket); + fflush(where); + } + + /* allocate what we need for option mgmt */ + if ((opt_req = (struct t_optmgmt *)t_alloc(temp_socket,T_OPTMGMT,T_ALL)) == + NULL) { + fprintf(where, + "netperf: create_xti_endpoint: t_alloc: opt_req errno %d\n", + errno); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where, + "create_xti_endpoint: opt_req->opt.buf %x maxlen %d len %d\n", + opt_req->opt.buf, + opt_req->opt.maxlen, + opt_req->opt.len); + + fflush(where); + } + + if ((opt_ret = (struct t_optmgmt *) t_alloc(temp_socket,T_OPTMGMT,T_ALL)) == + NULL) { + fprintf(where, + "netperf: create_xti_endpoint: t_alloc: opt_ret errno %d\n", + errno); + fflush(where); + exit(1); + } + + if (debug) { + fprintf(where, + "create_xti_endpoint: opt_ret->opt.buf %x maxlen %d len %d\n", + opt_ret->opt.buf, + opt_ret->opt.maxlen, + opt_ret->opt.len); + fflush(where); + } + + /* Modify the local socket size. The reason we alter the send buffer */ + /* size here rather than when the connection is made is to take care */ + /* of decreases in buffer size. Decreasing the window size after */ + /* connection establishment is a TCP no-no. Also, by setting the */ + /* buffer (window) size before the connection is established, we can */ + /* control the TCP MSS (segment size). The MSS is never more that 1/2 */ + /* the minimum receive buffer size at each half of the connection. */ + /* This is why we are altering the receive buffer size on the sending */ + /* size of a unidirectional transfer. If the user has not requested */ + /* that the socket buffers be altered, we will try to find-out what */ + /* their values are. If we cannot touch the socket buffer in any way, */ + /* we will set the values to -1 to indicate that. */ + +#ifdef XTI_SNDBUF + if (lss_size > 0) { + /* we want to "negotiate" the option */ + opt_req->flags = T_NEGOTIATE; + } + else { + /* we want to accept the default, and know what it is. I assume */ + /* that when nothing has been changed, that T_CURRENT will return */ + /* the same as T_DEFAULT raj 3/95 */ + opt_req->flags = T_CURRENT; + } + + /* the first part is for the netbuf that holds the option we want */ + /* to negotiate or check */ + /* the buffer of the netbuf points at the socket options structure */ + + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_req->opt.buf; + + /* and next, set the fields in the sock_option structure */ + sock_option->myopthdr.level = XTI_GENERIC; + sock_option->myopthdr.name = XTI_SNDBUF; + sock_option->myopthdr.len = sizeof(struct t_opthdr) + sizeof(long); + sock_option->value = lss_size; + + opt_req->opt.len = sizeof(struct t_opthdr) + sizeof(long); + + /* now, set-up the stuff to return the value in the end */ + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_ret->opt.buf; + + /* finally, call t_optmgmt. clear as mud. */ + if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) { + fprintf(where, + "netperf: create_xti_endpoint: XTI_SNDBUF option: t_errno %d\n", + t_errno); + fflush(where); + exit(1); + } + + if (sock_option->myopthdr.status == T_SUCCESS) { + lss_size = sock_option->value; + } + else { + fprintf(where,"create_xti_endpoint: XTI_SNDBUF option status 0x%.4x", + sock_option->myopthdr.status); + fprintf(where," value %d\n", + sock_option->value); + fflush(where); + lss_size = -1; + } + + if (lsr_size > 0) { + /* we want to "negotiate" the option */ + opt_req->flags = T_NEGOTIATE; + } + else { + /* we want to accept the default, and know what it is. I assume */ + /* that when nothing has been changed, that T_CURRENT will return */ + /* the same as T_DEFAULT raj 3/95 */ + opt_req->flags = T_CURRENT; + } + + /* the first part is for the netbuf that holds the option we want */ + /* to negotiate or check */ + /* the buffer of the netbuf points at the socket options structure */ + + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_req->opt.buf; + + /* and next, set the fields in the sock_option structure */ + sock_option->myopthdr.level = XTI_GENERIC; + sock_option->myopthdr.name = XTI_RCVBUF; + sock_option->myopthdr.len = sizeof(struct t_opthdr) + sizeof(long); + sock_option->value = lsr_size; + + opt_req->opt.len = sizeof(struct t_opthdr) + sizeof(long); + + /* now, set-up the stuff to return the value in the end */ + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_ret->opt.buf; + + /* finally, call t_optmgmt. clear as mud. */ + if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) { + fprintf(where, + "netperf: create_xti_endpoint: XTI_RCVBUF option: t_errno %d\n", + t_errno); + fflush(where); + exit(1); + } + lsr_size = sock_option->value; + + /* this needs code */ + + if (debug) { + fprintf(where,"netperf: create_xti_endpoint: socket sizes determined...\n"); + fprintf(where," send: %d recv: %d\n", + lss_size,lsr_size); + fflush(where); + } + +#else /* XTI_SNDBUF */ + + lss_size = -1; + lsr_size = -1; + +#endif /* XTI_SNDBUF */ + + /* now, we may wish to enable the copy avoidance features on the */ + /* local system. of course, this may not be possible... */ + + if (loc_rcvavoid) { + fprintf(where, + "netperf: create_xti_endpoint: Could not enable receive copy avoidance"); + fflush(where); + loc_rcvavoid = 0; + } + + if (loc_sndavoid) { + fprintf(where, + "netperf: create_xti_endpoint: Could not enable send copy avoidance"); + fflush(where); + loc_sndavoid = 0; + } + + /* Now, we will see about setting the TCP_NODELAY flag on the local */ + /* socket. We will only do this for those systems that actually */ + /* support the option. If it fails, note the fact, but keep going. */ + /* If the user tries to enable TCP_NODELAY on a UDP socket, this */ + /* will cause an error to be displayed */ + +#ifdef TCP_NODELAY + if ((strcmp(test_name,"XTI_TCP_STREAM") == 0) || + (strcmp(test_name,"XTI_TCP_RR") == 0) || + (strcmp(test_name,"XTI_TCP_CRR") == 0)) { + if (loc_nodelay) { + /* we want to "negotiate" the option */ + opt_req->flags = T_NEGOTIATE; + } + else { + /* we want to accept the default, and know what it is. I assume */ + /* that when nothing has been changed, that T_CURRENT will return */ + /* the same as T_DEFAULT raj 3/95 */ + opt_req->flags = T_CURRENT; + } + + /* the first part is for the netbuf that holds the option we want */ + /* to negotiate or check the buffer of the netbuf points at the */ + /* socket options structure */ + + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_req->opt.buf; + + /* and next, set the fields in the sock_option structure */ + sock_option->myopthdr.level = INET_TCP; + sock_option->myopthdr.name = TCP_NODELAY; + sock_option->myopthdr.len = sizeof(struct t_opthdr) + sizeof(long); + sock_option->value = T_YES; + + opt_req->opt.len = sizeof(struct t_opthdr) + sizeof(long); + + /* now, set-up the stuff to return the value in the end */ + /* we assume that the t_alloc call allocated a buffer that started */ + /* on a proper alignment */ + sock_option = (struct sock_option *)opt_ret->opt.buf; + + /* finally, call t_optmgmt. clear as mud. */ + if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) { + fprintf(where, + "create_xti_endpoint: TCP_NODELAY option: errno %d t_errno %d\n", + errno, + t_errno); + fflush(where); + exit(1); + } + loc_nodelay = sock_option->value; + } +#else /* TCP_NODELAY */ + + loc_nodelay = 0; + +#endif /* TCP_NODELAY */ + + return(temp_socket); + +} + + +/* This routine implements the TCP unidirectional data transfer test */ +/* (a.k.a. stream) for the xti interface. It receives its */ +/* parameters via global variables from the shell and writes its */ +/* output to the standard output. */ + + +void +send_xti_tcp_stream(char remote_host[]) +{ + + char *tput_title = "\ +Recv Send Send \n\ +Socket Socket Message Elapsed \n\ +Size Size Size Time Throughput \n\ +bytes bytes bytes secs. %s/sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f \n"; + + char *cpu_title = "\ +Recv Send Send Utilization Service Demand\n\ +Socket Socket Message Elapsed Send Recv Send Recv\n\ +Size Size Size Time Throughput local remote local remote\n\ +bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1 = + "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *ksink_fmt = "\n\ +Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ +Local Remote Local Remote Xfered Per Per\n\ +Send Recv Send Recv Send (avg) Recv (avg)\n\ +%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; + + char *ksink_fmt2 = "\n\ +Maximum\n\ +Segment\n\ +Size (bytes)\n\ +%6d\n"; + + + float elapsed_time; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif + + /* what we want is to have a buffer space that is at least one */ + /* send-size greater than our send window. this will insure that we */ + /* are never trying to re-use a buffer that may still be in the hands */ + /* of the transport. This buffer will be malloc'd after we have found */ + /* the size of the local senc socket buffer. We will want to deal */ + /* with alignment and offset concerns as well. */ + + int *message_int_ptr; + + struct ring_elt *send_ring; + + int len; + unsigned int nummessages; + SOCKET send_socket; + int bytes_remaining; + int tcp_mss = -1; /* possibly uninitialized on printf far below */ + + /* with links like fddi, one can send > 32 bits worth of bytes */ + /* during a test... ;-) at some point, this should probably become a */ + /* 64bit integral type, but those are not entirely common yet */ + + double bytes_sent; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + + double thruput; + + /* some addressing information */ + struct hostent *hp; + struct sockaddr_in server; + unsigned int addr; + + struct t_call server_call; + + struct xti_tcp_stream_request_struct *xti_tcp_stream_request; + struct xti_tcp_stream_response_struct *xti_tcp_stream_response; + struct xti_tcp_stream_results_struct *xti_tcp_stream_result; + + xti_tcp_stream_request = + (struct xti_tcp_stream_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_stream_response = + (struct xti_tcp_stream_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_stream_result = + (struct xti_tcp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + /* it would seem that while HP-UX will allow an IP address (as a */ + /* string) in a call to gethostbyname, other, less enlightened */ + /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */ + /* order changed to check for IP address first. raj 7/96 */ + + if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) { + /* it was not an IP address, try it as a name */ + if ((hp = gethostbyname(remote_host)) == NULL) { + /* we have no idea what it is */ + fprintf(where, + "establish_control: could not resolve the destination %s\n", + remote_host); + fflush(where); + exit(1); + } + else { + /* it was a valid remote_host */ + bcopy(hp->h_addr, + (char *)&server.sin_addr, + hp->h_length); + server.sin_family = hp->h_addrtype; + } + } + else { + /* it was a valid IP address */ + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + } + + if ( print_headers ) { + /* we want to have some additional, interesting information in */ + /* the headers. we know some of it here, but not all, so we will */ + /* only print the test title here and will print the results */ + /* titles after the test is finished */ + fprintf(where,"XTI TCP STREAM TEST"); + fprintf(where," to %s", remote_host); + if (iteration_max > 1) { + fprintf(where, + " : +/-%3.1f%% @ %2d%% conf.", + interval/0.02, + confidence_level); + } + if (loc_nodelay || rem_nodelay) { + fprintf(where," : nodelay"); + } + if (loc_sndavoid || + loc_rcvavoid || + rem_sndavoid || + rem_rcvavoid) { + fprintf(where," : copy avoidance"); + } +#ifdef WANT_HISTOGRAM + fprintf(where," : histogram"); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + fprintf(where," : interval"); +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + fprintf(where," : dirty data"); +#endif /* DIRTY */ + fprintf(where,"\n"); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_sent = 0.0; + times_up = 0; + + /*set up the data socket */ + send_socket = create_xti_endpoint(loc_xti_device); + + if (send_socket == INVALID_SOCKET) { + perror("netperf: send_xti_tcp_stream: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_xti_tcp_stream: send_socket obtained...\n"); + } + + /* it would seem that with XTI, there is no implicit bind on a */ + /* connect, so we have to make a call to t_bind. this is not */ + /* terribly convenient, but I suppose that "standard is better */ + /* than better" :) raj 2/95 */ + + if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) { + t_error("send_xti_tcp_stream: t_bind"); + exit(1); + } + + /* at this point, we have either retrieved the socket buffer sizes, */ + /* or have tried to set them, so now, we may want to set the send */ + /* size based on that (because the user either did not use a -m */ + /* option, or used one with an argument of 0). If the socket buffer */ + /* size is not available, we will set the send size to 4KB - no */ + /* particular reason, just arbitrary... */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer ring with the requested alignment and offset. */ + /* note also that we have allocated a quantity */ + /* of memory that is at least one send-size greater than our socket */ + /* buffer size. We want to be sure that there are at least two */ + /* buffers allocated - this can be a bit of a problem when the */ + /* send_size is bigger than the socket size, so we must check... the */ + /* user may have wanted to explicitly set the "width" of our send */ + /* buffers, we should respect that wish... */ + + if (send_width == 0) { + send_width = (lss_size/send_size) + 1; + if (send_width == 1) send_width++; + } + + if (send_ring == NULL) { + /* only allocate the send ring once. this is a networking test, */ + /* not a memory allocation test. this way, we do not need a */ + /* deallocate_buffer_ring() routine, and I don't feel like */ + /* writing one anyway :) raj 11/94 */ + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 1, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_XTI_TCP_STREAM; + xti_tcp_stream_request->send_buf_size = rss_size; + xti_tcp_stream_request->recv_buf_size = rsr_size; + xti_tcp_stream_request->receive_size = recv_size; + xti_tcp_stream_request->no_delay = rem_nodelay; + xti_tcp_stream_request->recv_alignment = remote_recv_align; + xti_tcp_stream_request->recv_offset = remote_recv_offset; + xti_tcp_stream_request->measure_cpu = remote_cpu_usage; + xti_tcp_stream_request->cpu_rate = remote_cpu_rate; + if (test_time) { + xti_tcp_stream_request->test_length = test_time; + } + else { + xti_tcp_stream_request->test_length = test_bytes; + } + xti_tcp_stream_request->so_rcvavoid = rem_rcvavoid; + xti_tcp_stream_request->so_sndavoid = rem_sndavoid; + + strcpy(xti_tcp_stream_request->xti_device, rem_xti_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_tcp_stream_request->xti_device; + lastword = initword + ((strlen(rem_xti_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + +#ifdef DIRTY + xti_tcp_stream_request->dirty_count = rem_dirty_count; + xti_tcp_stream_request->clean_count = rem_clean_count; +#endif /* DIRTY */ + + + if (debug > 1) { + fprintf(where, + "netperf: send_xti_tcp_stream: requesting TCP stream test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = xti_tcp_stream_response->recv_buf_size; + rss_size = xti_tcp_stream_response->send_buf_size; + rem_nodelay = xti_tcp_stream_response->no_delay; + remote_cpu_usage = xti_tcp_stream_response->measure_cpu; + remote_cpu_rate = xti_tcp_stream_response->cpu_rate; + + /* we have to make sure that the server port number is in */ + /* network order */ + server.sin_port = (short)xti_tcp_stream_response->data_port_number; + server.sin_port = htons(server.sin_port); + rem_rcvavoid = xti_tcp_stream_response->so_rcvavoid; + rem_sndavoid = xti_tcp_stream_response->so_sndavoid; + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /*Connect up to the remote port on the data socket */ + memset (&server_call, 0, sizeof(server_call)); + server_call.addr.maxlen = sizeof(struct sockaddr_in); + server_call.addr.len = sizeof(struct sockaddr_in); + server_call.addr.buf = (char *)&server; + + if (t_connect(send_socket, + &server_call, + NULL) == INVALID_SOCKET){ + t_error("netperf: send_xti_tcp_stream: data socket connect failed"); + printf(" port: %d\n",ntohs(server.sin_port)); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either */ + /* the connect would have failed, or the previous response would */ + /* have indicated a problem. I failed to see the value of the */ + /* extra message after the accept on the remote. If it failed, */ + /* we'll see it here. If it didn't, we might as well start pumping */ + /* data. */ + + /* Set-up the test end conditions. For a stream test, they can be */ + /* either time or byte-count based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + bytes_remaining = 0; + /* in previous revisions, we had the same code repeated throught */ + /* all the test suites. this was unnecessary, and meant more */ + /* work for me when I wanted to switch to POSIX signals, so I */ + /* have abstracted this out into a routine in netlib.c. if you */ + /* are experiencing signal problems, you might want to look */ + /* there. raj 11/94 */ + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + bytes_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_xti_tcp_stream: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* before we start, initialize a few variables */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. */ + + while ((!times_up) || (bytes_remaining > 0)) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. at some point, we might want to replace */ + /* the rand() call with something from a table to reduce our call */ + /* overhead during the test, but it is not a high priority item. */ + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + /* timestamp just before we go into send and then again just after */ + /* we come out raj 8/94 */ + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + if((len=t_snd(send_socket, + send_ring->buffer_ptr, + send_size, + 0)) != send_size) { + if ((len >=0) || (errno == EINTR)) { + /* the test was interrupted, must be the end of test */ + break; + } + fprintf(where, + "send_xti_tcp_stream: t_snd: errno %d t_errno %d t_look 0x%.4x\n", + errno, + t_errno, + t_look(send_socket)); + fflush(where); + exit(1); + } + +#ifdef WANT_HISTOGRAM + /* timestamp the exit from the send call and update the histogram */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ + +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += send_size; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_xti_tcp_stream: fault with signal set!\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + /* now we want to move our pointer to the next position in the */ + /* data buffer...we may also want to wrap back to the "beginning" */ + /* of the bufferspace, so we will mod the number of messages sent */ + /* by the send width, and use that to calculate the offset to add */ + /* to the base pointer. */ + nummessages++; + send_ring = send_ring->next; + if (bytes_remaining) { + bytes_remaining -= send_size; + } + } + + /* The test is over. Flush the buffers to the remote end. We do a */ + /* graceful release to insure that all data has been taken by the */ + /* remote. */ + + /* but first, if the verbosity is greater than 1, find-out what */ + /* the TCP maximum segment_size was (if possible) */ + if (verbosity > 1) { + tcp_mss = -1; + get_xti_info(send_socket,info_struct); + } + + if (t_sndrel(send_socket) == -1) { + t_error("netperf: cannot shutdown tcp stream socket"); + exit(1); + } + + /* hang a t_rcvrel() off the socket to block until the remote has */ + /* brought all the data up into the application. it will do a */ + /* t_sedrel to cause a FIN to be sent our way. We will assume that */ + /* any exit from the t_rcvrel() call is good... raj 2/95 */ + + if (debug > 1) { + fprintf(where,"about to hang a receive for graceful release.\n"); + fflush(where); + } + + t_rcvrel(send_socket); + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured and how */ + /* long did we really */ + /* run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) */ + + bytes_sent = xti_tcp_stream_result->bytes_received; + + thruput = calc_thruput(bytes_sent); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + + local_cpu_utilization = calc_cpu_util(0.0); + local_service_demand = calc_service_demand(bytes_sent, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + + remote_cpu_utilization = xti_tcp_stream_result->cpu_util; + remote_service_demand = calc_service_demand(bytes_sent, + 0.0, + remote_cpu_utilization, + xti_tcp_stream_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + } + + /* at this point, we have finished making all the runs that we */ + /* will be making. so, we should extract what the calcuated values */ + /* are for all the confidence stuff. we could make the values */ + /* global, but that seemed a little messy, and it did not seem worth */ + /* all the mucking with header files. so, we create a routine much */ + /* like calcualte_confidence, which just returns the mean values. */ + /* raj 11/94 */ + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(xti_tcp_stream_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + rsr_size, /* remote recvbuf size */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + thruput);/* how fast did it go */ + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + /* this stuff needs to be worked-out in the presence of confidence */ + /* intervals and multiple iterations of the test... raj 11/94 */ + + fprintf(where, + ksink_fmt, + "Bytes", + "Bytes", + "Bytes", + local_send_align, + remote_recv_align, + local_send_offset, + remote_recv_offset, + bytes_sent, + bytes_sent / (double)nummessages, + nummessages, + bytes_sent / (double)xti_tcp_stream_result->recv_calls, + xti_tcp_stream_result->recv_calls); + fprintf(where, + ksink_fmt2, + tcp_mss); + fflush(where); +#ifdef WANT_HISTOGRAM + fprintf(where,"\n\nHistogram of time spent in send() call.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } + +} + + +/* This is the server-side routine for the tcp stream test. It is */ +/* implemented as one routine. I could break things-out somewhat, but */ +/* didn't feel it was necessary. */ + +void +recv_xti_tcp_stream() +{ + + struct sockaddr_in myaddr_in, peeraddr_in; + struct t_bind bind_req, bind_resp; + struct t_call call_req; + + SOCKET s_listen,s_data; + int addrlen; + int len; + unsigned int receive_calls; + float elapsed_time; + double bytes_received; + + struct ring_elt *recv_ring; + + int *message_int_ptr; + int i; + + struct xti_tcp_stream_request_struct *xti_tcp_stream_request; + struct xti_tcp_stream_response_struct *xti_tcp_stream_response; + struct xti_tcp_stream_results_struct *xti_tcp_stream_results; + + xti_tcp_stream_request = + (struct xti_tcp_stream_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_stream_response = + (struct xti_tcp_stream_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_stream_results = + (struct xti_tcp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_xti_tcp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = XTI_TCP_STREAM_RESPONSE; + + if (debug) { + fprintf(where,"recv_xti_tcp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_stream: requested alignment of %d\n", + xti_tcp_stream_request->recv_alignment); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = 0; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_stream: grabbing a socket...\n"); + fflush(where); + } + + /* create_xti_endpoint expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size = xti_tcp_stream_request->send_buf_size; + lsr_size = xti_tcp_stream_request->recv_buf_size; + loc_nodelay = xti_tcp_stream_request->no_delay; + loc_rcvavoid = xti_tcp_stream_request->so_rcvavoid; + loc_sndavoid = xti_tcp_stream_request->so_sndavoid; + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_tcp_stream_request->xti_device; + lastword = initword + ((xti_tcp_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } + +#endif /* __alpha */ + + s_listen = create_xti_endpoint(xti_tcp_stream_request->xti_device); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + bind_req.addr.maxlen = sizeof(struct sockaddr_in); + bind_req.addr.len = sizeof(struct sockaddr_in); + bind_req.addr.buf = (char *)&myaddr_in; + bind_req.qlen = 1; + + bind_resp.addr.maxlen = sizeof(struct sockaddr_in); + bind_resp.addr.len = sizeof(struct sockaddr_in); + bind_resp.addr.buf = (char *)&myaddr_in; + bind_resp.qlen = 1; + + if (t_bind(s_listen, + &bind_req, + &bind_resp) == SOCKET_ERROR) { + netperf_response.content.serv_errno = t_errno; + close(s_listen); + send_response(); + + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_bind complete port %d\n", + ntohs(myaddr_in.sin_port)); + fflush(where); + } + + /* what sort of sizes did we end-up with? */ + if (xti_tcp_stream_request->receive_size == 0) { + if (lsr_size > 0) { + recv_size = lsr_size; + } + else { + recv_size = 4096; + } + } + else { + recv_size = xti_tcp_stream_request->receive_size; + } + + /* we want to set-up our recv_ring in a manner analagous to what we */ + /* do on the sending side. this is more for the sake of symmetry */ + /* than for the needs of say copy avoidance, but it might also be */ + /* more realistic - this way one could conceivably go with a */ + /* double-buffering scheme when taking the data an putting it into */ + /* the filesystem or something like that. raj 7/94 */ + + if (recv_width == 0) { + recv_width = (lsr_size/recv_size) + 1; + if (recv_width == 1) recv_width++; + } + + recv_ring = allocate_buffer_ring(recv_width, + recv_size, + xti_tcp_stream_request->recv_alignment, + xti_tcp_stream_request->recv_offset); + + if (debug) { + fprintf(where,"recv_xti_tcp_stream: recv alignment and offset set...\n"); + fflush(where); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + xti_tcp_stream_response->data_port_number = + (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + xti_tcp_stream_response->cpu_rate = 0.0; /* assume no cpu */ + if (xti_tcp_stream_request->measure_cpu) { + xti_tcp_stream_response->measure_cpu = 1; + xti_tcp_stream_response->cpu_rate = + calibrate_local_cpu(xti_tcp_stream_request->cpu_rate); + } + else { + xti_tcp_stream_response->measure_cpu = 0; + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + xti_tcp_stream_response->send_buf_size = lss_size; + xti_tcp_stream_response->recv_buf_size = lsr_size; + xti_tcp_stream_response->no_delay = loc_nodelay; + xti_tcp_stream_response->so_rcvavoid = loc_rcvavoid; + xti_tcp_stream_response->so_sndavoid = loc_sndavoid; + xti_tcp_stream_response->receive_size = recv_size; + + send_response(); + + /* Now, let's set-up the socket to listen for connections. for xti, */ + /* the t_listen call is blocking by default - this is different */ + /* semantics from BSD - probably has to do with being able to reject */ + /* a call before an accept */ + call_req.addr.maxlen = sizeof(struct sockaddr_in); + call_req.addr.len = sizeof(struct sockaddr_in); + call_req.addr.buf = (char *)&peeraddr_in; + call_req.opt.maxlen = 0; + call_req.opt.len = 0; + call_req.opt.buf = NULL; + call_req.udata.maxlen= 0; + call_req.udata.len = 0; + call_req.udata.buf = 0; + + if (t_listen(s_listen, &call_req) == -1) { + fprintf(where, + "recv_xti_tcp_stream: t_listen: errno %d t_errno %d\n", + errno, + t_errno); + fflush(where); + netperf_response.content.serv_errno = t_errno; + close(s_listen); + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_listen complete t_look 0x%.4x\n", + t_look(s_listen)); + fflush(where); + } + + /* now just rubber stamp the thing. we want to use the same fd? so */ + /* we will just equate s_data with s_listen. this seems a little */ + /* hokey to me, but then I'm a BSD biggot still. raj 2/95 */ + s_data = s_listen; + if (t_accept(s_listen, + s_data, + &call_req) == -1) { + fprintf(where, + "recv_xti_tcp_stream: t_accept: errno %d t_errno %d\n", + errno, + t_errno); + fflush(where); + close(s_listen); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_accept complete t_look 0x%.4x\n", + t_look(s_data)); + fprintf(where, + " remote is %s port %d\n", + inet_ntoa(*(struct in_addr *)&peeraddr_in.sin_addr), + ntohs(peeraddr_in.sin_port)); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(xti_tcp_stream_request->measure_cpu); + + /* The loop will exit when the sender does a t_sndrel, which will */ + /* return T_LOOK error from the t_recv */ + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to recv. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + access_buffer(recv_ring->buffer_ptr, + recv_size, + xti_tcp_stream_request->dirty_count, + xti_tcp_stream_request->clean_count); + +#endif /* DIRTY */ + + bytes_received = 0; + receive_calls = 0; + + while ((len = t_rcv(s_data, + recv_ring->buffer_ptr, + recv_size, + &xti_flags)) != -1) { + bytes_received += len; + receive_calls++; + + /* more to the next buffer in the recv_ring */ + recv_ring = recv_ring->next; + +#ifdef DIRTY + + access_buffer(recv_ring->buffer_ptr, + recv_size, + xti_tcp_stream_request->dirty_count, + xti_tcp_stream_request->clean_count); + +#endif /* DIRTY */ + } + + if (t_look(s_data) == T_ORDREL) { + /* this is a normal exit path */ + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_rcv T_ORDREL indicated\n"); + fflush(where); + } + } + else { + /* something went wrong */ + fprintf(where, + "recv_xti_tcp_stream: t_rcv: errno %d t_errno %d len %d", + errno, + t_errno, + len); + fprintf(where, + " t_look 0x%.4x", + t_look(s_data)); + fflush(where); + netperf_response.content.serv_errno = t_errno; + send_response(); + exit(1); + } + + /* receive the release and let the initiator know that we have */ + /* received all the data. raj 3/95 */ + + if (t_rcvrel(s_data) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_rcvrel complete\n"); + fflush(where); + } + + if (t_sndrel(s_data) == -1) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: t_sndrel complete\n"); + fflush(where); + } + + cpu_stop(xti_tcp_stream_request->measure_cpu,&elapsed_time); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: got %g bytes\n", + bytes_received); + fprintf(where, + "recv_xti_tcp_stream: got %d recvs\n", + receive_calls); + fflush(where); + } + + xti_tcp_stream_results->bytes_received = bytes_received; + xti_tcp_stream_results->elapsed_time = elapsed_time; + xti_tcp_stream_results->recv_calls = receive_calls; + + if (xti_tcp_stream_request->measure_cpu) { + xti_tcp_stream_results->cpu_util = calc_cpu_util(0.0); + }; + + if (debug) { + fprintf(where, + "recv_xti_tcp_stream: test complete, sending results.\n"); + fprintf(where, + " bytes_received %g receive_calls %d\n", + bytes_received, + receive_calls); + fprintf(where, + " len %d\n", + len); + fflush(where); + } + + xti_tcp_stream_results->cpu_method = cpu_method; + send_response(); + + /* we are now done with the socket */ + t_close(s_data); + +} + + + /* this routine implements the sending (netperf) side of the XTI_TCP_RR */ + /* test. */ + +void +send_xti_tcp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int timed_out = 0; + float elapsed_time; + + int len; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct hostent *hp; + struct sockaddr_in server; + unsigned int addr; + + struct t_call server_call; + + struct xti_tcp_rr_request_struct *xti_tcp_rr_request; + struct xti_tcp_rr_response_struct *xti_tcp_rr_response; + struct xti_tcp_rr_results_struct *xti_tcp_rr_result; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif /* WANT_INTERVALS */ + + xti_tcp_rr_request = + (struct xti_tcp_rr_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_rr_response= + (struct xti_tcp_rr_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_rr_result = + (struct xti_tcp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + /* it would seem that while HP-UX will allow an IP address (as a */ + /* string) in a call to gethostbyname, other, less enlightened */ + /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */ + /* order changed to check for IP address first. raj 7/96 */ + + if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) { + /* it was not an IP address, try it as a name */ + if ((hp = gethostbyname(remote_host)) == NULL) { + /* we have no idea what it is */ + fprintf(where, + "establish_control: could not resolve the destination %s\n", + remote_host); + fflush(where); + exit(1); + } + else { + /* it was a valid remote_host */ + bcopy(hp->h_addr, + (char *)&server.sin_addr, + hp->h_length); + server.sin_family = hp->h_addrtype; + } + } + else { + /* it was a valid IP address */ + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + } + + if ( print_headers ) { + fprintf(where,"XTI TCP REQUEST/RESPONSE TEST"); + fprintf(where," to %s", remote_host); + if (iteration_max > 1) { + fprintf(where, + " : +/-%3.1f%% @ %2d%% conf.", + interval/0.02, + confidence_level); + } + if (loc_nodelay || rem_nodelay) { + fprintf(where," : nodelay"); + } + if (loc_sndavoid || + loc_rcvavoid || + rem_sndavoid || + rem_rcvavoid) { + fprintf(where," : copy avoidance"); + } +#ifdef WANT_HISTOGRAM + fprintf(where," : histogram"); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + fprintf(where," : interval"); +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + fprintf(where," : dirty data"); +#endif /* DIRTY */ + fprintf(where,"\n"); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + confidence_iteration = 1; + init_stat(); + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + timed_out = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset. */ + /* since this is a request/response test, default the send_width and */ + /* recv_width to 1 and not two raj 7/94 */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /*set up the data socket */ + send_socket = create_xti_endpoint(loc_xti_device); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_xti_tcp_rr: tcp stream data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_xti_tcp_rr: send_socket obtained...\n"); + } + + /* it would seem that with XTI, there is no implicit bind on a */ + /* connect, so we have to make a call to t_bind. this is not */ + /* terribly convenient, but I suppose that "standard is better */ + /* than better" :) raj 2/95 */ + + if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) { + t_error("send_xti_tcp_stream: t_bind"); + exit(1); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_XTI_TCP_RR; + xti_tcp_rr_request->recv_buf_size = rsr_size; + xti_tcp_rr_request->send_buf_size = rss_size; + xti_tcp_rr_request->recv_alignment = remote_recv_align; + xti_tcp_rr_request->recv_offset = remote_recv_offset; + xti_tcp_rr_request->send_alignment = remote_send_align; + xti_tcp_rr_request->send_offset = remote_send_offset; + xti_tcp_rr_request->request_size = req_size; + xti_tcp_rr_request->response_size = rsp_size; + xti_tcp_rr_request->no_delay = rem_nodelay; + xti_tcp_rr_request->measure_cpu = remote_cpu_usage; + xti_tcp_rr_request->cpu_rate = remote_cpu_rate; + xti_tcp_rr_request->so_rcvavoid = rem_rcvavoid; + xti_tcp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + xti_tcp_rr_request->test_length = test_time; + } + else { + xti_tcp_rr_request->test_length = test_trans * -1; + } + + strcpy(xti_tcp_rr_request->xti_device, rem_xti_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_tcp_rr_request->xti_device; + lastword = initword + ((strlen(rem_xti_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (debug > 1) { + fprintf(where,"netperf: send_xti_tcp_rr: requesting TCP rr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = xti_tcp_rr_response->recv_buf_size; + rss_size = xti_tcp_rr_response->send_buf_size; + rem_nodelay = xti_tcp_rr_response->no_delay; + remote_cpu_usage = xti_tcp_rr_response->measure_cpu; + remote_cpu_rate = xti_tcp_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + server.sin_port = (short)xti_tcp_rr_response->data_port_number; + server.sin_port = htons(server.sin_port); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /*Connect up to the remote port on the data socket */ + memset (&server_call, 0, sizeof(server_call)); + server_call.addr.maxlen = sizeof(struct sockaddr_in); + server_call.addr.len = sizeof(struct sockaddr_in); + server_call.addr.buf = (char *)&server; + + if (t_connect(send_socket, + &server_call, + NULL) == INVALID_SOCKET){ + t_error("netperf: send_xti_tcp_rr: data socket connect failed"); + printf(" port: %d\n",ntohs(server.sin_port)); + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_xti_tcp_rr: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request. we assume that if we use a blocking socket, */ + /* the request will be sent at one shot. */ + +#ifdef WANT_HISTOGRAM + /* timestamp just before our call to send, and then again just */ + /* after the receive raj 8/94 */ + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + if((len=t_snd(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if ((errno == EINTR) || (errno == 0)) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + fprintf(where, + "send_xti_tcp_rr: t_snd: errno %d t_errno %d t_look 0x%.4x\n", + errno, + t_errno, + t_look(send_socket)); + fflush(where); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=t_rcv(send_socket, + temp_message_ptr, + rsp_bytes_left, + &xti_flags)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + fprintf(where, + "send_xti_tcp_rr: t_rcv: errno %d t_errno %d t_look 0x%x\n", + errno, + t_errno, + t_look(send_socket)); + fflush(where); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + +#ifdef WANT_HISTOGRAM + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += 1; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_xti_udp_rr: fault with signal set!\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where, + "Transaction %d completed\n", + nummessages); + fflush(where); + } + } + } + + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages/elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = xti_tcp_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + xti_tcp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are now done with the socket, so close it */ + t_close(send_socket); + + } + + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(xti_tcp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + thruput, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + thruput); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt, + local_send_align, + remote_recv_offset, + local_send_offset, + remote_recv_offset); + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/response times\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + + } + +} + +void +send_xti_udp_stream(char remote_host[]) +{ + /**********************************************************************/ + /* */ + /* UDP Unidirectional Send Test */ + /* */ + /**********************************************************************/ + char *tput_title = "\ +Socket Message Elapsed Messages \n\ +Size Size Time Okay Errors Throughput\n\ +bytes bytes secs # # %s/sec\n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1 = "\ +%6d %6d %-7.2f %7d %6d %7.2f\n\ +%6d %-7.2f %7d %7.2f\n\n"; + + + char *cpu_title = "\ +Socket Message Elapsed Messages CPU Service\n\ +Size Size Time Okay Errors Throughput Util Demand\n\ +bytes bytes secs # # %s/sec %% %c%c us/KB\n\n"; + + char *cpu_fmt_0 = + "%6.2f %c\n"; + + char *cpu_fmt_1 = "\ +%6d %6d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ +%6d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; + + unsigned int messages_recvd; + unsigned int messages_sent; + unsigned int failed_sends; + + float elapsed_time, + recv_elapsed, + local_cpu_utilization, + remote_cpu_utilization; + + float local_service_demand, remote_service_demand; + double local_thruput, remote_thruput; + double bytes_sent; + double bytes_recvd; + + + int len; + int *message_int_ptr; + struct ring_elt *send_ring; + SOCKET data_socket; + + unsigned int sum_messages_sent; + unsigned int sum_messages_recvd; + unsigned int sum_failed_sends; + double sum_local_thruput; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif /* WANT_INTERVALS */ + + struct hostent *hp; + struct sockaddr_in server; + unsigned int addr; + + struct t_unitdata unitdata; + + struct xti_udp_stream_request_struct *xti_udp_stream_request; + struct xti_udp_stream_response_struct *xti_udp_stream_response; + struct xti_udp_stream_results_struct *xti_udp_stream_results; + + xti_udp_stream_request = + (struct xti_udp_stream_request_struct *)netperf_request.content.test_specific_data; + xti_udp_stream_response = + (struct xti_udp_stream_response_struct *)netperf_response.content.test_specific_data; + xti_udp_stream_results = + (struct xti_udp_stream_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif /* WANT_HISTOGRAM */ + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + /* it would seem that while HP-UX will allow an IP address (as a */ + /* string) in a call to gethostbyname, other, less enlightened */ + /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */ + /* order changed to check for IP address first. raj 7/96 */ + + if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) { + /* it was not an IP address, try it as a name */ + if ((hp = gethostbyname(remote_host)) == NULL) { + /* we have no idea what it is */ + fprintf(where, + "establish_control: could not resolve the destination %s\n", + remote_host); + fflush(where); + exit(1); + } + else { + /* it was a valid remote_host */ + bcopy(hp->h_addr, + (char *)&server.sin_addr, + hp->h_length); + server.sin_family = hp->h_addrtype; + } + } + else { + /* it was a valid IP address */ + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + } + + if ( print_headers ) { + fprintf(where,"UDP UNIDIRECTIONAL SEND TEST"); + fprintf(where," to %s", remote_host); + if (iteration_max > 1) { + fprintf(where, + " : +/-%3.1f%% @ %2d%% conf.", + interval/0.02, + confidence_level); + } + if (loc_sndavoid || + loc_rcvavoid || + rem_sndavoid || + rem_rcvavoid) { + fprintf(where," : copy avoidance"); + } +#ifdef WANT_HISTOGRAM + fprintf(where," : histogram"); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + fprintf(where," : interval"); +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + fprintf(where," : dirty data"); +#endif /* DIRTY */ + fprintf(where,"\n"); + } + + send_ring = NULL; + confidence_iteration = 1; + init_stat(); + sum_messages_sent = 0; + sum_messages_recvd = 0; + sum_failed_sends = 0; + sum_local_thruput = 0.0; + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + /* initialize a few counters. we have to remember that we might be */ + /* going through the loop more than once. */ + messages_sent = 0; + messages_recvd = 0; + failed_sends = 0; + times_up = 0; + + /*set up the data socket */ + data_socket = create_xti_endpoint(loc_xti_device); + + if (data_socket == INVALID_SOCKET) { + perror("send_xti_udp_stream: create_xti_endpoint"); + exit(1); + } + + if (t_bind(data_socket, NULL, NULL) == SOCKET_ERROR) { + t_error("send_xti_udp_stream: t_bind"); + exit(1); + } + + /* now, we want to see if we need to set the send_size */ + if (send_size == 0) { + if (lss_size > 0) { + send_size = lss_size; + } + else { + send_size = 4096; + } + } + + /* set-up the data buffer with the requested alignment and offset, */ + /* most of the numbers here are just a hack to pick something nice */ + /* and big in an attempt to never try to send a buffer a second time */ + /* before it leaves the node...unless the user set the width */ + /* explicitly. */ + if (send_width == 0) send_width = 32; + + if (send_ring == NULL ) { + send_ring = allocate_buffer_ring(send_width, + send_size, + local_send_align, + local_send_offset); + } + + + /* if the user supplied a cpu rate, this call will complete rather */ + /* quickly, otherwise, the cpu rate will be retured to us for */ + /* possible display. The Library will keep it's own copy of this data */ + /* for use elsewhere. We will only display it. (Does that make it */ + /* "opaque" to us?) */ + + if (local_cpu_usage) + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + + /* Tell the remote end to set up the data connection. The server */ + /* sends back the port number and alters the socket parameters there. */ + /* Of course this is a datagram service so no connection is actually */ + /* set up, the server just sets up the socket and binds it. */ + + netperf_request.content.request_type = DO_XTI_UDP_STREAM; + xti_udp_stream_request->recv_buf_size = rsr_size; + xti_udp_stream_request->message_size = send_size; + xti_udp_stream_request->recv_alignment = remote_recv_align; + xti_udp_stream_request->recv_offset = remote_recv_offset; + xti_udp_stream_request->measure_cpu = remote_cpu_usage; + xti_udp_stream_request->cpu_rate = remote_cpu_rate; + xti_udp_stream_request->test_length = test_time; + xti_udp_stream_request->so_rcvavoid = rem_rcvavoid; + xti_udp_stream_request->so_sndavoid = rem_sndavoid; + + strcpy(xti_udp_stream_request->xti_device, rem_xti_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_udp_stream_request->xti_device; + lastword = initword + ((strlen(rem_xti_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + send_request(); + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_xti_udp_stream: remote data connection done.\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_xti_udp_stream: error on remote"); + exit(1); + } + + /* Place the port number returned by the remote into the sockaddr */ + /* structure so our sends can be sent to the correct place. Also get */ + /* some of the returned socket buffer information for user display. */ + + /* make sure that port numbers are in the proper order */ + server.sin_port = (short)xti_udp_stream_response->data_port_number; + server.sin_port = htons(server.sin_port); + rsr_size = xti_udp_stream_response->recv_buf_size; + rss_size = xti_udp_stream_response->send_buf_size; + remote_cpu_rate = xti_udp_stream_response->cpu_rate; + + /* it would seem that XTI does not allow the expedient of */ + /* "connecting" a UDP end-point the way BSD does. so, we will do */ + /* everything with t_sndudata and t_rcvudata. Our "virtual" */ + /* connect here will be to assign the destination portion of the */ + /* t_unitdata struct here, where we would have otherwise called */ + /* t_connect() raj 3/95 */ + + memset (&unitdata, 0, sizeof(unitdata)); + unitdata.addr.maxlen = sizeof(struct sockaddr_in); + unitdata.addr.len = sizeof(struct sockaddr_in); + unitdata.addr.buf = (char *)&server; + + /* we don't use any options, so might as well set that part here */ + /* too */ + + unitdata.opt.maxlen = 0; + unitdata.opt.len = 0; + unitdata.opt.buf = NULL; + + /* we need to initialize the send buffer for the first time as */ + /* well since we move to the next pointer after the send call. */ + + unitdata.udata.maxlen = send_size; + unitdata.udata.len = send_size; + unitdata.udata.buf = send_ring->buffer_ptr; + + /* set up the timer to call us after test_time. one of these days, */ + /* it might be nice to figure-out a nice reliable way to have the */ + /* test controlled by a byte count as well, but since UDP is not */ + /* reliable, that could prove difficult. so, in the meantime, we */ + /* only allow a XTI_UDP_STREAM test to be a timed test. */ + + if (test_time) { + times_up = 0; + start_timer(test_time); + } + else { + fprintf(where,"Sorry, XTI_UDP_STREAM tests must be timed.\n"); + fflush(where); + exit(1); + } + + /* Get the start count for the idle counter and the start time */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_xti_udp_stream: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* Send datagrams like there was no tomorrow. at somepoint it might */ + /* be nice to set this up so that a quantity of bytes could be sent, */ + /* but we still need some sort of end of test trigger on the receive */ + /* side. that could be a select with a one second timeout, but then */ + /* if there is a test where none of the data arrives for awile and */ + /* then starts again, we would end the test too soon. something to */ + /* think about... */ + while (!times_up) { + +#ifdef DIRTY + /* we want to dirty some number of consecutive integers in the buffer */ + /* we are about to send. we may also want to bring some number of */ + /* them cleanly into the cache. The clean ones will follow any dirty */ + /* ones into the cache. */ + + access_buffer(send_ring->buffer_ptr, + send_size, + loc_dirty_count, + loc_clean_count); + +#endif /* DIRTY */ + +#ifdef WANT_HISTOGRAM + HIST_timestamp(&time_one); +#endif /* WANT_HISTOGRAM */ + + if ((t_sndudata(data_socket, + &unitdata)) != 0) { + if (errno == EINTR) + break; + if (errno == ENOBUFS) { + failed_sends++; + continue; + } + perror("xti_udp_send: data send error"); + t_error("xti_udp_send: data send error"); + exit(1); + } + messages_sent++; + + /* now we want to move our pointer to the next position in the */ + /* data buffer...and update the unitdata structure */ + + send_ring = send_ring->next; + unitdata.udata.buf = send_ring->buffer_ptr; + +#ifdef WANT_HISTOGRAM + /* get the second timestamp */ + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += send_size; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_xti_udp_stream: fault with signal set!\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + } + + /* This is a timed test, so the remote will be returning to us after */ + /* a time. We should not need to send any "strange" messages to tell */ + /* the remote that the test is completed, unless we decide to add a */ + /* number of messages to the test. */ + + /* the test is over, so get stats and stuff */ + cpu_stop(local_cpu_usage, + &elapsed_time); + + /* Get the statistics from the remote end */ + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"send_xti_udp_stream: remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("send_xti_udp_stream: error on remote"); + exit(1); + } + + bytes_sent = (double) send_size * (double) messages_sent; + local_thruput = calc_thruput(bytes_sent); + + messages_recvd = xti_udp_stream_results->messages_recvd; + bytes_recvd = (double) send_size * (double) messages_recvd; + + /* we asume that the remote ran for as long as we did */ + + remote_thruput = calc_thruput(bytes_recvd); + + /* print the results for this socket and message size */ + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) We pass zeros for the local */ + /* cpu utilization and elapsed time to tell the routine to use */ + /* the libraries own values for those. */ + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + /* shouldn't this really be based on bytes_recvd, since that is */ + /* the effective throughput of the test? I think that it should, */ + /* so will make the change raj 11/94 */ + local_service_demand = calc_service_demand(bytes_recvd, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + /* The local calculations could use variables being kept by */ + /* the local netlib routines. The remote calcuations need to */ + /* have a few things passed to them. */ + if (remote_cpu_usage) { + remote_cpu_utilization = xti_udp_stream_results->cpu_util; + remote_service_demand = calc_service_demand(bytes_recvd, + 0.0, + remote_cpu_utilization, + xti_udp_stream_results->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + remote_thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + /* since the routine calculate_confidence is rather generic, and */ + /* we have a few other parms of interest, we will do a little work */ + /* here to caclulate their average. */ + sum_messages_sent += messages_sent; + sum_messages_recvd += messages_recvd; + sum_failed_sends += failed_sends; + sum_local_thruput += local_thruput; + + confidence_iteration++; + + /* this datapoint is done, so we don't need the socket any longer */ + close(data_socket); + + } + + /* we should reach this point once the test is finished */ + + retrieve_confident_values(&elapsed_time, + &remote_thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* some of the interesting values aren't covered by the generic */ + /* confidence routine */ + messages_sent = sum_messages_sent / (confidence_iteration -1); + messages_recvd = sum_messages_recvd / (confidence_iteration -1); + failed_sends = sum_failed_sends / (confidence_iteration -1); + local_thruput = sum_local_thruput / (confidence_iteration -1); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(xti_udp_stream_results->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + local_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + format_units(), + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long was the test */ + messages_sent, + failed_sends, + local_thruput, /* what was the xfer rate */ + local_cpu_utilization, /* local cpu */ + local_service_demand, /* local service demand */ + rsr_size, + elapsed_time, + messages_recvd, + remote_thruput, + remote_cpu_utilization, /* remote cpu */ + remote_service_demand); /* remote service demand */ + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + local_thruput); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + fprintf(where, + tput_fmt_1, /* the format string */ + lss_size, /* local sendbuf size */ + send_size, /* how large were the sends */ + elapsed_time, /* how long did it take */ + messages_sent, + failed_sends, + local_thruput, + rsr_size, /* remote recvbuf size */ + elapsed_time, + messages_recvd, + remote_thruput); + break; + } + } + + fflush(where); +#ifdef WANT_HISTOGRAM + if (verbosity > 1) { + fprintf(where,"\nHistogram of time spent in send() call\n"); + fflush(where); + HIST_report(time_hist); + } +#endif /* WANT_HISTOGRAM */ + +} + + + /* this routine implements the receive side (netserver) of the */ + /* XTI_UDP_STREAM performance test. */ + +void +recv_xti_udp_stream() +{ + struct ring_elt *recv_ring; + + struct t_bind bind_req, bind_resp; + struct t_unitdata unitdata; + int flags = 0; + + struct sockaddr_in myaddr_in; + struct sockaddr_in fromaddr_in; + + SOCKET s_data; + int addrlen; + unsigned int bytes_received = 0; + float elapsed_time; + + unsigned int message_size; + unsigned int messages_recvd = 0; + + struct xti_udp_stream_request_struct *xti_udp_stream_request; + struct xti_udp_stream_response_struct *xti_udp_stream_response; + struct xti_udp_stream_results_struct *xti_udp_stream_results; + + xti_udp_stream_request = + (struct xti_udp_stream_request_struct *)netperf_request.content.test_specific_data; + xti_udp_stream_response = + (struct xti_udp_stream_response_struct *)netperf_response.content.test_specific_data; + xti_udp_stream_results = + (struct xti_udp_stream_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_xti_udp_stream: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug > 1) { + fprintf(where,"recv_xti_udp_stream: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = XTI_UDP_STREAM_RESPONSE; + + if (debug > 2) { + fprintf(where,"recv_xti_udp_stream: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variable to be at the desired */ + /* alignment with the desired offset. */ + + if (debug > 1) { + fprintf(where,"recv_xti_udp_stream: requested alignment of %d\n", + xti_udp_stream_request->recv_alignment); + fflush(where); + } + + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + xti_udp_stream_request->message_size, + xti_udp_stream_request->recv_alignment, + xti_udp_stream_request->recv_offset); + + if (debug > 1) { + fprintf(where,"recv_xti_udp_stream: receive alignment and offset set...\n"); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = 0; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug > 1) { + fprintf(where,"recv_xti_udp_stream: grabbing a socket...\n"); + fflush(where); + } + + /* create_xti_endpoint expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lsr_size = xti_udp_stream_request->recv_buf_size; + loc_rcvavoid = xti_udp_stream_request->so_rcvavoid; + loc_sndavoid = xti_udp_stream_request->so_sndavoid; + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_udp_stream_request->xti_device; + lastword = initword + ((xti_udp_stream_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } + +#endif /* __alpha */ + + s_data = create_xti_endpoint(xti_udp_stream_request->xti_device); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + bind_req.addr.maxlen = sizeof(struct sockaddr_in); + bind_req.addr.len = sizeof(struct sockaddr_in); + bind_req.addr.buf = (char *)&myaddr_in; + bind_req.qlen = 1; + + bind_resp.addr.maxlen = sizeof(struct sockaddr_in); + bind_resp.addr.len = sizeof(struct sockaddr_in); + bind_resp.addr.buf = (char *)&myaddr_in; + bind_resp.qlen = 1; + + if (t_bind(s_data, + &bind_req, + &bind_resp) == SOCKET_ERROR) { + netperf_response.content.serv_errno = t_errno; + send_response(); + + exit(1); + } + + xti_udp_stream_response->test_length = + xti_udp_stream_request->test_length; + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + xti_udp_stream_response->data_port_number = + (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a -1 to */ + /* the initiator. */ + + xti_udp_stream_response->cpu_rate = 0.0; /* assume no cpu */ + xti_udp_stream_response->measure_cpu = 0; + if (xti_udp_stream_request->measure_cpu) { + /* We will pass the rate into the calibration routine. If the */ + /* user did not specify one, it will be 0.0, and we will do a */ + /* "real" calibration. Otherwise, all it will really do is */ + /* store it away... */ + xti_udp_stream_response->measure_cpu = 1; + xti_udp_stream_response->cpu_rate = + calibrate_local_cpu(xti_udp_stream_request->cpu_rate); + } + + message_size = xti_udp_stream_request->message_size; + test_time = xti_udp_stream_request->test_length; + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + xti_udp_stream_response->send_buf_size = lss_size; + xti_udp_stream_response->recv_buf_size = lsr_size; + xti_udp_stream_response->so_rcvavoid = loc_rcvavoid; + xti_udp_stream_response->so_sndavoid = loc_sndavoid; + + /* since we are going to call t_rcvudata() instead of t_rcv() we */ + /* need to init the unitdata structure raj 3/95 */ + + unitdata.addr.maxlen = sizeof(fromaddr_in); + unitdata.addr.len = sizeof(fromaddr_in); + unitdata.addr.buf = (char *)&fromaddr_in; + + unitdata.opt.maxlen = 0; + unitdata.opt.len = 0; + unitdata.opt.buf = NULL; + + unitdata.udata.maxlen = xti_udp_stream_request->message_size; + unitdata.udata.len = xti_udp_stream_request->message_size; + unitdata.udata.buf = recv_ring->buffer_ptr; + + send_response(); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(xti_udp_stream_request->measure_cpu); + + /* The loop will exit when the timer pops, or if we happen to recv a */ + /* message of less than send_size bytes... */ + + times_up = 0; + start_timer(test_time + PAD_TIME); + + if (debug) { + fprintf(where,"recv_xti_udp_stream: about to enter inner sanctum.\n"); + fflush(where); + } + + while (!times_up) { +#ifdef RAJ_DEBUG + if (debug) { + fprintf(where,"t_rcvudata, errno %d, t_errno %d", + errno, + t_errno); + fprintf(where," after %d messages\n",messages_recvd); + fprintf(where,"addrmax %d addrlen %d addrbuf %x\n", + unitdata.addr.maxlen, + unitdata.addr.len, + unitdata.addr.buf); + fprintf(where,"optmax %d optlen %d optbuf %x\n", + unitdata.opt.maxlen, + unitdata.opt.len, + unitdata.opt.buf); + fprintf(where,"udatamax %d udatalen %d udatabuf %x\n", + unitdata.udata.maxlen, + unitdata.udata.len, + unitdata.udata.buf); + fflush(where); + } +#endif /* RAJ_DEBUG */ + if (t_rcvudata(s_data, + &unitdata, + &flags) != 0) { + if (errno == TNODATA) { + continue; + } + if (errno != EINTR) { + netperf_response.content.serv_errno = t_errno; + send_response(); + exit(1); + } + break; + } + messages_recvd++; + recv_ring = recv_ring->next; + unitdata.udata.buf = recv_ring->buffer_ptr; + } + + if (debug) { + fprintf(where,"recv_xti_udp_stream: got %d messages.\n",messages_recvd); + fflush(where); + } + + + /* The loop now exits due timer or < send_size bytes received. */ + + cpu_stop(xti_udp_stream_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended on a timer, subtract the PAD_TIME */ + elapsed_time -= (float)PAD_TIME; + } + else { + stop_timer(); + } + + if (debug) { + fprintf(where,"recv_xti_udp_stream: test ended in %f seconds.\n",elapsed_time); + fflush(where); + } + + bytes_received = (messages_recvd * message_size); + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_xti_udp_stream: got %d bytes\n", + bytes_received); + fflush(where); + } + + netperf_response.content.response_type = XTI_UDP_STREAM_RESULTS; + xti_udp_stream_results->bytes_received = bytes_received; + xti_udp_stream_results->messages_recvd = messages_recvd; + xti_udp_stream_results->elapsed_time = elapsed_time; + xti_udp_stream_results->cpu_method = cpu_method; + if (xti_udp_stream_request->measure_cpu) { + xti_udp_stream_results->cpu_util = calc_cpu_util(elapsed_time); + } + else { + xti_udp_stream_results->cpu_util = -1.0; + } + + if (debug > 1) { + fprintf(where, + "recv_xti_udp_stream: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +void send_xti_udp_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %c %% %c us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f %c\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + float elapsed_time; + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct t_bind bind_req, bind_resp; + struct t_unitdata unitdata; + struct t_unitdata send_unitdata; + struct t_unitdata recv_unitdata; + int flags = 0; + + int len; + int nummessages; + SOCKET send_socket; + int trans_remaining; + int bytes_xferd; + + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct hostent *hp; + struct sockaddr_in server, myaddr_in; + unsigned int addr; + int addrlen; + + struct xti_udp_rr_request_struct *xti_udp_rr_request; + struct xti_udp_rr_response_struct *xti_udp_rr_response; + struct xti_udp_rr_results_struct *xti_udp_rr_result; + +#ifdef WANT_INTERVALS + int interval_count; + sigset_t signal_set; +#endif /* WANT_INTERVALS */ + + xti_udp_rr_request = + (struct xti_udp_rr_request_struct *)netperf_request.content.test_specific_data; + xti_udp_rr_response = + (struct xti_udp_rr_response_struct *)netperf_response.content.test_specific_data; + xti_udp_rr_result = + (struct xti_udp_rr_results_struct *)netperf_response.content.test_specific_data; + +#ifdef WANT_HISTOGRAM + time_hist = HIST_new(); +#endif + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + bzero((char *)&server, + sizeof(server)); + + /* it would seem that while HP-UX will allow an IP address (as a */ + /* string) in a call to gethostbyname, other, less enlightened */ + /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */ + /* order changed to check for IP address first. raj 7/96 */ + + if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) { + /* it was not an IP address, try it as a name */ + if ((hp = gethostbyname(remote_host)) == NULL) { + /* we have no idea what it is */ + fprintf(where, + "establish_control: could not resolve the destination %s\n", + remote_host); + fflush(where); + exit(1); + } + else { + /* it was a valid remote_host */ + bcopy(hp->h_addr, + (char *)&server.sin_addr, + hp->h_length); + server.sin_family = hp->h_addrtype; + } + } + else { + /* it was a valid IP address */ + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + } + + if ( print_headers ) { + fprintf(where,"XTI UDP REQUEST/RESPONSE TEST"); + fprintf(where," to %s", remote_host); + if (iteration_max > 1) { + fprintf(where, + " : +/-%3.1f%% @ %2d%% conf.", + interval/0.02, + confidence_level); + } + if (loc_sndavoid || + loc_rcvavoid || + rem_sndavoid || + rem_rcvavoid) { + fprintf(where," : copy avoidance"); + } +#ifdef WANT_HISTOGRAM + fprintf(where," : histogram"); +#endif /* WANT_HISTOGRAM */ +#ifdef WANT_INTERVALS + fprintf(where," : interval"); +#endif /* WANT_INTERVALS */ +#ifdef DIRTY + fprintf(where," : dirty data"); +#endif /* DIRTY */ + fprintf(where,"\n"); + } + + /* initialize a few counters */ + + send_ring = NULL; + recv_ring = NULL; + nummessages = 0; + bytes_xferd = 0; + times_up = 0; + confidence_iteration = 1; + init_stat(); + + + /* we have a great-big while loop which controls the number of times */ + /* we run a particular test. this is for the calculation of a */ + /* confidence interval (I really should have stayed awake during */ + /* probstats :). If the user did not request confidence measurement */ + /* (no confidence is the default) then we will only go though the */ + /* loop once. the confidence stuff originates from the folks at IBM */ + + while (((confidence < 0) && (confidence_iteration < iteration_max)) || + (confidence_iteration <= iteration_min)) { + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + trans_remaining = 0; + + /* set-up the data buffers with the requested alignment and offset */ + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + if (send_ring == NULL) { + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + } + + if (recv_ring == NULL) { + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + } + + /* since we are going to call t_rcvudata() instead of t_rcv() we */ + /* need to init the unitdata structure raj 8/95 */ + + memset (&recv_unitdata, 0, sizeof(recv_unitdata)); + recv_unitdata.addr.maxlen = sizeof(struct sockaddr_in); + recv_unitdata.addr.len = sizeof(struct sockaddr_in); + recv_unitdata.addr.buf = (char *)&server; + + recv_unitdata.opt.maxlen = 0; + recv_unitdata.opt.len = 0; + recv_unitdata.opt.buf = NULL; + + recv_unitdata.udata.maxlen = rsp_size; + recv_unitdata.udata.len = rsp_size; + recv_unitdata.udata.buf = recv_ring->buffer_ptr; + + /* since we are going to call t_sndudata() instead of t_snd() we */ + /* need to init the unitdata structure raj 8/95 */ + + memset (&send_unitdata, 0, sizeof(send_unitdata)); + send_unitdata.addr.maxlen = sizeof(struct sockaddr_in); + send_unitdata.addr.len = sizeof(struct sockaddr_in); + send_unitdata.addr.buf = (char *)&server; + + send_unitdata.opt.maxlen = 0; + send_unitdata.opt.len = 0; + send_unitdata.opt.buf = NULL; + + send_unitdata.udata.maxlen = req_size; + send_unitdata.udata.len = req_size; + send_unitdata.udata.buf = send_ring->buffer_ptr; + + /*set up the data socket */ + send_socket = create_xti_endpoint(loc_xti_device); + + if (send_socket == INVALID_SOCKET){ + perror("netperf: send_xti_udp_rr: udp rr data socket"); + exit(1); + } + + if (debug) { + fprintf(where,"send_xti_udp_rr: send_socket obtained...\n"); + } + + /* it would seem that with XTI, there is no implicit bind */ + /* so we have to make a call to t_bind. this is not */ + /* terribly convenient, but I suppose that "standard is better */ + /* than better" :) raj 2/95 */ + + if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) { + t_error("send_xti_tcp_stream: t_bind"); + exit(1); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back. If */ + /* there is no idle counter in the kernel idle loop, the */ + /* local_cpu_rate will be set to -1. */ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_XTI_UDP_RR; + xti_udp_rr_request->recv_buf_size = rsr_size; + xti_udp_rr_request->send_buf_size = rss_size; + xti_udp_rr_request->recv_alignment = remote_recv_align; + xti_udp_rr_request->recv_offset = remote_recv_offset; + xti_udp_rr_request->send_alignment = remote_send_align; + xti_udp_rr_request->send_offset = remote_send_offset; + xti_udp_rr_request->request_size = req_size; + xti_udp_rr_request->response_size = rsp_size; + xti_udp_rr_request->measure_cpu = remote_cpu_usage; + xti_udp_rr_request->cpu_rate = remote_cpu_rate; + xti_udp_rr_request->so_rcvavoid = rem_rcvavoid; + xti_udp_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + xti_udp_rr_request->test_length = test_time; + } + else { + xti_udp_rr_request->test_length = test_trans * -1; + } + + strcpy(xti_udp_rr_request->xti_device, rem_xti_device); + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I didn't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_udp_rr_request->xti_device; + lastword = initword + ((strlen(rem_xti_device) + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = ntohl(*charword); + } + } +#endif /* __alpha */ + + if (debug > 1) { + fprintf(where,"netperf: send_xti_udp_rr: requesting UDP r/r test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right*/ + /* after the connect returns. The remote will grab the counter right*/ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the UDP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote listen done.\n"); + rsr_size = xti_udp_rr_response->recv_buf_size; + rss_size = xti_udp_rr_response->send_buf_size; + remote_cpu_usage = xti_udp_rr_response->measure_cpu; + remote_cpu_rate = xti_udp_rr_response->cpu_rate; + /* port numbers in proper order */ + server.sin_port = (short)xti_udp_rr_response->data_port_number; + server.sin_port = htons(server.sin_port); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* Data Socket set-up is finished. If there were problems, either the */ + /* connect would have failed, or the previous response would have */ + /* indicated a problem. I failed to see the value of the extra */ + /* message after the accept on the remote. If it failed, we'll see it */ + /* here. If it didn't, we might as well start pumping data. */ + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + +#ifdef WANT_INTERVALS + if ((interval_burst) || (demo_mode)) { + /* zero means that we never pause, so we never should need the */ + /* interval timer, unless we are in demo_mode */ + start_itimer(interval_wate); + } + interval_count = interval_burst; + /* get the signal set for the call to sigsuspend */ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { + fprintf(where, + "send_xti_udp_rr: unable to get sigmask errno %d\n", + errno); + fflush(where); + exit(1); + } +#endif /* WANT_INTERVALS */ + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return */ + /* false. When the test is controlled by byte count, the time test */ + /* will always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think */ + /* I just arbitrarily decrement trans_remaining for the timed */ + /* test, but will not do that just yet... One other question is */ + /* whether or not the send buffer and the receive buffer should be */ + /* the same buffer. */ + + while ((!times_up) || (trans_remaining > 0)) { + /* send the request */ +#ifdef WANT_HISTOGRAM + HIST_timestamp(&time_one); +#endif + if((t_sndudata(send_socket, + &send_unitdata)) != 0) { + if (errno == EINTR) { + /* We likely hit */ + /* test-end time. */ + break; + } + fprintf(where, + "send_xti_udp_rr: t_sndudata: errno %d t_errno %d t_look 0x%.4x\n", + errno, + t_errno, + t_look(send_socket)); + fflush(where); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response. with UDP we will get it all, or nothing */ + + if((t_rcvudata(send_socket, + &recv_unitdata, + &flags)) != 0) { + if (errno == TNODATA) { + continue; + } + if (errno == EINTR) { + /* Again, we have likely hit test-end time */ + break; + } + fprintf(where, + "send_xti_udp_rr: t_rcvudata: errno %d t_errno %d t_look 0x%x\n", + errno, + t_errno, + t_look(send_socket)); + fprintf(where, + "recv_unitdata.udata.buf %x\n",recv_unitdata.udata.buf); + fprintf(where, + "recv_unitdata.udata.maxlen %x\n",recv_unitdata.udata.maxlen); + fprintf(where, + "recv_unitdata.udata.len %x\n",recv_unitdata.udata.len); + fprintf(where, + "recv_unitdata.addr.buf %x\n",recv_unitdata.addr.buf); + fprintf(where, + "recv_unitdata.addr.maxlen %x\n",recv_unitdata.addr.maxlen); + fprintf(where, + "recv_unitdata.addr.len %x\n",recv_unitdata.addr.len); + fflush(where); + exit(1); + } + recv_ring = recv_ring->next; + +#ifdef WANT_HISTOGRAM + HIST_timestamp(&time_two); + HIST_add(time_hist,delta_micro(&time_one,&time_two)); + + /* at this point, we may wish to sleep for some period of */ + /* time, so we see how long that last transaction just took, */ + /* and sleep for the difference of that and the interval. We */ + /* will not sleep if the time would be less than a */ + /* millisecond. */ +#endif +#ifdef WANT_INTERVALS + if (demo_mode) { + units_this_tick += 1; + } + /* in this case, the interval count is the count-down couter */ + /* to decide to sleep for a little bit */ + if ((interval_burst) && (--interval_count == 0)) { + /* call sigsuspend and wait for the interval timer to get us */ + /* out */ + if (debug) { + fprintf(where,"about to suspend\n"); + fflush(where); + } + if (sigsuspend(&signal_set) == EFAULT) { + fprintf(where, + "send_xti_udp_rr: fault with signal set!\n"); + fflush(where); + exit(1); + } + interval_count = interval_burst; + } +#endif /* WANT_INTERVALS */ + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + if ((nummessages % 100) == 0) { + fprintf(where,"Transaction %d completed\n",nummessages); + fflush(where); + } + } + + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ + /* measured? how long */ + /* did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If */ + /* it wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the */ + /* future, we may want to include a calculation of the thruput */ + /* measured by the remote, but it should be the case that for a */ + /* UDP rr test, that the two numbers should be *very* close... */ + /* We calculate bytes_sent regardless of the way the test length */ + /* was controlled. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = nummessages / elapsed_time; + + if (local_cpu_usage || remote_cpu_usage) { + + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) Of course, some of the */ + /* information might be bogus because there was no idle counter */ + /* in the kernel(s). We need to make a note of this for the */ + /* user's benefit by placing a code for the metod used in the */ + /* test banner */ + + if (local_cpu_usage) { + local_cpu_utilization = calc_cpu_util(0.0); + + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + remote_cpu_utilization = xti_udp_rr_result->cpu_util; + + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + xti_udp_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + } + else { + /* we were not measuring cpu, for the confidence stuff, we */ + /* should make it -1.0 */ + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* at this point, we want to calculate the confidence information. */ + /* if debugging is on, calculate_confidence will print-out the */ + /* parameters we pass it */ + + calculate_confidence(confidence_iteration, + elapsed_time, + thruput, + local_cpu_utilization, + remote_cpu_utilization, + local_service_demand, + remote_service_demand); + + + confidence_iteration++; + + /* we are done with the socket */ + t_close(send_socket); + } + + /* at this point, we have made all the iterations we are going to */ + /* make. */ + retrieve_confident_values(&elapsed_time, + &thruput, + &local_cpu_utilization, + &remote_cpu_utilization, + &local_service_demand, + &remote_service_demand); + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + if (confidence < 0) { + /* we did not hit confidence, but were we asked to look for it? */ + if (iteration_max > 1) { + display_confidence(); + } + } + + if (local_cpu_usage || remote_cpu_usage) { + local_cpu_method = format_cpu_method(cpu_method); + remote_cpu_method = format_cpu_method(xti_udp_rr_result->cpu_method); + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand, + local_cpu_method); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand, + remote_cpu_method); + } + break; + case 1: + case 2: + if (print_headers) { + fprintf(where, + cpu_title, + local_cpu_method, + remote_cpu_method); + } + + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + case 2: + if (print_headers) { + fprintf(where,tput_title,format_units()); + } + + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + fflush(where); + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + /* how to handle the verbose information in the presence of */ + /* confidence intervals is yet to be determined... raj 11/94 */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* UDP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + +#ifdef WANT_HISTOGRAM + fprintf(where,"\nHistogram of request/reponse times.\n"); + fflush(where); + HIST_report(time_hist); +#endif /* WANT_HISTOGRAM */ + } +} + + /* this routine implements the receive side (netserver) of a XTI_UDP_RR */ + /* test. */ +void + recv_xti_udp_rr() +{ + + struct ring_elt *recv_ring; + struct ring_elt *send_ring; + + struct t_bind bind_req, bind_resp; + struct t_unitdata send_unitdata; + struct t_unitdata recv_unitdata; + int flags = 0; + + struct sockaddr_in myaddr_in, peeraddr_in; + SOCKET s_data; + int addrlen; + int trans_received; + int trans_remaining; + float elapsed_time; + + struct xti_udp_rr_request_struct *xti_udp_rr_request; + struct xti_udp_rr_response_struct *xti_udp_rr_response; + struct xti_udp_rr_results_struct *xti_udp_rr_results; + + + /* a little variable initialization */ + memset (&myaddr_in, 0, sizeof(struct sockaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = 0; + memset (&peeraddr_in, 0, sizeof(struct sockaddr_in)); + + /* and some not so paranoid :) */ + xti_udp_rr_request = + (struct xti_udp_rr_request_struct *)netperf_request.content.test_specific_data; + xti_udp_rr_response = + (struct xti_udp_rr_response_struct *)netperf_response.content.test_specific_data; + xti_udp_rr_results = + (struct xti_udp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_xti_udp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_xti_udp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = XTI_UDP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_xti_udp_rr: the response type is set...\n"); + fflush(where); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where,"recv_xti_udp_rr: requested recv alignment of %d offset %d\n", + xti_udp_rr_request->recv_alignment, + xti_udp_rr_request->recv_offset); + fprintf(where,"recv_xti_udp_rr: requested send alignment of %d offset %d\n", + xti_udp_rr_request->send_alignment, + xti_udp_rr_request->send_offset); + fflush(where); + } + + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + recv_ring = allocate_buffer_ring(recv_width, + xti_udp_rr_request->request_size, + xti_udp_rr_request->recv_alignment, + xti_udp_rr_request->recv_offset); + + send_ring = allocate_buffer_ring(send_width, + xti_udp_rr_request->response_size, + xti_udp_rr_request->send_alignment, + xti_udp_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_xti_udp_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* create_xti_endpoint expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size = xti_udp_rr_request->send_buf_size; + lsr_size = xti_udp_rr_request->recv_buf_size; + loc_rcvavoid = xti_udp_rr_request->so_rcvavoid; + loc_sndavoid = xti_udp_rr_request->so_sndavoid; + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_udp_rr_request->xti_device; + lastword = initword + ((xti_udp_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } + +#endif /* __alpha */ + + s_data = create_xti_endpoint(xti_udp_rr_request->xti_device); + + if (s_data == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + + if (debug) { + fprintf(where,"recv_xti_udp_rr: endpoint created...\n"); + fflush(where); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + bind_req.addr.maxlen = sizeof(struct sockaddr_in); + bind_req.addr.len = sizeof(struct sockaddr_in); + bind_req.addr.buf = (char *)&myaddr_in; + bind_req.qlen = 1; + + bind_resp.addr.maxlen = sizeof(struct sockaddr_in); + bind_resp.addr.len = sizeof(struct sockaddr_in); + bind_resp.addr.buf = (char *)&myaddr_in; + bind_resp.qlen = 1; + + if (t_bind(s_data, + &bind_req, + &bind_resp) == SOCKET_ERROR) { + if (debug) { + fprintf(where, + "recv_xti_udp_rr: t_bind failed, t_errno %d errno %d\n", + t_errno, + errno); + fflush(where); + } + + netperf_response.content.serv_errno = t_errno; + send_response(); + + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_udp_rr: endpoint bound to port %d...\n", + ntohs(myaddr_in.sin_port)); + fflush(where); + } + + xti_udp_rr_response->test_length = + xti_udp_rr_request->test_length; + + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + xti_udp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + fprintf(where,"recv port number %d\n",myaddr_in.sin_port); + fflush(where); + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + xti_udp_rr_response->cpu_rate = 0.0; /* assume no cpu */ + xti_udp_rr_response->measure_cpu = 0; + if (xti_udp_rr_request->measure_cpu) { + xti_udp_rr_response->measure_cpu = 1; + xti_udp_rr_response->cpu_rate = + calibrate_local_cpu(xti_udp_rr_request->cpu_rate); + } + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + xti_udp_rr_response->send_buf_size = lss_size; + xti_udp_rr_response->recv_buf_size = lsr_size; + xti_udp_rr_response->so_rcvavoid = loc_rcvavoid; + xti_udp_rr_response->so_sndavoid = loc_sndavoid; + + /* since we are going to call t_rcvudata() instead of t_rcv() we */ + /* need to init the unitdata structure raj 3/95 */ + + memset (&recv_unitdata, 0, sizeof(recv_unitdata)); + recv_unitdata.addr.maxlen = sizeof(struct sockaddr_in); + recv_unitdata.addr.len = sizeof(struct sockaddr_in); + recv_unitdata.addr.buf = (char *)&peeraddr_in; + + recv_unitdata.opt.maxlen = 0; + recv_unitdata.opt.len = 0; + recv_unitdata.opt.buf = NULL; + + recv_unitdata.udata.maxlen = xti_udp_rr_request->request_size; + recv_unitdata.udata.len = xti_udp_rr_request->request_size; + recv_unitdata.udata.buf = recv_ring->buffer_ptr; + + /* since we are going to call t_sndudata() instead of t_snd() we */ + /* need to init the unitdata structure raj 8/95 */ + + memset (&send_unitdata, 0, sizeof(send_unitdata)); + send_unitdata.addr.maxlen = sizeof(struct sockaddr_in); + send_unitdata.addr.len = sizeof(struct sockaddr_in); + send_unitdata.addr.buf = (char *)&peeraddr_in; + + send_unitdata.opt.maxlen = 0; + send_unitdata.opt.len = 0; + send_unitdata.opt.buf = NULL; + + send_unitdata.udata.maxlen = xti_udp_rr_request->response_size; + send_unitdata.udata.len = xti_udp_rr_request->response_size; + send_unitdata.udata.buf = send_ring->buffer_ptr; + + send_response(); + + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(xti_udp_rr_request->measure_cpu); + + if (xti_udp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(xti_udp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = xti_udp_rr_request->test_length * -1; + } + + addrlen = sizeof(peeraddr_in); + bzero((char *)&peeraddr_in, addrlen); + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + /* receive the request from the other side */ + if (t_rcvudata(s_data, + &recv_unitdata, + &flags) != 0) { + if (errno == TNODATA) { + continue; + } + if (errno == EINTR) { + /* we must have hit the end of test time. */ + break; + } + if (debug) { + fprintf(where, + "recv_xti_udp_rr: t_rcvudata failed, t_errno %d errno %d\n", + t_errno, + errno); + fflush(where); + } + netperf_response.content.serv_errno = t_errno; + send_response(); + exit(1); + } + recv_ring = recv_ring->next; + recv_unitdata.udata.buf = recv_ring->buffer_ptr; + + /* Now, send the response to the remote */ + if (t_sndudata(s_data, + &send_unitdata) != 0) { + if (errno == EINTR) { + /* we have hit end of test time. */ + break; + } + if (debug) { + fprintf(where, + "recv_xti_udp_rr: t_sndudata failed, t_errno %d errno %d\n", + t_errno, + errno); + fflush(where); + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + send_ring = send_ring->next; + send_unitdata.udata.buf = send_ring->buffer_ptr; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_xti_udp_rr: Transaction %d complete.\n", + trans_received); + fflush(where); + } + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(xti_udp_rr_request->measure_cpu,&elapsed_time); + + if (times_up) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_xti_udp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + xti_udp_rr_results->bytes_received = (trans_received * + (xti_udp_rr_request->request_size + + xti_udp_rr_request->response_size)); + xti_udp_rr_results->trans_received = trans_received; + xti_udp_rr_results->elapsed_time = elapsed_time; + xti_udp_rr_results->cpu_method = cpu_method; + if (xti_udp_rr_request->measure_cpu) { + xti_udp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_xti_udp_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + + /* we are done with the socket now */ + close(s_data); + +} + + /* this routine implements the receive (netserver) side of a XTI_TCP_RR */ + /* test */ +void +recv_xti_tcp_rr() +{ + + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + + struct sockaddr_in myaddr_in, peeraddr_in; + struct t_bind bind_req, bind_resp; + struct t_call call_req; + + SOCKET s_listen,s_data; + int addrlen; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct xti_tcp_rr_request_struct *xti_tcp_rr_request; + struct xti_tcp_rr_response_struct *xti_tcp_rr_response; + struct xti_tcp_rr_results_struct *xti_tcp_rr_results; + + xti_tcp_rr_request = + (struct xti_tcp_rr_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_rr_response = + (struct xti_tcp_rr_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_rr_results = + (struct xti_tcp_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_xti_tcp_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = XTI_TCP_RR_RESPONSE; + + if (debug) { + fprintf(where,"recv_xti_tcp_rr: the response type is set...\n"); + fflush(where); + } + + /* allocate the recv and send rings with the requested alignments */ + /* and offsets. raj 7/94 */ + if (debug) { + fprintf(where,"recv_xti_tcp_rr: requested recv alignment of %d offset %d\n", + xti_tcp_rr_request->recv_alignment, + xti_tcp_rr_request->recv_offset); + fprintf(where,"recv_xti_tcp_rr: requested send alignment of %d offset %d\n", + xti_tcp_rr_request->send_alignment, + xti_tcp_rr_request->send_offset); + fflush(where); + } + + /* at some point, these need to come to us from the remote system */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + xti_tcp_rr_request->response_size, + xti_tcp_rr_request->send_alignment, + xti_tcp_rr_request->send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + xti_tcp_rr_request->request_size, + xti_tcp_rr_request->recv_alignment, + xti_tcp_rr_request->recv_offset); + + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = 0; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_xti_endpoint expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size = xti_tcp_rr_request->send_buf_size; + lsr_size = xti_tcp_rr_request->recv_buf_size; + loc_nodelay = xti_tcp_rr_request->no_delay; + loc_rcvavoid = xti_tcp_rr_request->so_rcvavoid; + loc_sndavoid = xti_tcp_rr_request->so_sndavoid; + +#ifdef __alpha + + /* ok - even on a DEC box, strings are strings. I din't really want */ + /* to ntohl the words of a string. since I don't want to teach the */ + /* send_ and recv_ _request and _response routines about the types, */ + /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ + /* solution would be to use XDR, but I am still leary of being able */ + /* to find XDR libs on all platforms I want running netperf. raj */ + { + int *charword; + int *initword; + int *lastword; + + initword = (int *) xti_tcp_rr_request->xti_device; + lastword = initword + ((xti_tcp_rr_request->dev_name_len + 3) / 4); + + for (charword = initword; + charword < lastword; + charword++) { + + *charword = htonl(*charword); + } + } + +#endif /* __alpha */ + + s_listen = create_xti_endpoint(xti_tcp_rr_request->xti_device); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + bind_req.addr.maxlen = sizeof(struct sockaddr_in); + bind_req.addr.len = sizeof(struct sockaddr_in); + bind_req.addr.buf = (char *)&myaddr_in; + bind_req.qlen = 1; + + bind_resp.addr.maxlen = sizeof(struct sockaddr_in); + bind_resp.addr.len = sizeof(struct sockaddr_in); + bind_resp.addr.buf = (char *)&myaddr_in; + bind_resp.qlen = 1; + + if (t_bind(s_listen, + &bind_req, + &bind_resp) == SOCKET_ERROR) { + netperf_response.content.serv_errno = t_errno; + close(s_listen); + send_response(); + + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_rr: t_bind complete port %d\n", + ntohs(myaddr_in.sin_port)); + fflush(where); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + xti_tcp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + xti_tcp_rr_response->cpu_rate = 0.0; /* assume no cpu */ + xti_tcp_rr_response->measure_cpu = 0; + + if (xti_tcp_rr_request->measure_cpu) { + xti_tcp_rr_response->measure_cpu = 1; + xti_tcp_rr_response->cpu_rate = calibrate_local_cpu(xti_tcp_rr_request->cpu_rate); + } + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + xti_tcp_rr_response->send_buf_size = lss_size; + xti_tcp_rr_response->recv_buf_size = lsr_size; + xti_tcp_rr_response->no_delay = loc_nodelay; + xti_tcp_rr_response->so_rcvavoid = loc_rcvavoid; + xti_tcp_rr_response->so_sndavoid = loc_sndavoid; + xti_tcp_rr_response->test_length = xti_tcp_rr_request->test_length; + send_response(); + + /* Now, let's set-up the socket to listen for connections. for xti, */ + /* the t_listen call is blocking by default - this is different */ + /* semantics from BSD - probably has to do with being able to reject */ + /* a call before an accept */ + call_req.addr.maxlen = sizeof(struct sockaddr_in); + call_req.addr.len = sizeof(struct sockaddr_in); + call_req.addr.buf = (char *)&peeraddr_in; + call_req.opt.maxlen = 0; + call_req.opt.len = 0; + call_req.opt.buf = NULL; + call_req.udata.maxlen= 0; + call_req.udata.len = 0; + call_req.udata.buf = 0; + + if (t_listen(s_listen, &call_req) == -1) { + fprintf(where, + "recv_xti_tcp_rr: t_listen: errno %d t_errno %d\n", + errno, + t_errno); + fflush(where); + netperf_response.content.serv_errno = t_errno; + close(s_listen); + send_response(); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_rr: t_listen complete t_look 0x%.4x\n", + t_look(s_listen)); + fflush(where); + } + + /* now just rubber stamp the thing. we want to use the same fd? so */ + /* we will just equate s_data with s_listen. this seems a little */ + /* hokey to me, but then I'm a BSD biggot still. raj 2/95 */ + s_data = s_listen; + if (t_accept(s_listen, + s_data, + &call_req) == -1) { + fprintf(where, + "recv_xti_tcp_rr: t_accept: errno %d t_errno %d\n", + errno, + t_errno); + fflush(where); + close(s_listen); + exit(1); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_rr: t_accept complete t_look 0x%.4x", + t_look(s_data)); + fprintf(where, + " remote is %s port %d\n", + inet_ntoa(*(struct in_addr *)&peeraddr_in.sin_addr), + ntohs(peeraddr_in.sin_port)); + fflush(where); + } + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(xti_tcp_rr_request->measure_cpu); + + if (xti_tcp_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(xti_tcp_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = xti_tcp_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + temp_message_ptr = recv_ring->buffer_ptr; + request_bytes_remaining = xti_tcp_rr_request->request_size; + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=t_rcv(s_data, + temp_message_ptr, + request_bytes_remaining, + &xti_flags)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + fprintf(where, + "recv_xti_tcp_rr: t_rcv: errno %d t_errno %d len %d", + errno, + t_errno, + request_bytes_recvd); + fprintf(where, + " t_look 0x%x", + t_look(s_data)); + fflush(where); + netperf_response.content.serv_errno = t_errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + recv_ring = recv_ring->next; + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + if (debug) { + fprintf(where,"yo5\n"); + fflush(where); + } + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=t_snd(s_data, + send_ring->buffer_ptr, + xti_tcp_rr_request->response_size, + 0)) == -1) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + if (debug) { + fprintf(where,"yo6\n"); + fflush(where); + } + break; + } + fprintf(where, + "recv_xti_tcp_rr: t_rcv: errno %d t_errno %d len %d", + errno, + t_errno, + bytes_sent); + fprintf(where, + " t_look 0x%x", + t_look(s_data)); + fflush(where); + netperf_response.content.serv_errno = t_errno; + send_response(); + exit(1); + } + + send_ring = send_ring->next; + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(xti_tcp_rr_request->measure_cpu,&elapsed_time); + + stop_timer(); /* this is probably unnecessary, but it shouldn't hurt */ + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_xti_tcp_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + xti_tcp_rr_results->bytes_received = (trans_received * + (xti_tcp_rr_request->request_size + + xti_tcp_rr_request->response_size)); + xti_tcp_rr_results->trans_received = trans_received; + xti_tcp_rr_results->elapsed_time = elapsed_time; + xti_tcp_rr_results->cpu_method = cpu_method; + if (xti_tcp_rr_request->measure_cpu) { + xti_tcp_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_rr: test complete, sending results.\n"); + fflush(where); + } + + /* we are done with the socket, free it */ + t_close(s_data); + + send_response(); + +} + + + + /* this test is intended to test the performance of establishing a */ + /* connection, exchanging a request/response pair, and repeating. it */ + /* is expected that this would be a good starting-point for */ + /* comparision of T/TCP with classic TCP for transactional workloads. */ + /* it will also look (can look) much like the communication pattern */ + /* of http for www access. */ + +void +send_xti_tcp_conn_rr(char remote_host[]) +{ + + char *tput_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans.\n\ +Send Recv Size Size Time Rate \n\ +bytes Bytes bytes bytes secs. per sec \n\n"; + + char *tput_fmt_0 = + "%7.2f\n"; + + char *tput_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; + char *tput_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *cpu_title = "\ +Local /Remote\n\ +Socket Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ +Send Recv Size Size Time Rate local remote local remote\n\ +bytes bytes bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; + + char *cpu_fmt_0 = + "%6.3f\n"; + + char *cpu_fmt_1_line_1 = "\ +%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; + + char *cpu_fmt_1_line_2 = "\ +%-6d %-6d\n"; + + char *ksink_fmt = "\ +Alignment Offset\n\ +Local Remote Local Remote\n\ +Send Recv Send Recv\n\ +%5d %5d %5d %5d\n"; + + + int one = 1; + int timed_out = 0; + float elapsed_time; + + int len; + struct ring_elt *send_ring; + struct ring_elt *recv_ring; + char *temp_message_ptr; + int nummessages; + SOCKET send_socket; + int trans_remaining; + double bytes_xferd; + int sock_opt_len = sizeof(int); + int rsp_bytes_left; + int rsp_bytes_recvd; + + float local_cpu_utilization; + float local_service_demand; + float remote_cpu_utilization; + float remote_service_demand; + double thruput; + + struct hostent *hp; + struct sockaddr_in server; + struct sockaddr_in *myaddr; + unsigned int addr; + int myport; + + struct xti_tcp_conn_rr_request_struct *xti_tcp_conn_rr_request; + struct xti_tcp_conn_rr_response_struct *xti_tcp_conn_rr_response; + struct xti_tcp_conn_rr_results_struct *xti_tcp_conn_rr_result; + + xti_tcp_conn_rr_request = + (struct xti_tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_conn_rr_response = + (struct xti_tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_conn_rr_result = + (struct xti_tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data; + + /* since we are now disconnected from the code that established the */ + /* control socket, and since we want to be able to use different */ + /* protocols and such, we are passed the name of the remote host and */ + /* must turn that into the test specific addressing information. */ + + myaddr = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in)); + if (myaddr == NULL) { + printf("malloc(%d) failed!\n", sizeof(struct sockaddr_in)); + exit(1); + } + + bzero((char *)&server, + sizeof(server)); + bzero((char *)myaddr, + sizeof(struct sockaddr_in)); + myaddr->sin_family = AF_INET; + + /* it would seem that while HP-UX will allow an IP address (as a */ + /* string) in a call to gethostbyname, other, less enlightened */ + /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */ + /* order changed to check for IP address first. raj 7/96 */ + + if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) { + /* it was not an IP address, try it as a name */ + if ((hp = gethostbyname(remote_host)) == NULL) { + /* we have no idea what it is */ + fprintf(where, + "establish_control: could not resolve the destination %s\n", + remote_host); + fflush(where); + exit(1); + } + else { + /* it was a valid remote_host */ + bcopy(hp->h_addr, + (char *)&server.sin_addr, + hp->h_length); + server.sin_family = hp->h_addrtype; + } + } + else { + /* it was a valid IP address */ + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + } + + if ( print_headers ) { + fprintf(where,"TCP Connect/Request/Response Test\n"); + if (local_cpu_usage || remote_cpu_usage) + fprintf(where,cpu_title,format_units()); + else + fprintf(where,tput_title,format_units()); + } + + /* initialize a few counters */ + + nummessages = 0; + bytes_xferd = 0.0; + times_up = 0; + + /* set-up the data buffers with the requested alignment and offset */ + if (send_width == 0) send_width = 1; + if (recv_width == 0) recv_width = 1; + + send_ring = allocate_buffer_ring(send_width, + req_size, + local_send_align, + local_send_offset); + + recv_ring = allocate_buffer_ring(recv_width, + rsp_size, + local_recv_align, + local_recv_offset); + + + if (debug) { + fprintf(where,"send_xti_tcp_conn_rr: send_socket obtained...\n"); + } + + /* If the user has requested cpu utilization measurements, we must */ + /* calibrate the cpu(s). We will perform this task within the tests */ + /* themselves. If the user has specified the cpu rate, then */ + /* calibrate_local_cpu will return rather quickly as it will have */ + /* nothing to do. If local_cpu_rate is zero, then we will go through */ + /* all the "normal" calibration stuff and return the rate back.*/ + + if (local_cpu_usage) { + local_cpu_rate = calibrate_local_cpu(local_cpu_rate); + } + + /* Tell the remote end to do a listen. The server alters the socket */ + /* paramters on the other side at this point, hence the reason for */ + /* all the values being passed in the setup message. If the user did */ + /* not specify any of the parameters, they will be passed as 0, which */ + /* will indicate to the remote that no changes beyond the system's */ + /* default should be used. Alignment is the exception, it will */ + /* default to 8, which will be no alignment alterations. */ + + netperf_request.content.request_type = DO_XTI_TCP_CRR; + xti_tcp_conn_rr_request->recv_buf_size = rsr_size; + xti_tcp_conn_rr_request->send_buf_size = rss_size; + xti_tcp_conn_rr_request->recv_alignment = remote_recv_align; + xti_tcp_conn_rr_request->recv_offset = remote_recv_offset; + xti_tcp_conn_rr_request->send_alignment = remote_send_align; + xti_tcp_conn_rr_request->send_offset = remote_send_offset; + xti_tcp_conn_rr_request->request_size = req_size; + xti_tcp_conn_rr_request->response_size = rsp_size; + xti_tcp_conn_rr_request->no_delay = rem_nodelay; + xti_tcp_conn_rr_request->measure_cpu = remote_cpu_usage; + xti_tcp_conn_rr_request->cpu_rate = remote_cpu_rate; + xti_tcp_conn_rr_request->so_rcvavoid = rem_rcvavoid; + xti_tcp_conn_rr_request->so_sndavoid = rem_sndavoid; + if (test_time) { + xti_tcp_conn_rr_request->test_length = test_time; + } + else { + xti_tcp_conn_rr_request->test_length = test_trans * -1; + } + + if (debug > 1) { + fprintf(where,"netperf: send_xti_tcp_conn_rr: requesting TCP crr test\n"); + } + + send_request(); + + /* The response from the remote will contain all of the relevant */ + /* socket parameters for this test type. We will put them back into */ + /* the variables here so they can be displayed if desired. The */ + /* remote will have calibrated CPU if necessary, and will have done */ + /* all the needed set-up we will have calibrated the cpu locally */ + /* before sending the request, and will grab the counter value right */ + /* after the connect returns. The remote will grab the counter right */ + /* after the accept call. This saves the hassle of extra messages */ + /* being sent for the TCP tests. */ + + recv_response(); + + if (!netperf_response.content.serv_errno) { + rsr_size = xti_tcp_conn_rr_response->recv_buf_size; + rss_size = xti_tcp_conn_rr_response->send_buf_size; + rem_nodelay = xti_tcp_conn_rr_response->no_delay; + remote_cpu_usage= xti_tcp_conn_rr_response->measure_cpu; + remote_cpu_rate = xti_tcp_conn_rr_response->cpu_rate; + /* make sure that port numbers are in network order */ + server.sin_port = (short)xti_tcp_conn_rr_response->data_port_number; + server.sin_port = htons(server.sin_port); + if (debug) { + fprintf(where,"remote listen done.\n"); + fprintf(where,"remote port is %d\n",ntohs(server.sin_port)); + fflush(where); + } + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* Set-up the test end conditions. For a request/response test, they */ + /* can be either time or transaction based. */ + + if (test_time) { + /* The user wanted to end the test after a period of time. */ + times_up = 0; + trans_remaining = 0; + start_timer(test_time); + } + else { + /* The tester wanted to send a number of bytes. */ + trans_remaining = test_bytes; + times_up = 1; + } + + /* The cpu_start routine will grab the current time and possibly */ + /* value of the idle counter for later use in measuring cpu */ + /* utilization and/or service demand and thruput. */ + + cpu_start(local_cpu_usage); + + /* We use an "OR" to control test execution. When the test is */ + /* controlled by time, the byte count check will always return false. */ + /* When the test is controlled by byte count, the time test will */ + /* always return false. When the test is finished, the whole */ + /* expression will go false and we will stop sending data. I think I */ + /* just arbitrarily decrement trans_remaining for the timed test, but */ + /* will not do that just yet... One other question is whether or not */ + /* the send buffer and the receive buffer should be the same buffer. */ + + /* just for grins, start the port numbers at 65530. this should */ + /* quickly flush-out those broken implementations of TCP which treat */ + /* the port number as a signed 16 bit quantity. */ + myport = 65530; + myaddr->sin_port = htons(myport); + + while ((!times_up) || (trans_remaining > 0)) { + + /* set up the data socket */ + send_socket = create_xti_endpoint(loc_xti_device); + + if (send_socket == INVALID_SOCKET) { + perror("netperf: send_xti_tcp_conn_rr: tcp stream data socket"); + exit(1); + } + + /* we set SO_REUSEADDR on the premis that no unreserved port */ + /* number on the local system is going to be already connected to */ + /* the remote netserver's port number. we might still have a */ + /* problem if there is a port in the unconnected state. In that */ + /* case, we might want to throw-in a goto to the point where we */ + /* increment the port number by one and try again. of course, this */ + /* could lead to a big load of spinning. one thing that I might */ + /* try later is to have the remote actually allocate a couple of */ + /* port numbers and cycle through those as well. depends on if we */ + /* can get through all the unreserved port numbers in less than */ + /* the length of the TIME_WAIT state raj 8/94 */ + one = 1; + if(setsockopt(send_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sock_opt_len) == SOCKET_ERROR) { + perror("netperf: send_xti_tcp_conn_rr: so_reuseaddr"); + exit(1); + } + + /* we want to bind our socket to a particular port number. */ + if (bind(send_socket, + (struct sockaddr *)myaddr, + sizeof(struct sockaddr_in)) == SOCKET_ERROR) { + printf("netperf: send_xti_tcp_conn_rr: tried to bind to port %d\n", + ntohs(myaddr->sin_port)); + perror("netperf: send_xti_tcp_conn_rr: bind"); + exit(1); + } + + /* Connect up to the remote port on the data socket */ + if (connect(send_socket, + (struct sockaddr *)&server, + sizeof(server)) == INVALID_SOCKET){ + if (errno == EINTR) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("netperf: data socket connect failed"); + printf("\tattempted to connect on socket %d to port %d", + send_socket, + ntohs(server.sin_port)); + printf(" from port %d \n",ntohs(myaddr->sin_port)); + exit(1); + } + + /* send the request */ + if((len=send(send_socket, + send_ring->buffer_ptr, + req_size, + 0)) != req_size) { + if (errno == EINTR) { + /* we hit the end of a */ + /* timed test. */ + timed_out = 1; + break; + } + perror("send_xti_tcp_conn_rr: data send error"); + exit(1); + } + send_ring = send_ring->next; + + /* receive the response */ + rsp_bytes_left = rsp_size; + temp_message_ptr = recv_ring->buffer_ptr; + while(rsp_bytes_left > 0) { + if((rsp_bytes_recvd=recv(send_socket, + temp_message_ptr, + rsp_bytes_left, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* We hit the end of a timed test. */ + timed_out = 1; + break; + } + perror("send_xti_tcp_conn_rr: data recv error"); + exit(1); + } + rsp_bytes_left -= rsp_bytes_recvd; + temp_message_ptr += rsp_bytes_recvd; + } + recv_ring = recv_ring->next; + + if (timed_out) { + /* we may have been in a nested while loop - we need */ + /* another call to break. */ + break; + } + + close(send_socket); + + nummessages++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug > 3) { + fprintf(where, + "Transaction %d completed on local port %d\n", + nummessages, + ntohs(myaddr->sin_port)); + fflush(where); + } + +newport: + /* pick a new port number */ + myport = ntohs(myaddr->sin_port); + myport++; + /* we do not want to use the port number that the server is */ + /* sitting at - this would cause us to fail in a loopback test */ + + if (myport == ntohs(server.sin_port)) myport++; + + /* wrap the port number when we get to 65535. NOTE, some broken */ + /* TCP's might treat the port number as a signed 16 bit quantity. */ + /* we aren't interested in testing such broekn implementations :) */ + /* raj 8/94 */ + if (myport == 65535) { + myport = 5000; + } + myaddr->sin_port = htons(myport); + + if (debug) { + if ((myport % 1000) == 0) { + printf("port %d\n",myport); + } + } + + } + + /* this call will always give us the elapsed time for the test, and */ + /* will also store-away the necessaries for cpu utilization */ + + cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ + /* how long did we really run? */ + + /* Get the statistics from the remote end. The remote will have */ + /* calculated service demand and all those interesting things. If it */ + /* wasn't supposed to care, it will return obvious values. */ + + recv_response(); + if (!netperf_response.content.serv_errno) { + if (debug) + fprintf(where,"remote results obtained\n"); + } + else { + Set_errno(netperf_response.content.serv_errno); + perror("netperf: remote error"); + + exit(1); + } + + /* We now calculate what our thruput was for the test. In the future, */ + /* we may want to include a calculation of the thruput measured by */ + /* the remote, but it should be the case that for a TCP stream test, */ + /* that the two numbers should be *very* close... We calculate */ + /* bytes_sent regardless of the way the test length was controlled. */ + /* If it was time, we needed to, and if it was by bytes, the user may */ + /* have specified a number of bytes that wasn't a multiple of the */ + /* send_size, so we really didn't send what he asked for ;-) We use */ + /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ + /* 1024. A future enhancement *might* be to choose from a couple of */ + /* unit selections. */ + + bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); + thruput = calc_thruput(bytes_xferd); + + if (local_cpu_usage || remote_cpu_usage) { + /* We must now do a little math for service demand and cpu */ + /* utilization for the system(s) */ + /* Of course, some of the information might be bogus because */ + /* there was no idle counter in the kernel(s). We need to make */ + /* a note of this for the user's benefit...*/ + if (local_cpu_usage) { + if (local_cpu_rate == 0.0) { + fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); + fprintf(where,"Local CPU usage numbers based on process information only!\n"); + fflush(where); + } + local_cpu_utilization = calc_cpu_util(0.0); + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + local_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + 0.0, + 0); + } + else { + local_cpu_utilization = -1.0; + local_service_demand = -1.0; + } + + if (remote_cpu_usage) { + if (remote_cpu_rate == 0.0) { + fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); + fprintf(where,"Remote CPU usage numbers based on process information only!\n"); + fflush(where); + } + remote_cpu_utilization = xti_tcp_conn_rr_result->cpu_util; + /* since calc_service demand is doing ms/Kunit we will */ + /* multiply the number of transaction by 1024 to get */ + /* "good" numbers */ + remote_service_demand = calc_service_demand((double) nummessages*1024, + 0.0, + remote_cpu_utilization, + xti_tcp_conn_rr_result->num_cpus); + } + else { + remote_cpu_utilization = -1.0; + remote_service_demand = -1.0; + } + + /* We are now ready to print all the information. If the user */ + /* has specified zero-level verbosity, we will just print the */ + /* local service demand, or the remote service demand. If the */ + /* user has requested verbosity level 1, he will get the basic */ + /* "streamperf" numbers. If the user has specified a verbosity */ + /* of greater than 1, we will display a veritable plethora of */ + /* background information from outside of this block as it it */ + /* not cpu_measurement specific... */ + + switch (verbosity) { + case 0: + if (local_cpu_usage) { + fprintf(where, + cpu_fmt_0, + local_service_demand); + } + else { + fprintf(where, + cpu_fmt_0, + remote_service_demand); + } + break; + case 1: + fprintf(where, + cpu_fmt_1_line_1, /* the format string */ + lss_size, /* local sendbuf size */ + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* guess */ + elapsed_time, /* how long was the test */ + nummessages/elapsed_time, + local_cpu_utilization, /* local cpu */ + remote_cpu_utilization, /* remote cpu */ + local_service_demand, /* local service demand */ + remote_service_demand); /* remote service demand */ + fprintf(where, + cpu_fmt_1_line_2, + rss_size, + rsr_size); + break; + } + } + else { + /* The tester did not wish to measure service demand. */ + switch (verbosity) { + case 0: + fprintf(where, + tput_fmt_0, + nummessages/elapsed_time); + break; + case 1: + fprintf(where, + tput_fmt_1_line_1, /* the format string */ + lss_size, + lsr_size, + req_size, /* how large were the requests */ + rsp_size, /* how large were the responses */ + elapsed_time, /* how long did it take */ + nummessages/elapsed_time); + fprintf(where, + tput_fmt_1_line_2, + rss_size, /* remote recvbuf size */ + rsr_size); + + break; + } + } + + /* it would be a good thing to include information about some of the */ + /* other parameters that may have been set for this test, but at the */ + /* moment, I do not wish to figure-out all the formatting, so I will */ + /* just put this comment here to help remind me that it is something */ + /* that should be done at a later time. */ + + if (verbosity > 1) { + /* The user wanted to know it all, so we will give it to him. */ + /* This information will include as much as we can find about */ + /* TCP statistics, the alignments of the sends and receives */ + /* and all that sort of rot... */ + + fprintf(where, + ksink_fmt); + } + +} + + +void +recv_xti_tcp_conn_rr() +{ + + char *message; + struct sockaddr_in myaddr_in, + peeraddr_in; + SOCKET s_listen,s_data; + int addrlen; + char *recv_message_ptr; + char *send_message_ptr; + char *temp_message_ptr; + int trans_received; + int trans_remaining; + int bytes_sent; + int request_bytes_recvd; + int request_bytes_remaining; + int timed_out = 0; + float elapsed_time; + + struct xti_tcp_conn_rr_request_struct *xti_tcp_conn_rr_request; + struct xti_tcp_conn_rr_response_struct *xti_tcp_conn_rr_response; + struct xti_tcp_conn_rr_results_struct *xti_tcp_conn_rr_results; + + xti_tcp_conn_rr_request = + (struct xti_tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data; + xti_tcp_conn_rr_response = + (struct xti_tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data; + xti_tcp_conn_rr_results = + (struct xti_tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data; + + if (debug) { + fprintf(where,"netserver: recv_xti_tcp_conn_rr: entered...\n"); + fflush(where); + } + + /* We want to set-up the listen socket with all the desired */ + /* parameters and then let the initiator know that all is ready. If */ + /* socket size defaults are to be used, then the initiator will have */ + /* sent us 0's. If the socket sizes cannot be changed, then we will */ + /* send-back what they are. If that information cannot be determined, */ + /* then we send-back -1's for the sizes. If things go wrong for any */ + /* reason, we will drop back ten yards and punt. */ + + /* If anything goes wrong, we want the remote to know about it. It */ + /* would be best if the error that the remote reports to the user is */ + /* the actual error we encountered, rather than some bogus unexpected */ + /* response type message. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_conn_rr: setting the response type...\n"); + fflush(where); + } + + netperf_response.content.response_type = XTI_TCP_CRR_RESPONSE; + + if (debug) { + fprintf(where,"recv_xti_tcp_conn_rr: the response type is set...\n"); + fflush(where); + } + + /* set-up the data buffer with the requested alignment and offset */ + message = (char *)malloc(DATABUFFERLEN); + if (message == NULL) { + printf("malloc(%d) failed!\n", DATABUFFERLEN); + exit(1); + } + + /* We now alter the message_ptr variables to be at the desired */ + /* alignments with the desired offsets. */ + + if (debug) { + fprintf(where, + "recv_xti_tcp_conn_rr: requested recv alignment of %d offset %d\n", + xti_tcp_conn_rr_request->recv_alignment, + xti_tcp_conn_rr_request->recv_offset); + fprintf(where, + "recv_xti_tcp_conn_rr: requested send alignment of %d offset %d\n", + xti_tcp_conn_rr_request->send_alignment, + xti_tcp_conn_rr_request->send_offset); + fflush(where); + } + + recv_message_ptr = ALIGN_BUFFER(message, xti_tcp_conn_rr_request->recv_alignment, xti_tcp_conn_rr_request->recv_offset); + + send_message_ptr = ALIGN_BUFFER(message, xti_tcp_conn_rr_request->send_alignment, xti_tcp_conn_rr_request->send_offset); + + if (debug) { + fprintf(where,"recv_xti_tcp_conn_rr: receive alignment and offset set...\n"); + fflush(where); + } + + /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ + /* can put in OUR values !-) At some point, we may want to nail this */ + /* socket to a particular network-level address, but for now, */ + /* INADDR_ANY should be just fine. */ + + bzero((char *)&myaddr_in, + sizeof(myaddr_in)); + myaddr_in.sin_family = AF_INET; + myaddr_in.sin_addr.s_addr = INADDR_ANY; + myaddr_in.sin_port = 0; + + /* Grab a socket to listen on, and then listen on it. */ + + if (debug) { + fprintf(where,"recv_xti_tcp_conn_rr: grabbing a socket...\n"); + fflush(where); + } + + /* create_xti_endpoint expects to find some things in the global */ + /* variables, so set the globals based on the values in the request. */ + /* once the socket has been created, we will set the response values */ + /* based on the updated value of those globals. raj 7/94 */ + lss_size = xti_tcp_conn_rr_request->send_buf_size; + lsr_size = xti_tcp_conn_rr_request->recv_buf_size; + loc_nodelay = xti_tcp_conn_rr_request->no_delay; + loc_rcvavoid = xti_tcp_conn_rr_request->so_rcvavoid; + loc_sndavoid = xti_tcp_conn_rr_request->so_sndavoid; + + s_listen = create_xti_endpoint(loc_xti_device); + + if (s_listen == INVALID_SOCKET) { + netperf_response.content.serv_errno = errno; + send_response(); + if (debug) { + fprintf(where,"could not create data socket\n"); + fflush(where); + } + exit(1); + } + + /* Let's get an address assigned to this socket so we can tell the */ + /* initiator how to reach the data socket. There may be a desire to */ + /* nail this socket to a specific IP address in a multi-homed, */ + /* multi-connection situation, but for now, we'll ignore the issue */ + /* and concentrate on single connection testing. */ + + if (bind(s_listen, + (struct sockaddr *)&myaddr_in, + sizeof(myaddr_in)) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not bind\n"); + fflush(where); + } + exit(1); + } + + /* Now, let's set-up the socket to listen for connections */ + if (listen(s_listen, 5) == SOCKET_ERROR) { + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not listen\n"); + fflush(where); + } + exit(1); + } + + /* now get the port number assigned by the system */ + addrlen = sizeof(myaddr_in); + if (getsockname(s_listen, + (struct sockaddr *)&myaddr_in, + &addrlen) == SOCKET_ERROR){ + netperf_response.content.serv_errno = errno; + close(s_listen); + send_response(); + if (debug) { + fprintf(where,"could not geetsockname\n"); + fflush(where); + } + exit(1); + } + + /* Now myaddr_in contains the port and the internet address this is */ + /* returned to the sender also implicitly telling the sender that the */ + /* socket buffer sizing has been done. */ + + xti_tcp_conn_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port); + if (debug) { + fprintf(where,"telling the remote to call me at %d\n", + xti_tcp_conn_rr_response->data_port_number); + fflush(where); + } + netperf_response.content.serv_errno = 0; + + /* But wait, there's more. If the initiator wanted cpu measurements, */ + /* then we must call the calibrate routine, which will return the max */ + /* rate back to the initiator. If the CPU was not to be measured, or */ + /* something went wrong with the calibration, we will return a 0.0 to */ + /* the initiator. */ + + xti_tcp_conn_rr_response->cpu_rate = 0.0; /* assume no cpu */ + if (xti_tcp_conn_rr_request->measure_cpu) { + xti_tcp_conn_rr_response->measure_cpu = 1; + xti_tcp_conn_rr_response->cpu_rate = + calibrate_local_cpu(xti_tcp_conn_rr_request->cpu_rate); + } + + + + /* before we send the response back to the initiator, pull some of */ + /* the socket parms from the globals */ + xti_tcp_conn_rr_response->send_buf_size = lss_size; + xti_tcp_conn_rr_response->recv_buf_size = lsr_size; + xti_tcp_conn_rr_response->no_delay = loc_nodelay; + xti_tcp_conn_rr_response->so_rcvavoid = loc_rcvavoid; + xti_tcp_conn_rr_response->so_sndavoid = loc_sndavoid; + + send_response(); + + addrlen = sizeof(peeraddr_in); + + /* Now it's time to start receiving data on the connection. We will */ + /* first grab the apropriate counters and then start grabbing. */ + + cpu_start(xti_tcp_conn_rr_request->measure_cpu); + + /* The loop will exit when the sender does a shutdown, which will */ + /* return a length of zero */ + + if (xti_tcp_conn_rr_request->test_length > 0) { + times_up = 0; + trans_remaining = 0; + start_timer(xti_tcp_conn_rr_request->test_length + PAD_TIME); + } + else { + times_up = 1; + trans_remaining = xti_tcp_conn_rr_request->test_length * -1; + } + + trans_received = 0; + + while ((!times_up) || (trans_remaining > 0)) { + + /* accept a connection from the remote */ + if ((s_data=accept(s_listen, + (struct sockaddr *)&peeraddr_in, + &addrlen)) == INVALID_SOCKET) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + fprintf(where,"recv_xti_tcp_conn_rr: accept: errno = %d\n",errno); + fflush(where); + close(s_listen); + + exit(1); + } + + if (debug) { + fprintf(where,"recv_xti_tcp_conn_rr: accepted data connection.\n"); + fflush(where); + } + + temp_message_ptr = recv_message_ptr; + request_bytes_remaining = xti_tcp_conn_rr_request->request_size; + + /* receive the request from the other side */ + while(request_bytes_remaining > 0) { + if((request_bytes_recvd=recv(s_data, + temp_message_ptr, + request_bytes_remaining, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the timer popped */ + timed_out = 1; + break; + } + netperf_response.content.serv_errno = errno; + send_response(); + exit(1); + } + else { + request_bytes_remaining -= request_bytes_recvd; + temp_message_ptr += request_bytes_recvd; + } + } + + if (timed_out) { + /* we hit the end of the test based on time - lets */ + /* bail out of here now... */ + fprintf(where,"yo5\n"); + fflush(where); + break; + } + + /* Now, send the response to the remote */ + if((bytes_sent=send(s_data, + send_message_ptr, + xti_tcp_conn_rr_request->response_size, + 0)) == SOCKET_ERROR) { + if (errno == EINTR) { + /* the test timer has popped */ + timed_out = 1; + fprintf(where,"yo6\n"); + fflush(where); + break; + } + netperf_response.content.serv_errno = 99; + send_response(); + exit(1); + } + + trans_received++; + if (trans_remaining) { + trans_remaining--; + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_conn_rr: Transaction %d complete\n", + trans_received); + fflush(where); + } + + /* close the connection */ + close(s_data); + + } + + + /* The loop now exits due to timeout or transaction count being */ + /* reached */ + + cpu_stop(xti_tcp_conn_rr_request->measure_cpu,&elapsed_time); + + if (timed_out) { + /* we ended the test by time, which was at least 2 seconds */ + /* longer than we wanted to run. so, we want to subtract */ + /* PAD_TIME from the elapsed_time. */ + elapsed_time -= PAD_TIME; + } + /* send the results to the sender */ + + if (debug) { + fprintf(where, + "recv_xti_tcp_conn_rr: got %d transactions\n", + trans_received); + fflush(where); + } + + xti_tcp_conn_rr_results->bytes_received = (trans_received * + (xti_tcp_conn_rr_request->request_size + + xti_tcp_conn_rr_request->response_size)); + xti_tcp_conn_rr_results->trans_received = trans_received; + xti_tcp_conn_rr_results->elapsed_time = elapsed_time; + if (xti_tcp_conn_rr_request->measure_cpu) { + xti_tcp_conn_rr_results->cpu_util = calc_cpu_util(elapsed_time); + } + + if (debug) { + fprintf(where, + "recv_xti_tcp_conn_rr: test complete, sending results.\n"); + fflush(where); + } + + send_response(); + +} + +void +print_xti_usage() +{ + + fwrite(xti_usage, sizeof(char), strlen(xti_usage), stdout); + exit(1); + +} + +void +scan_xti_args(int argc, char *argv[]) +{ +#define XTI_ARGS "Dhm:M:r:s:S:Vw:W:X:" + extern int optind, opterrs; /* index of first unused arg */ + extern char *optarg; /* pointer to option string */ + + int c; + + char + arg1[BUFSIZ], /* argument holders */ + arg2[BUFSIZ]; + + if (no_control) { + fprintf(where, + "The XTI tests do not know how to run with no control connection\n"); + exit(-1); + } + + /* Go through all the command line arguments and break them */ + /* out. For those options that take two parms, specifying only */ + /* the first will set both to that value. Specifying only the */ + /* second will leave the first untouched. To change only the */ + /* first, use the form "first," (see the routine break_args.. */ + + while ((c= getopt(argc, argv, XTI_ARGS)) != EOF) { + switch (c) { + case '?': + case 'h': + print_xti_usage(); + exit(1); + case 'D': + /* set the TCP nodelay flag */ + loc_nodelay = 1; + rem_nodelay = 1; + break; + case 's': + /* set local socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + lss_size = convert(arg1); + if (arg2[0]) + lsr_size = convert(arg2); + break; + case 'S': + /* set remote socket sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + rss_size = convert(arg1); + if (arg2[0]) + rsr_size = convert(arg2); + break; + case 'r': + /* set the request/response sizes */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + req_size = convert(arg1); + if (arg2[0]) + rsp_size = convert(arg2); + break; + case 'm': + /* set the send size */ + send_size = convert(optarg); + break; + case 'M': + /* set the recv size */ + recv_size = convert(optarg); + break; + case 'W': + /* set the "width" of the user space data */ + /* buffer. This will be the number of */ + /* send_size buffers malloc'd in the */ + /* *_STREAM test. It may be enhanced to set */ + /* both send and receive "widths" but for now */ + /* it is just the sending *_STREAM. */ + send_width = convert(optarg); + break; + case 'V' : + /* we want to do copy avoidance and will set */ + /* it for everything, everywhere, if we really */ + /* can. of course, we don't know anything */ + /* about the remote... */ +#ifdef SO_SND_COPYAVOID + loc_sndavoid = 1; +#else + loc_sndavoid = 0; + printf("Local send copy avoidance not available.\n"); +#endif +#ifdef SO_RCV_COPYAVOID + loc_rcvavoid = 1; +#else + loc_rcvavoid = 0; + printf("Local recv copy avoidance not available.\n"); +#endif + rem_sndavoid = 1; + rem_rcvavoid = 1; + break; + case 'X': + /* set the xti device file name(s) */ + break_args(optarg,arg1,arg2); + if (arg1[0]) + strcpy(loc_xti_device,arg1); + if (arg2[0]) + strcpy(rem_xti_device,arg2); + break; + }; + } +} +#endif /* WANT_XTI */ diff --git a/nettest_xti.h b/nettest_xti.h new file mode 100644 index 0000000..3bf9968 --- /dev/null +++ b/nettest_xti.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 1995,2004 Hewlett-Packard Company + */ + + /* This file contains the test-specific definitions for netperf's BSD */ + /* sockets tests */ + +struct xti_tcp_stream_request_struct { + int send_buf_size; + int recv_buf_size; /* how big does the client want it - the */ + /* receive socket buffer that is */ + int receive_size; /* how many bytes do we want to receive at one */ + /* time? */ + int recv_alignment; /* what is the alignment of the receive */ + /* buffer? */ + int recv_offset; /* and at what offset from that alignment? */ + int no_delay; /* do we disable the nagle algorithm for send */ + /* coalescing? */ + int measure_cpu; /* does the client want server cpu utilization */ + /* measured? */ + float cpu_rate; /* do we know how fast the cpu is already? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid copies on */ + /* receives? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dirty_count; /* how many integers in the receive buffer */ + /* should be made dirty before calling recv? */ + int clean_count; /* how many integers should be read from the */ + /* recv buffer before calling recv? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char xti_device[32]; /* the path to the dlpi device */ +}; + +struct xti_tcp_stream_response_struct { + int recv_buf_size; /* how big does the client want it */ + int receive_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct xti_tcp_stream_results_struct { + double bytes_received; + unsigned int recv_calls; + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs were there */ +}; + +struct xti_tcp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char xti_device[32]; /* the path to the dlpi device */ +}; + +struct xti_tcp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct xti_tcp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs were there */ +}; + +struct xti_tcp_conn_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char xti_device[32]; /* the path to the dlpi device */ +}; + + +struct xti_tcp_conn_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct xti_tcp_conn_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs were there */ +}; + +struct xti_udp_stream_request_struct { + int recv_buf_size; + int message_size; + int recv_alignment; + int recv_offset; + int checksum_off; /* not used. left in for compatibility */ + int measure_cpu; + float cpu_rate; + int test_length; + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char xti_device[32]; /* the path to the dlpi device */ +}; + +struct xti_udp_stream_response_struct { + int recv_buf_size; + int send_buf_size; + int measure_cpu; + int test_length; + int data_port_number; + float cpu_rate; + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct xti_udp_stream_results_struct { + unsigned int messages_recvd; + unsigned int bytes_received; + float elapsed_time; + float cpu_util; + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs were there */ +}; + + +struct xti_udp_rr_request_struct { + int recv_buf_size; /* how big does the client want it */ + int send_buf_size; + int recv_alignment; + int recv_offset; + int send_alignment; + int send_offset; + int request_size; + int response_size; + int no_delay; + int measure_cpu; /* does the client want server cpu */ + float cpu_rate; /* do we know how fast the cpu is? */ + int test_length; /* how long is the test? */ + int so_rcvavoid; /* do we want the remote to avoid receive */ + /* copies? */ + int so_sndavoid; /* do we want the remote to avoid send copies? */ + int dev_name_len; /* the length of the device name string. this */ + /* is used to put it into the proper order on */ + /* @#$% byte-swapped boxes... */ + char xti_device[32]; /* the path to the dlpi device */ +}; + +struct xti_udp_rr_response_struct { + int recv_buf_size; /* how big does the client want it */ + int no_delay; + int measure_cpu; /* does the client want server cpu */ + int test_length; /* how long is the test? */ + int send_buf_size; + int data_port_number; /* connect to me here */ + float cpu_rate; /* could we measure */ + int so_rcvavoid; /* could the remote avoid receive copies? */ + int so_sndavoid; /* could the remote avoid send copies? */ +}; + +struct xti_udp_rr_results_struct { + unsigned int bytes_received; /* ignored initially */ + unsigned int recv_calls; /* ignored initially */ + unsigned int trans_received; /* not ignored */ + float elapsed_time; /* how long the test ran */ + float cpu_util; /* -1 if not measured */ + float serv_dem; /* -1 if not measured */ + int cpu_method; /* how was cpu util measured? */ + int num_cpus; /* how many CPUs were there */ +}; + +extern void send_xti_tcp_stream(char remote_host[]); + +extern void recv_xti_tcp_stream(); + +extern void send_xti_tcp_rr(char remote_host[]); + +extern void send_xti_udp_stream(char remote_host[]); + +extern void recv_xti_udp_stream(); + +extern void send_xti_udp_rr(char remote_host[]); + +extern void recv_xti_udp_rr(); + +extern void recv_xti_tcp_rr(); + +extern void send_xti_tcp_conn_rr(char remote_host[]); + +extern void recv_xti_tcp_conn_rr(); + +extern void scan_xti_args(int argc, char *argv[]); + + + + + + + + + + |