aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:07 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:07 -0800
commit02fb0aca1189a2c1fd20806c588e9ee80d9755f8 (patch)
tree90ccbb66266424ee5bef1ad707610c05e33e1ea4
parent8eb453f761a608a321986303fd8fefecb463fd6a (diff)
downloadnetperf-02fb0aca1189a2c1fd20806c588e9ee80d9755f8.tar.gz
auto import from //depot/cupcake/@135843
-rw-r--r--Android.mk42
-rw-r--r--MODULE_LICENSE_HP43
-rw-r--r--config.h351
-rw-r--r--hist.h116
-rw-r--r--netcpu.h19
-rw-r--r--netcpu_kstat.c415
-rw-r--r--netcpu_kstat10.c559
-rw-r--r--netcpu_looper.c656
-rw-r--r--netcpu_none.c67
-rw-r--r--netcpu_ntperf.c497
-rw-r--r--netcpu_osx.c149
-rw-r--r--netcpu_perfstat.c351
-rw-r--r--netcpu_procstat.c265
-rw-r--r--netcpu_pstat.c307
-rw-r--r--netcpu_pstatnew.c398
-rw-r--r--netcpu_sysctl.c127
-rw-r--r--netlib.c4161
-rw-r--r--netlib.h621
-rw-r--r--netperf.c284
-rw-r--r--netperf_version.h1
-rw-r--r--netserver.c1022
-rw-r--r--netsh.c1002
-rw-r--r--netsh.h149
-rw-r--r--nettest_bsd.c12333
-rw-r--r--nettest_bsd.h471
-rw-r--r--nettest_dlpi.c3798
-rw-r--r--nettest_dlpi.h215
-rw-r--r--nettest_sctp.c4869
-rw-r--r--nettest_sctp.h128
-rw-r--r--nettest_sdp.c3553
-rw-r--r--nettest_sdp.h170
-rw-r--r--nettest_unix.c3431
-rw-r--r--nettest_unix.h198
-rw-r--r--nettest_xti.c6026
-rw-r--r--nettest_xti.h264
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 */
diff --git a/hist.h b/hist.h
new file mode 100644
index 0000000..b2ed22b
--- /dev/null
+++ b/hist.h
@@ -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);
+}
diff --git a/netsh.c b/netsh.c
new file mode 100644
index 0000000..7659679
--- /dev/null
+++ b/netsh.c
@@ -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");
+}
diff --git a/netsh.h b/netsh.h
new file mode 100644
index 0000000..e99883b
--- /dev/null
+++ b/netsh.h
@@ -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[]);
+
+
+
+
+
+
+
+
+
+