diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:25:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:25:49 +0000 |
commit | 04fc174d50fd19d6ae78fd2fd2faae221acff807 (patch) | |
tree | 23e5482ac4eb332df0fc69bf932118f0d4e42eb0 /src | |
parent | Adding upstream version 2.1.9+dfsg. (diff) | |
download | iperf-upstream.tar.xz iperf-upstream.zip |
Adding upstream version 2.2.0+dfsg.upstream/2.2.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/Client.cpp | 798 | ||||
-rw-r--r-- | src/Launch.cpp | 101 | ||||
-rw-r--r-- | src/Listener.cpp | 522 | ||||
-rw-r--r-- | src/Locale.c | 328 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/Makefile.in | 23 | ||||
-rw-r--r-- | src/PerfSocket.cpp | 234 | ||||
-rw-r--r-- | src/ReportOutputs.c | 1057 | ||||
-rw-r--r-- | src/Reporter.c | 466 | ||||
-rw-r--r-- | src/Reports.c | 139 | ||||
-rw-r--r-- | src/Server.cpp | 321 | ||||
-rw-r--r-- | src/Settings.cpp | 429 | ||||
-rw-r--r-- | src/SocketAddr.c | 31 | ||||
-rw-r--r-- | src/active_hosts.cpp | 198 | ||||
-rw-r--r-- | src/dscp.c | 10 | ||||
-rw-r--r-- | src/histogram.c | 21 | ||||
-rw-r--r-- | src/iperf_formattime.c | 82 | ||||
-rw-r--r-- | src/iperf_multicast_api.c | 386 | ||||
-rw-r--r-- | src/isochronous.cpp | 86 | ||||
-rw-r--r-- | src/packet_ring.c | 21 | ||||
-rw-r--r-- | src/pdfs.c | 7 | ||||
-rw-r--r-- | src/socket_io.c | 16 | ||||
-rw-r--r-- | src/stdio.c | 11 |
23 files changed, 3665 insertions, 1625 deletions
diff --git a/src/Client.cpp b/src/Client.cpp index 89e47e2..d56ee1c 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -67,6 +67,7 @@ #include "payloads.h" #include "active_hosts.h" #include "gettcpinfo.h" +#include "iperf_formattime.h" // const double kSecs_to_usecs = 1e6; const double kSecs_to_nsecs = 1e9; @@ -74,6 +75,7 @@ const int kBytes_to_Bits = 8; #define VARYLOAD_PERIOD 0.1 // recompute the variable load every n seconds #define MAXUDPBUF 1470 +#define TCPDELAYDEFAULTQUANTUM 4000 // units usecs Client::Client (thread_Settings *inSettings) { #ifdef HAVE_THREAD_DEBUG @@ -136,13 +138,7 @@ Client::~Client () { void Client::mySockInit (void) { // create an internet socket int type = (isUDP(mSettings) ? SOCK_DGRAM : SOCK_STREAM); - int domain = (SockAddr_isIPv6(&mSettings->peer) ? -#if HAVE_IPV6 - AF_INET6 -#else - AF_INET -#endif - : AF_INET); + int domain = SockAddr_getAFdomain(&mSettings->peer); mySocket = socket(domain, type, 0); WARN_errno(mySocket == INVALID_SOCKET, "socket"); @@ -151,20 +147,23 @@ void Client::mySockInit (void) { SetSocketOptions(mSettings); SockAddr_localAddr(mSettings); SockAddr_remoteAddr(mSettings); - if (mSettings->mLocalhost != NULL) { - // bind socket to local address - int rc = bind(mySocket, reinterpret_cast<sockaddr*>(&mSettings->local), - SockAddr_get_sizeof_sockaddr(&mSettings->local)); - WARN_errno(rc == SOCKET_ERROR, "bind"); + // do this bind to device after IP addr name lookups per above + SetSocketBindToDeviceIfNeeded(mSettings); + if (mSettings->mLocalhost != NULL) { // bind src ip if needed + // bind socket to local address + int rc = bind(mSettings->mSock, (struct sockaddr *)(&mSettings->local), \ + SockAddr_get_sizeof_sockaddr(&mSettings->local)); + WARN_errno(rc == SOCKET_ERROR, "bind"); } - mysock_init_done = true; - if (!isUDP(mSettings) && isReport(mSettings) && isSettingsReport(mSettings)) { + if (isSettingsReport(mSettings)) { struct ReportHeader *tmp = InitSettingsReport(mSettings); assert(tmp!=NULL); PostReport(tmp); - setNoSettReport(mSettings); } + mysock_init_done = true; } + + /* ------------------------------------------------------------------- * Setup a socket connected to a server. * If inLocalhost is not null, bind to that address, specifying @@ -177,29 +176,69 @@ bool Client::my_connect (bool close_on_fail) { } // connect socket connected = false; + int connect_errno = 0; mSettings->tcpinitstats.connecttime = -1; if (!isUDP(mSettings)) { - int trycnt = mSettings->mConnectRetries + 1; - while (trycnt > 0) { + Timestamp end_connect_retry; + end_connect_retry.add(mSettings->connect_retry_time); + do { connect_start.setnow(); rc = connect(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer), SockAddr_get_sizeof_sockaddr(&mSettings->peer)); - WARN_errno((rc == SOCKET_ERROR), "tcp connect"); + connect_done.setnow(); if (rc == SOCKET_ERROR) { - if ((--trycnt) <= 0) { - if (close_on_fail) { - close(mySocket); - mySocket = INVALID_SOCKET; - } - } else { - delay_loop(200000); + char timestr[120]; + char tmpaddr[200]; + char errtext[50]; + connect_errno = errno_decode(errtext, sizeof(errtext)); + unsigned short port = SockAddr_getPort(&mSettings->peer); + SockAddr_getHostAddress(&mSettings->peer, tmpaddr, sizeof(tmpaddr)); + struct timeval t; + t.tv_sec = connect_done.getSecs(); + t.tv_usec = connect_done.getUsecs(); + iperf_formattime(timestr, sizeof(timestr), t, isEnhanced(mSettings), isUTC(mSettings), YearThruSecTZ); + int slen = snprintf(NULL, 0, "%stcp connect to %s port %d failed (%s) on %s", \ + mSettings->mTransferIDStr, tmpaddr, port, errtext, timestr); + char *text = (char *) calloc((slen+1), sizeof(char)); + if (text) { + snprintf(text, (slen+1), "%stcp connect to %s port %d failed (%s) on %s", \ + mSettings->mTransferIDStr, tmpaddr, port, errtext, timestr); + PostReport(InitStringReport(text)); + FREE_ARRAY(text); + } + bool need_open = false; + if (close_on_fail || FATALTCPCONNECTERR(errno)) { // MAC OS kicks out invalid argument at times, not sure why + close(mySocket); + mySockInit(); + delay_loop(10000); // delay the minimum of 10ms + need_open = true; + } + if (!need_open && connect_done.before(end_connect_retry)) { + int delay = mSettings->connect_retry_timer - (connect_done.subUsec(connect_start)); + delay_loop((delay < 10000) ? 10000 : delay); // minimum of 10ms } } else { - connect_done.setnow(); mSettings->tcpinitstats.connecttime = 1e3 * connect_done.subSec(connect_start); connected = true; break; } + } while (connect_done.before(end_connect_retry)); + if (!connected && (mSettings->connect_retry_time > 0)) { + char timestr[120]; + struct timeval t; + t.tv_sec = end_connect_retry.getSecs(); + t.tv_usec = end_connect_retry.getUsecs(); + iperf_formattime(timestr, sizeof(timestr), t, isEnhanced(mSettings), isUTC(mSettings), YearThruSecTZ); + int len = snprintf(NULL, 0, "%stcp connect attempt timer expired on %s\n", \ + mSettings->mTransferIDStr, timestr); + char *text = (char *) calloc(len+1, sizeof(char)); + if (text) { + snprintf(text, len, "%stcp connect attempt timer expried on %s\n", \ + mSettings->mTransferIDStr, timestr); + PostReport(InitStringReport(text)); + FREE_ARRAY(text); + return false; + } } } else { rc = connect(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer), @@ -217,8 +256,6 @@ bool Client::my_connect (bool close_on_fail) { } #endif // Set the send timeout for the very first write which has the test exchange - int sosndtimer = TESTEXCHANGETIMEOUT; // 4 sec in usecs - SetSocketOptionsSendTimeout(mSettings, sosndtimer); getsockname(mySocket, reinterpret_cast<sockaddr*>(&mSettings->local), &mSettings->size_local); getpeername(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer), &mSettings->size_peer); SockAddr_Ifrname(mSettings); @@ -230,12 +267,6 @@ bool Client::my_connect (bool close_on_fail) { if (isUDP(mSettings) && !isIsochronous(mSettings) && !isIPG(mSettings)) { mSettings->mBurstIPG = get_delay_target() / 1e3; // this is being set for the settings report only } - if (isReport(mSettings) && isSettingsReport(mSettings)) { - struct ReportHeader *tmp = InitSettingsReport(mSettings); - assert(tmp!=NULL); - PostReport(tmp); - setNoSettReport(mSettings); - } } else { if (mySocket != INVALID_SOCKET) { int rc = close(mySocket); @@ -252,7 +283,7 @@ bool Client::my_connect (bool close_on_fail) { cr->connect_timestamp.tv_usec = connect_start.getUsecs(); assert(reporthdr); PostReport(reporthdr); - } else { + } else if (!connect_errno) { PostReport(InitConnectionReport(mSettings)); } } @@ -277,6 +308,10 @@ inline void Client::myReportPacket (void) { reportstruct->packetLen = 0; } +inline void Client::myReportPacket (struct ReportStruct *reportptr) { + ReportPacket(myReport, reportptr); +} + // There are multiple startup synchronizations, this code // handles them all. The caller decides to apply them @@ -303,8 +338,8 @@ int Client::StartSynch () { // o Second is a holdback, a relative amount of seconds between the connect and data xfers // check for an epoch based start time reportstruct->packetLen = 0; - if (!isServerReverse(mSettings)) { - if (!isCompat(mSettings) && !isBounceBack(mSettings)) { + if (!isServerReverse(mSettings) && !isCompat(mSettings)) { + if (!isBounceBack(mSettings)) { reportstruct->packetLen = SendFirstPayload(); // Reverse UDP tests need to retry "first sends" a few times // before going to server or read mode @@ -336,14 +371,18 @@ int Client::StartSynch () { } else if (isTripTime(mSettings) || isPeriodicBurst(mSettings)) { reportstruct->packetLen = SendFirstPayload(); } - if (isIsochronous(mSettings) || isPeriodicBurst(mSettings)) { + if (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || isBounceBack(mSettings)) { Timestamp tmp; - tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec); - framecounter = new Isochronous::FrameCounter(mSettings->mFPS, tmp); - // set the mbuf valid for burst period ahead of time. The same value will be set for all burst writes - if (!isUDP(mSettings) && framecounter) { - struct TCP_burst_payload * mBuf_burst = reinterpret_cast<struct TCP_burst_payload *>(mSettings->mBuf); - mBuf_burst->burst_period_us = htonl(framecounter->period_us()); + if (isTxStartTime(mSettings)) { + tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec); + } + if (mSettings->mFPS > 0) { + framecounter = new Isochronous::FrameCounter(mSettings->mFPS, tmp); + // set the mbuf valid for burst period ahead of time. The same value will be set for all burst writes + if (!isUDP(mSettings) && framecounter) { + struct TCP_burst_payload * mBuf_burst = reinterpret_cast<struct TCP_burst_payload *>(mSettings->mBuf); + mBuf_burst->burst_period_us = htonl(framecounter->period_us()); + } } } int setfullduplexflag = 0; @@ -355,12 +394,12 @@ int Client::StartSynch () { SetReportStartTime(); #if HAVE_TCP_STATS if (!isUDP(mSettings)) { - // Near congestion and peridiodic need sampling on every report packet + // Near congestion and periodic need sampling on every report packet if (isNearCongest(mSettings) || isPeriodicBurst(mSettings)) { myReport->info.isEnableTcpInfo = true; myReport->info.ts.nextTCPSampleTime.tv_sec = 0; myReport->info.ts.nextTCPSampleTime.tv_usec = 0; - } else if (isEnhanced(mSettings) || isBounceBack(mSettings)) { + } else if (isEnhanced(mSettings) || isBounceBack(mSettings) || isFQPacing(mSettings)) { myReport->info.isEnableTcpInfo = true; myReport->info.ts.nextTCPSampleTime = myReport->info.ts.nextTime; } @@ -368,6 +407,7 @@ int Client::StartSynch () { #endif if (reportstruct->packetLen > 0) { + reportstruct->err_readwrite = WriteSuccess; reportstruct->packetTime = myReport->info.ts.startTime; reportstruct->sentTime = reportstruct->packetTime; reportstruct->prevSentTime = reportstruct->packetTime; @@ -406,6 +446,9 @@ inline void Client::SetFullDuplexReportStartTime () { inline void Client::SetReportStartTime () { assert(myReport!=NULL); now.setnow(); + if (isUDP(mSettings) && (mSettings->sendfirst_pacing > 0)) { + now.add(static_cast<unsigned int>(mSettings->sendfirst_pacing)); + } myReport->info.ts.startTime.tv_sec = now.getSecs(); myReport->info.ts.startTime.tv_usec = now.getUsecs(); myReport->info.ts.IPGstart = myReport->info.ts.startTime; @@ -491,11 +534,27 @@ void Client::InitTrafficLoop () { if (isPeriodicBurst(mSettings) && (mSettings->mFPS > 0.0)) { sosndtimer = static_cast<int>(round(250000.0 / mSettings->mFPS)); } else if (mSettings->mInterval > 0) { - sosndtimer = static_cast<int>(round(0.5 * mSettings->mInterval)); + sosndtimer = static_cast<int>(round(((mSettings->mThreads > 1) ? 0.25 : 0.5) * mSettings->mInterval)); } else { sosndtimer = static_cast<int>(mSettings->mAmount * 5e3); } - SetSocketOptionsSendTimeout(mSettings, sosndtimer); + // set to 1 second for wraps or too large + if ((sosndtimer < 0) || (sosndtimer > 1000000)) { + sosndtimer = 1000000; + } + if (sosndtimer < 1000) { + sosndtimer = 1000; //lower bound of 1 ms + } + if ((mSettings->mInterval > 0) && (mSettings->mIntervalMode == kInterval_Time)) { + int interval_half = static_cast<int>(round(mSettings->mAmount * 10000) / 2); + if (sosndtimer > interval_half) { + sosndtimer = interval_half; + } + } + if (!isUDP(mSettings)) { + mSettings->sosndtimer = sosndtimer; + SetSocketOptionsSendTimeout(mSettings, sosndtimer); + } // set the lower bounds delay based of the socket timeout timer // units needs to be in nanoseconds delay_lower_bounds = static_cast<double>(sosndtimer) * -1e3; @@ -510,10 +569,18 @@ void Client::InitTrafficLoop () { mEndTime.add(mSettings->mAmount / 100.0); // now.setnow(); fprintf(stderr, "DEBUG: end time set to %ld.%ld now is %ld.%ld\n", mEndTime.getSecs(), mEndTime.getUsecs(), now.getSecs(), now.getUsecs()); } +#if HAVE_DECL_TCP_TX_DELAY + current_state = NO_DELAY; + if (isTcpTxDelay(mSettings)) { + state_tokens[NO_DELAY] = (int) (mSettings->mTcpTxDelayMean * (1 - mSettings->mTcpTxDelayProb)); + state_tokens[ADD_DELAY] = (int) (mSettings->mTcpTxDelayMean * mSettings->mTcpTxDelayProb); + TcpTxDelayQuantumEnd.setnow(); + } +#endif readAt = mSettings->mBuf; lastPacketTime.set(myReport->info.ts.startTime.tv_sec, myReport->info.ts.startTime.tv_usec); - reportstruct->errwrite=WriteNoErr; - reportstruct->emptyreport=0; + reportstruct->err_readwrite=WriteSuccess; + reportstruct->emptyreport = false; reportstruct->packetLen = 0; // Finally, post this thread's "job report" which the reporter thread // will continuously process as long as there are packets flowing @@ -576,6 +643,48 @@ void Client::Run () { } } +#if HAVE_DECL_TCP_TX_DELAY +inline void Client::apply_txdelay_func (void) { + now.setnow(); + if (isTcpTxDelay(mSettings) && TcpTxDelayQuantumEnd.before(now)) { + // expense the tokens for the current state + state_tokens[current_state] -= now.subUsec(TcpTxDelayQuantumEnd); + // add tokens + do { + state_tokens[NO_DELAY] += (int) (mSettings->mTcpTxDelayMean * (1 - mSettings->mTcpTxDelayProb)); + state_tokens[ADD_DELAY] += (int) (mSettings->mTcpTxDelayMean * mSettings->mTcpTxDelayProb); + } while ((state_tokens[NO_DELAY] < 0) && (state_tokens[ADD_DELAY] < 0)); + // set the next quantum end + while (TcpTxDelayQuantumEnd.before(now)) + TcpTxDelayQuantumEnd.add((unsigned int) TCPDELAYDEFAULTQUANTUM); + // do any state change + if ((state_tokens[NO_DELAY] < 0) && (current_state == NO_DELAY)) { +// printf("**** f state change to 0->1 current=%d %d %d\n", current_state, state_tokens[NO_DELAY], state_tokens[ADD_DELAY]); + SetSocketTcpTxDelay(mSettings, (mSettings->mTcpTxDelayMean * 1000.0)); + current_state = ADD_DELAY; + } else if ((state_tokens[ADD_DELAY] < 0) && (current_state == ADD_DELAY)) { +// printf("**** f state change to 1->0 current=%d %d %d\n", current_state, state_tokens[NO_DELAY], state_tokens[ADD_DELAY]); + SetSocketTcpTxDelay(mSettings, 0.0); + current_state = NO_DELAY; + } else { + int rval = (random() % 2); + // printf("**** curr=%d rval=%d tokens 0:%d 1:%d\n", current_state, rval, state_tokens[NO_DELAY], state_tokens[ADD_DELAY]); + if (rval != current_state) { + if (rval && (state_tokens[ADD_DELAY] > 0)) { +// printf("**** state change to 0->1 rval=%d current=%d %d %d\n", rval, current_state, state_tokens[NO_DELAY], state_tokens[ADD_DELAY]); + SetSocketTcpTxDelay(mSettings, (mSettings->mTcpTxDelayMean * 1000.0)); + current_state = ADD_DELAY; + } else if ((rval != 1) && (state_tokens[NO_DELAY] > 0)) { +// printf("**** state change to 1->0 rval=%d current=%d %d %d\n", rval, current_state, state_tokens[NO_DELAY], state_tokens[ADD_DELAY]); + SetSocketTcpTxDelay(mSettings, 0.0); + current_state = NO_DELAY; + } + } + } + } +} +#endif + /* * TCP send loop */ @@ -588,6 +697,12 @@ void Client::RunTCP () { reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->write_time = 0; +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacingStep(mSettings)) { + PacingStepTime = now; + PacingStepTime.add(mSettings->mFQPacingRateStepInterval); + } +#endif while (InProgress()) { reportstruct->writecnt = 0; reportstruct->scheduled = false; @@ -598,7 +713,7 @@ void Client::RunTCP () { if (isIsochronous(mSettings)) { assert(mSettings->mMean); burst_remaining = static_cast<int>(lognormal(mSettings->mMean,mSettings->mVariance)) / (mSettings->mFPS * 8); - } else if (isPeriodicBurst(mSettings)){ + } else if (isBurstSize(mSettings)){ assert(mSettings->mBurstSize); burst_remaining = mSettings->mBurstSize; } else { @@ -609,7 +724,7 @@ void Client::RunTCP () { burst_remaining = static_cast<int>(sizeof(struct TCP_burst_payload)); // apply scheduling if needed if (framecounter) { - burst_id = framecounter->wait_tick(&reportstruct->sched_err); + burst_id = framecounter->wait_tick(&reportstruct->sched_err, true); reportstruct->scheduled = true; if (isPeriodicBurst(mSettings)) { // low duty cycle traffic needs special event handling @@ -619,20 +734,17 @@ void Client::RunTCP () { reportstruct->packetTime.tv_usec = now.getUsecs(); if (!InProgress()) { reportstruct->packetLen = 0; - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; // wait may have crossed the termination boundry break; } else { //time interval crossings may have occurred during the wait //post a null event to cause the report to flush the packet ring - PostNullEvent(); + PostNullEvent(false, false); } } -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isWritePrefetch(mSettings)) { - AwaitWriteSelectEventTCP(); - } -#endif + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) + continue; } now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); @@ -659,11 +771,8 @@ void Client::RunTCP () { if (isTcpWriteTimes(mSettings)) { write_start.setnow(); } -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isWritePrefetch(mSettings)) { - AwaitWriteSelectEventTCP(); - } -#endif + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) + continue; reportstruct->packetLen = write(mySocket, mSettings->mBuf, writelen); now.setnow(); reportstruct->writecnt++; @@ -676,28 +785,33 @@ void Client::RunTCP () { } if (reportstruct->packetLen <= 0) { if (reportstruct->packetLen == 0) { + reportstruct->err_readwrite=WriteErrFatal; peerclose = true; } else if (NONFATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrAccount; + reportstruct->err_readwrite=WriteErrAccount; } else if (FATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrFatal; - WARN_errno(1, "tcp write"); + reportstruct->err_readwrite=WriteErrFatal; + now.setnow(); + char warnbuf[WARNBUFSIZE]; + snprintf(warnbuf, sizeof(warnbuf), "%stcp write (%ld.%ld)", mSettings->mTransferIDStr, now.getSecs(), now.getUsecs()); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN(1, warnbuf); break; } else { - reportstruct->errwrite=WriteErrNoAccount; + reportstruct->err_readwrite=WriteNoAccount; } reportstruct->packetLen = 0; - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; } else { - reportstruct->emptyreport = 0; + reportstruct->emptyreport = false; totLen += reportstruct->packetLen; - reportstruct->errwrite=WriteNoErr; + reportstruct->err_readwrite=WriteSuccess; if (isburst) { burst_remaining -= reportstruct->packetLen; if (burst_remaining > 0) { - reportstruct->transit_ready = 0; + reportstruct->transit_ready = false; } else { - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; reportstruct->prevSentTime = myReport->info.ts.prevsendTime; } } @@ -711,7 +825,20 @@ void Client::RunTCP () { } } if (!one_report) { +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacing(mSettings)) + reportstruct->FQPacingRate = mSettings->mFQPacingRateCurrent; +#endif myReportPacket(); +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacingStep(mSettings) && PacingStepTime.before(now)) { + mSettings->mFQPacingRateCurrent += mSettings->mFQPacingRateStep; + setsockopt(mSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &mSettings->mFQPacingRateCurrent, sizeof(mSettings->mFQPacingRateCurrent)); + PacingStepTime.add(mSettings->mFQPacingRateStepInterval); + socklen_t len = sizeof(reportstruct->FQPacingRate); + getsockopt(mSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &reportstruct->FQPacingRate, &len); + } +#endif } } FinishTrafficActions(); @@ -761,26 +888,26 @@ void Client::RunNearCongestionTCP () { reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->sentTime = reportstruct->packetTime; ReportNow: - reportstruct->transit_ready = 0; + reportstruct->transit_ready = false; if (reportstruct->packetLen < 0) { if (NONFATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrAccount; + reportstruct->err_readwrite=WriteErrAccount; } else if (FATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrFatal; + reportstruct->err_readwrite=WriteErrFatal; WARN_errno(1, "tcp write"); break; } else { - reportstruct->errwrite=WriteErrNoAccount; + reportstruct->err_readwrite=WriteNoAccount; } reportstruct->packetLen = 0; - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; } else { - reportstruct->emptyreport = 0; + reportstruct->emptyreport = false; totLen += reportstruct->packetLen; - reportstruct->errwrite=WriteNoErr; + reportstruct->err_readwrite=WriteSuccess; burst_remaining -= reportstruct->packetLen; if (burst_remaining <= 0) { - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; } } if (isModeAmount(mSettings) && !reportstruct->emptyreport) { @@ -814,8 +941,7 @@ void Client::RunNearCongestionTCP () { void Client::RunRateLimitedTCP () { double tokens = 0; Timestamp time1, time2; - int burst_size = mSettings->mBufLen; - int burst_remaining = 0; + int write_remaining = 0; int burst_id = 1; long var_rate = mSettings->mAppRate; @@ -848,61 +974,79 @@ void Client::RunRateLimitedTCP () { // perform write int len = 0; int len2 = 0; - if (burst_remaining == 0) { - burst_remaining = mSettings->mBufLen; + + if (isburst && !(write_remaining > 0)) { + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) + continue; + write_remaining = mSettings->mBufLen; + // check for TCP minimum payload + if (write_remaining < static_cast<int>(sizeof(struct TCP_burst_payload))) + write_remaining = static_cast<int>(sizeof(struct TCP_burst_payload)); now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->sentTime = reportstruct->packetTime; - WriteTcpTxHdr(reportstruct, burst_size, burst_id++); + WriteTcpTxHdr(reportstruct, write_remaining, burst_id++); // perform write - if (isTripTime(mSettings)) { - len = writen(mySocket, mSettings->mBuf, mSettings->mBufLen, &reportstruct->writecnt); - WARN(len != mSettings->mBufLen, "burst write failed"); - } else { - len = writen(mySocket, mSettings->mBuf, sizeof(struct TCP_burst_payload), &reportstruct->writecnt); - WARN(len != sizeof(struct TCP_burst_payload), "burst hdr write failed"); + // perform write, full header must succeed + if (isTcpWriteTimes(mSettings)) { + write_start.setnow(); + } + len = writen(mySocket, mSettings->mBuf, write_remaining, &reportstruct->writecnt); + WARN((len < static_cast<int> (sizeof(struct TCP_burst_payload))), "burst hdr write failed"); + if (isTcpWriteTimes(mSettings)) { + now.setnow(); + reportstruct->write_time = now.subUsec(write_start); } if (len < 0) { len = 0; if (NONFATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrAccount; + reportstruct->err_readwrite=WriteErrAccount; } else if (FATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrFatal; + reportstruct->err_readwrite=WriteErrFatal; WARN_errno(1, "write"); fatalwrite_err = 1; break; } else { - reportstruct->errwrite=WriteErrNoAccount; + reportstruct->err_readwrite=WriteNoAccount; } } else { - burst_remaining -= len; + write_remaining -= len; } // thread_debug("***write burst header %d id=%d", burst_size, (burst_id - 1)); - } else if (reportstruct->packetLen > burst_remaining) { - reportstruct->packetLen = burst_remaining; + } else { + write_remaining = mSettings->mBufLen; } - if (burst_remaining > 0) { - len2 = write(mySocket, mSettings->mBuf, reportstruct->packetLen); + if (write_remaining > 0) { + if (isTcpWriteTimes(mSettings)) { + write_start.setnow(); + } + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) + continue; + len2 = write(mySocket, mSettings->mBuf, write_remaining); + if (isTcpWriteTimes(mSettings)) { + now.setnow(); + reportstruct->write_time = now.subUsec(write_start); + } reportstruct->writecnt++; } if (len2 < 0) { len2 = 0; if (NONFATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrAccount; + reportstruct->err_readwrite=WriteErrAccount; } else if (FATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrFatal; + reportstruct->err_readwrite=WriteErrFatal; WARN_errno(1, "write"); fatalwrite_err = 1; break; } else { - reportstruct->errwrite=WriteErrNoAccount; + reportstruct->err_readwrite=WriteNoAccount; } } else { // Consume tokens per the transmit tokens -= (len + len2); - totLen += (len + len2);; - reportstruct->errwrite=WriteNoErr; + totLen += (len + len2); + reportstruct->err_readwrite=WriteSuccess; } time2.setnow(); reportstruct->packetLen = len + len2; @@ -922,51 +1066,60 @@ void Client::RunRateLimitedTCP () { } } else { // Use a 4 usec delay to fill tokens -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isWritePrefetch(mSettings)) { - AwaitWriteSelectEventTCP(); - } else -#endif - { - delay_loop(4); + delay_loop(4); + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) { + continue; } } } FinishTrafficActions(); } +inline bool Client::AwaitSelectWrite (void) { #if HAVE_DECL_TCP_NOTSENT_LOWAT -inline bool Client::AwaitWriteSelectEventTCP (void) { - int rc; - struct timeval timeout; - fd_set writeset; - FD_ZERO(&writeset); - FD_SET(mySocket, &writeset); - if (isModeTime(mSettings)) { - Timestamp write_event_timeout(0,0); - if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { - write_event_timeout.add((double) mSettings->mInterval / 1e6 * 2.0); + do { + int rc; + struct timeval timeout; + fd_set writeset; + FD_ZERO(&writeset); + FD_SET(mySocket, &writeset); + if (isModeTime(mSettings)) { + Timestamp write_event_timeout(0,0); + if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { + write_event_timeout.add((double) mSettings->mInterval / ((mSettings->mThreads > 1) ? 4e6 : 2e6)); + } else { + write_event_timeout.add((double) mSettings->mAmount / 4e2); + } + timeout.tv_sec = write_event_timeout.getSecs(); + timeout.tv_usec = write_event_timeout.getUsecs(); } else { - write_event_timeout.add((double) mSettings->mAmount / 1e2 * 4.0); + timeout.tv_sec = 1; // longest is 1 seconds + timeout.tv_usec = 0; } - timeout.tv_sec = write_event_timeout.getSecs(); - timeout.tv_usec = write_event_timeout.getUsecs(); - } else { - timeout.tv_sec = 10; // longest is 10 seconds - timeout.tv_usec = 0; - } - if ((rc = select(mySocket + 1, NULL, &writeset, NULL, &timeout)) <= 0) { - WARN_errno((rc < 0), "select"); -#ifdef HAVE_THREAD_DEBUG - if (rc == 0) - thread_debug("AwaitWrite timeout"); + if ((rc = select(mySocket + 1, NULL, &writeset, NULL, &timeout)) <= 0) { + WARN_errno((rc < 0), "select"); + if (rc <= 0) + PostNullEvent(false, true); +#if HAVE_SUMMING_DEBUG + if (rc == 0) { + char warnbuf[WARNBUFSIZE]; + snprintf(warnbuf, sizeof(warnbuf), "%sTimeout: write select", mSettings->mTransferIDStr); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN(1, warnbuf); + } #endif - return false; - } + } else { + return true; + } + } while (InProgress()); + return false; +#else return true; +#endif } +#if HAVE_DECL_TCP_NOTSENT_LOWAT void Client::RunWriteEventsTCP () { int burst_id = 0; int writelen = mSettings->mBufLen; @@ -975,18 +1128,28 @@ void Client::RunWriteEventsTCP () { now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacingStep(mSettings)) { + PacingStepTime = now; + PacingStepTime.add(mSettings->mFQPacingRateStepInterval); + } +#endif while (InProgress()) { +#if HAVE_DECL_TCP_TX_DELAY +// apply_txdelay_func(); +#endif if (isModeAmount(mSettings)) { writelen = ((mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen); } now.setnow(); reportstruct->write_time = 0; + reportstruct->writecnt = 0; if (isTcpWriteTimes(mSettings)) { write_start = now; } - bool rc = AwaitWriteSelectEventTCP(); - reportstruct->emptyreport = (rc == false) ? 1 : 0; - if (rc) { + if (isWritePrefetch(mSettings) && !AwaitSelectWrite()) + continue; + if (!reportstruct->emptyreport) { now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); @@ -999,7 +1162,7 @@ void Client::RunWriteEventsTCP () { peerclose = true; } reportstruct->packetLen = 0; - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; } else if (isTcpWriteTimes(mSettings)) { write_end.setnow(); reportstruct->write_time = write_end.subUsec(write_start); @@ -1014,7 +1177,20 @@ void Client::RunWriteEventsTCP () { } } if (!one_report) { +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacing(mSettings)) + reportstruct->FQPacingRate = mSettings->mFQPacingRateCurrent; +#endif myReportPacket(); +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacingStep(mSettings) && PacingStepTime.before(now)) { + mSettings->mFQPacingRateCurrent += mSettings->mFQPacingRateStep; + setsockopt(mSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &mSettings->mFQPacingRateCurrent, sizeof(mSettings->mFQPacingRateCurrent)); + PacingStepTime.add(mSettings->mFQPacingRateStepInterval); + socklen_t len = sizeof(reportstruct->FQPacingRate); + getsockopt(mSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &reportstruct->FQPacingRate, &len); + } +#endif } } FinishTrafficActions(); @@ -1022,7 +1198,7 @@ void Client::RunWriteEventsTCP () { #endif void Client::RunBounceBackTCP () { int burst_id = 0; - int writelen = mSettings->mBufLen; + int writelen = mSettings->mBounceBackBytes; memset(mSettings->mBuf, 0x5A, sizeof(struct bounceback_hdr)); if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { int sotimer = static_cast<int>(round(mSettings->mInterval / 2.0)); @@ -1044,11 +1220,12 @@ void Client::RunBounceBackTCP () { reportstruct->packetTime.tv_usec = now.getUsecs(); while (InProgress()) { int n; + long remaining; reportstruct->writecnt = 0; bool isFirst; if (framecounter) { - burst_id = framecounter->wait_tick(&reportstruct->sched_err); - PostNullEvent(true); // this will set the now timestamp + burst_id = framecounter->wait_tick(&reportstruct->sched_err, false); + PostNullEvent(true, false); // now is set in this call reportstruct->sentTime.tv_sec = now.getSecs(); reportstruct->sentTime.tv_usec = now.getUsecs(); isFirst = true; @@ -1057,7 +1234,7 @@ void Client::RunBounceBackTCP () { isFirst = false; } int bb_burst = (mSettings->mBounceBackBurst > 0) ? mSettings->mBounceBackBurst : 1; - while (bb_burst > 0) { + while ((bb_burst > 0) && InProgress() && (!framecounter || (framecounter->get(&remaining)) == (unsigned int) burst_id)) { bb_burst--; if (isFirst) { isFirst = false; @@ -1067,28 +1244,31 @@ void Client::RunBounceBackTCP () { reportstruct->sentTime.tv_usec = now.getUsecs(); } WriteTcpTxBBHdr(reportstruct, burst_id, 0); - reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt); - if (reportstruct->packetLen <= 0) { - if ((reportstruct->packetLen < 0) && FATALTCPWRITERR(errno)) { - reportstruct->errwrite=WriteErrFatal; + int write_offset = 0; + reportstruct->writecnt = 0; + RETRY_WRITE: + n = writen(mySocket, (mSettings->mBuf + write_offset), (writelen - write_offset), &reportstruct->writecnt); + if (n < 0) { + if (FATALTCPWRITERR(errno)) { + reportstruct->err_readwrite=WriteErrFatal; WARN_errno(1, "tcp bounceback write fatal error"); peerclose = true; - } else if (reportstruct->packetLen == 0) { - peerclose = true; - } else if (reportstruct->packetLen != writelen) { - WARN_errno(1, "tcp bounceback writen incomplete"); - peerclose = true; - } else { - // retry the write - bb_burst++; - continue; + break; + } else if (InProgress()) { + PostNullEvent(false,false); + goto RETRY_WRITE; } - break; } - if (reportstruct->packetLen == writelen) { - reportstruct->emptyreport = 0; - totLen += reportstruct->packetLen; - reportstruct->errwrite=WriteNoErr; + write_offset += n; + if ((write_offset < writelen) && InProgress()) { + WARN_errno(1, "tcp bounceback writen incomplete"); + PostNullEvent(false,false); + goto RETRY_WRITE; + } + if (write_offset == writelen) { + reportstruct->emptyreport = false; + totLen += writelen; + reportstruct->err_readwrite=WriteSuccess; #if HAVE_DECL_TCP_QUICKACK if (isTcpQuickAck(mSettings)) { int opt = 1; @@ -1098,43 +1278,50 @@ void Client::RunBounceBackTCP () { WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); } #endif - if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) { - struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); - now.setnow(); - reportstruct->sentTimeRX.tv_sec = ntohl(bbhdr->bbserverRx_ts.sec); - reportstruct->sentTimeRX.tv_usec = ntohl(bbhdr->bbserverRx_ts.usec); - reportstruct->sentTimeTX.tv_sec = ntohl(bbhdr->bbserverTx_ts.sec); - reportstruct->sentTimeTX.tv_usec = ntohl(bbhdr->bbserverTx_ts.usec); - reportstruct->packetTime.tv_sec = now.getSecs(); - reportstruct->packetTime.tv_usec = now.getUsecs(); - reportstruct->packetLen += n; - reportstruct->emptyreport = 0; - myReportPacket(); + int read_offset = 0; + RETRY_READ: + n = recvn(mySocket, (mSettings->mBuf + read_offset), (mSettings->mBounceBackReplyBytes - read_offset), 0); + if (n > 0) { + read_offset += n; + if (read_offset == mSettings->mBounceBackReplyBytes) { + struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); + now.setnow(); + reportstruct->sentTimeRX.tv_sec = ntohl(bbhdr->bbserverRx_ts.sec); + reportstruct->sentTimeRX.tv_usec = ntohl(bbhdr->bbserverRx_ts.usec); + reportstruct->sentTimeTX.tv_sec = ntohl(bbhdr->bbserverTx_ts.sec); + reportstruct->sentTimeTX.tv_usec = ntohl(bbhdr->bbserverTx_ts.usec); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->packetLen += n; + reportstruct->emptyreport = false; + reportstruct->packetID = burst_id; + myReportPacket(); + } else if (InProgress()) { + PostNullEvent(false,false); + goto RETRY_READ; + } else { + break; + } } else if (n == 0) { peerclose = true; - } else if (n < 0) { + break; + } else { if (FATALTCPREADERR(errno)) { - WARN_errno(1, "bounceback read"); + FAIL_errno(1, "fatal bounceback read", mSettings); peerclose = true; - n = 0; + break; } else { - WARN(1, "timeout: bounceback read"); + WARN_errno(1, "timeout: bounceback read"); + PostNullEvent(false,false); + if (InProgress()) + goto RETRY_READ; } } - } else if ((reportstruct->packetLen < 0 ) && NONFATALTCPWRITERR(errno)) { - reportstruct->packetLen = 0; - reportstruct->emptyreport = 1; - reportstruct->errwrite=WriteErrNoAccount; - myReportPacket(); - } else { - reportstruct->errwrite=WriteErrFatal; - reportstruct->packetLen = -1; - FAIL_errno(1, "tcp bounce-back write", mSettings); } } } - WriteTcpTxBBHdr(reportstruct, 0x0, 1); - disarm_itimer(); + if (!peerclose) + WriteTcpTxBBHdr(reportstruct, 0x0, 1); // Signal end of BB test FinishTrafficActions(); } /* @@ -1148,10 +1335,11 @@ double Client::get_delay_target () { // compute delay target in units of nanoseconds if (mSettings->mAppRateUnits == kRate_BW) { // compute delay for bandwidth restriction, constrained to [0,1] seconds - delay_target = (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits) - / mSettings->mAppRate)); + delay_target = ((mSettings->mAppRate > 0) ? \ + (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits) / mSettings->mAppRate)) \ + : 0); } else { - delay_target = 1e9 / mSettings->mAppRate; + delay_target = (mSettings->mAppRate > 0) ? (1e9 / mSettings->mAppRate) : 0; } } return delay_target; @@ -1202,53 +1390,77 @@ void Client::RunUDP () { mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec); mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec); - // Adjustment for the running delay - // o measure how long the last loop iteration took - // o calculate the delay adjust - // - If write succeeded, adjust = target IPG - the loop time - // - If write failed, adjust = the loop time - // o then adjust the overall running delay - // Note: adjust units are nanoseconds, - // packet timestamps are microseconds - if (currLen > 0) - adjust = delay_target + \ - (1000.0 * lastPacketTime.subUsec(reportstruct->packetTime)); - else - adjust = 1000.0 * lastPacketTime.subUsec(reportstruct->packetTime); - - lastPacketTime.set(reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec); - // Since linux nanosleep/busyloop can exceed delay - // there are two possible equilibriums - // 1) Try to perserve inter packet gap - // 2) Try to perserve requested transmit rate - // The latter seems preferred, hence use a running delay - // that spans the life of the thread and constantly adjust. - // A negative delay means the iperf app is behind. - delay += adjust; - // Don't let delay grow unbounded - if (delay < delay_lower_bounds) { - delay = delay_target; - } + if (delay_target > 0) { + // Adjustment for the running delay + // o measure how long the last loop iteration took + // o calculate the delay adjust + // - If write succeeded, adjust = target IPG - the loop time + // - If write failed, adjust = the loop time + // o then adjust the overall running delay + // Note: adjust units are nanoseconds, + // packet timestamps are microseconds + if (currLen > 0) + adjust = delay_target + \ + (1000.0 * lastPacketTime.subUsec(reportstruct->packetTime)); + else + adjust = 1000.0 * lastPacketTime.subUsec(reportstruct->packetTime); - reportstruct->errwrite = WriteNoErr; - reportstruct->emptyreport = 0; + lastPacketTime.set(reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec); + // Since linux nanosleep/busyloop can exceed delay + // there are two possible equilibriums + // 1) Try to perserve inter packet gap + // 2) Try to perserve requested transmit rate + // The latter seems preferred, hence use a running delay + // that spans the life of the thread and constantly adjust. + // A negative delay means the iperf app is behind. + delay += adjust; + // Don't let delay grow unbounded + if (delay < delay_lower_bounds) { + delay = delay_target; + } + } + reportstruct->err_readwrite = WriteSuccess; + reportstruct->emptyreport = false; // perform write if (isModeAmount(mSettings)) { currLen = write(mySocket, mSettings->mBuf, (mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen); } else { + currLen = -1; +#if (HAVE_USE_WRITE_SELECT) && (HAVE_SELECT) +#if HAVE_DECL_MSG_DONTWAIT + currLen = send(mySocket, mSettings->mBuf, mSettings->mBufLen, MSG_DONTWAIT); + if ((currLen < 0) && !FATALUDPWRITERR(errno)) { + if (AwaitSelectWrite()) { + currLen = write(mySocket, mSettings->mBuf, mSettings->mBufLen); + reportstruct->err_readwrite = WriteSelectRetry; + } else { + reportstruct->err_readwrite = WriteTimeo; + } + } +#else + if (AwaitSelectWrite()) { + currLen = write(mySocket, mSettings->mBuf, mSettings->mBufLen); + } else { + reportstruct->err_readwrite = WriteTimeo; + } +#endif +#else currLen = write(mySocket, mSettings->mBuf, mSettings->mBufLen); +#endif } if (currLen < 0) { reportstruct->packetID--; if (FATALUDPWRITERR(errno)) { - reportstruct->errwrite = WriteErrFatal; + reportstruct->err_readwrite = WriteErrFatal; WARN_errno(1, "write"); + currLen = 0; break; } else { - reportstruct->errwrite = WriteErrAccount; + //WARN_errno(1, "write n"); currLen = 0; + reportstruct->err_readwrite = WriteErrAccount; } - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; } if (isModeAmount(mSettings)) { @@ -1310,7 +1522,7 @@ void Client::RunUDPIsochronous () { udp_payload->isoch.burstsize = htonl(bytecnt); udp_payload->isoch.prevframeid = htonl(frameid); reportstruct->burstsize=bytecnt; - frameid = framecounter->wait_tick(&reportstruct->sched_err); + frameid = framecounter->wait_tick(&reportstruct->sched_err, true); reportstruct->scheduled = true; udp_payload->isoch.frameid = htonl(frameid); lastPacketTime.setnow(); @@ -1356,8 +1568,8 @@ void Client::RunUDPIsochronous () { // delay = delay_target; // } - reportstruct->errwrite = WriteNoErr; - reportstruct->emptyreport = 0; + reportstruct->err_readwrite = WriteSuccess; + reportstruct->emptyreport = false; reportstruct->writecnt = 1; // perform write @@ -1373,21 +1585,22 @@ void Client::RunUDPIsochronous () { if (currLen < 0) { reportstruct->packetID--; - reportstruct->emptyreport = 1; - currLen = 0; + reportstruct->emptyreport = true; if (FATALUDPWRITERR(errno)) { - reportstruct->errwrite = WriteErrFatal; + reportstruct->err_readwrite = WriteErrFatal; WARN_errno(1, "write"); fatalwrite_err = 1; - } else { - reportstruct->errwrite = WriteErrAccount; + currLen = 0; + } else { + currLen = 0; + reportstruct->err_readwrite = WriteErrAccount; } } else { bytecnt -= currLen; if (!bytecnt) - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; else - reportstruct->transit_ready = 0; + reportstruct->transit_ready = false; // adjust bytecnt so last packet of burst is greater or equal to min packet if ((bytecnt > 0) && (bytecnt < udp_payload_minimum)) { bytecnt = udp_payload_minimum; @@ -1504,8 +1717,11 @@ void Client::WriteTcpTxBBHdr (struct ReportStruct *reportstruct, uint32_t bbid, if (final) { bbflags |= HEADER_BBSTOP; } + if ((mSettings->mBounceBackReplyBytes > 0) && (mSettings->mBounceBackReplyBytes != mSettings->mBounceBackBytes)) { + bbflags |= HEADER_BBREPLYSIZE; + } mBuf_bb->bbflags = htons(bbflags); - mBuf_bb->bbsize = htonl(mSettings->mBufLen); + mBuf_bb->bbsize = htonl(mSettings->mBounceBackBytes); mBuf_bb->bbid = htonl(bbid); mBuf_bb->bbclientTx_ts.sec = htonl(reportstruct->packetTime.tv_sec); mBuf_bb->bbclientTx_ts.usec = htonl(reportstruct->packetTime.tv_usec); @@ -1514,6 +1730,7 @@ void Client::WriteTcpTxBBHdr (struct ReportStruct *reportstruct, uint32_t bbid, mBuf_bb->bbserverTx_ts.sec = -1; mBuf_bb->bbserverTx_ts.usec = -1; mBuf_bb->bbhold = htonl(mSettings->mBounceBackHold); + mBuf_bb->bbreplysize = htonl(mSettings->mBounceBackReplyBytes); } inline bool Client::InProgress (void) { @@ -1535,7 +1752,10 @@ inline void Client::tcp_shutdown (void) { #ifdef HAVE_THREAD_DEBUG thread_debug("Client calls shutdown() SHUTW_WR on tcp socket %d", mySocket); #endif - WARN_errno(rc == SOCKET_ERROR, "shutdown"); + char warnbuf[256]; + snprintf(warnbuf, sizeof(warnbuf), "%sshutdown", mSettings->mTransferIDStr); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN_errno(rc == SOCKET_ERROR, warnbuf); if (!rc && !isFullDuplex(mSettings)) AwaitServerCloseEvent(); } @@ -1557,7 +1777,9 @@ void Client::FinishTrafficActions () { disarm_itimer(); // Shutdown the TCP socket's writes as the event for the server to end its traffic loop if (!isUDP(mSettings)) { - tcp_shutdown(); + if (!isIgnoreShutdown(mSettings)) { + tcp_shutdown(); + } now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); @@ -1568,6 +1790,8 @@ void Client::FinishTrafficActions () { * */ reportstruct->packetLen = totLen; + reportstruct->err_readwrite = WriteSuccess; + myReportPacket(); } } else { // stop timing @@ -1593,7 +1817,7 @@ void Client::FinishTrafficActions () { } reportstruct->packetLen = 0; } - int do_close = EndJob(myJob, reportstruct); + bool do_close = EndJob(myJob, reportstruct); if (isIsochronous(mSettings) && (myReport->info.schedule_error.cnt > 2)) { fprintf(stderr,"%sIsoch schedule errors (mean/min/max/stdev) = %0.3f/%0.3f/%0.3f/%0.3f ms\n",mSettings->mTransferIDStr, \ ((myReport->info.schedule_error.sum / myReport->info.schedule_error.cnt) * 1e-3), (myReport->info.schedule_error.min * 1e-3), \ @@ -1632,6 +1856,7 @@ void Client::AwaitServerFinPacket () { struct timeval timeout; int ack_success = 0; int count = RETRYCOUNT; + static int read_warn_rate_limiter = 3; // rate limit read warn msgs while (--count >= 0) { // wait until the socket is readable, or our timeout expires FD_ZERO(&readSet); @@ -1664,8 +1889,16 @@ void Client::AwaitServerFinPacket () { continue; } } - - WARN_errno(rc < 0, "read"); + // only warn when threads is small, too many warnings are too much outputs + if (rc < 0 && (--read_warn_rate_limiter > 0)) { + int len = snprintf(NULL, 0, "%sRead UDP fin", mSettings->mTransferIDStr); + char *tmpbuf = (char *)calloc(1, len + 2); + if (tmpbuf) { + len = snprintf(tmpbuf, len + 1, "%sRead UDP fin", mSettings->mTransferIDStr); + WARN_errno(1, tmpbuf); + free(tmpbuf); + } + } if (rc > 0) { ack_success = 1; #ifdef HAVE_THREAD_DEBUG @@ -1682,32 +1915,23 @@ void Client::AwaitServerFinPacket () { fprintf(stderr, warn_no_ack, mySocket, (isModeTime(mSettings) ? 10 : 1)); } - -void Client::PostNullEvent (void) { +// isFirst indicates first event occurred per wait_tick +void Client::PostNullEvent (bool isFirst, bool select_retry) { assert(myReport!=NULL); // push a nonevent into the packet ring // this will cause the reporter to process // up to this event - memset(reportstruct, 0, sizeof(struct ReportStruct)); + struct ReportStruct report_nopacket; + memset(&report_nopacket, 0, sizeof(struct ReportStruct)); now.setnow(); - reportstruct->packetTime.tv_sec = now.getSecs(); - reportstruct->packetTime.tv_usec = now.getUsecs(); - reportstruct->emptyreport=1; - myReportPacket(); -} - -void Client::PostNullEvent (bool isFirst) { - assert(myReport!=NULL); - // push a nonevent into the packet ring - // this will cause the reporter to process - // up to this event - memset(reportstruct, 0, sizeof(struct ReportStruct)); - now.setnow(); - reportstruct->packetTime.tv_sec = now.getSecs(); - reportstruct->packetTime.tv_usec = now.getUsecs(); - reportstruct->emptyreport=1; - reportstruct->scheduled = isFirst; - myReportPacket(); + report_nopacket.packetTime.tv_sec = now.getSecs(); + report_nopacket.packetTime.tv_usec = now.getUsecs(); + report_nopacket.emptyreport = true; + report_nopacket.scheduled = isFirst; + report_nopacket.packetID = 0; + report_nopacket.err_readwrite = (select_retry ? WriteSelectRetry : WriteNoAccount); + reportstruct->packetTime = report_nopacket.packetTime; // needed for the InProgress loop test + myReportPacket(&report_nopacket); } // The client end timer is based upon the final fin, fin-ack w/the server @@ -1717,7 +1941,7 @@ void Client::PostNullEvent (bool isFirst) { #define MINAWAITCLOSEUSECS 2000000 void Client::AwaitServerCloseEvent () { // the await detection can take awhile so post a non event ahead of it - PostNullEvent(); + PostNullEvent(false,false); unsigned int amount_usec = \ (isModeTime(mSettings) ? static_cast<int>(mSettings->mAmount * 10000) : MINAWAITCLOSEUSECS); if (amount_usec < MINAWAITCLOSEUSECS) @@ -1739,6 +1963,9 @@ void Client::AwaitServerCloseEvent () { int Client::SendFirstPayload () { int pktlen = 0; if (!isConnectOnly(mSettings)) { + if (isUDP(mSettings) && (mSettings->sendfirst_pacing > 0)) { + delay_loop(mSettings->sendfirst_pacing); + } if (myReport && !TimeZero(myReport->info.ts.startTime) && !(mSettings->mMode == kTest_TradeOff)) { reportstruct->packetTime = myReport->info.ts.startTime; } else { @@ -1746,7 +1973,7 @@ int Client::SendFirstPayload () { reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); } - pattern(mSettings->mBuf, mSettings->mBufLen); + if (isTxStartTime(mSettings)) { pktlen = Settings_GenerateClientHdr(mSettings, (void *) mSettings->mBuf, mSettings->txstart_epoch); } else { @@ -1766,6 +1993,9 @@ int Client::SendFirstPayload () { #endif apply_first_udppkt_delay = true; } else { + // Set the send timeout for the very first write which has the test exchange + int sosndtimer = DEFAULT_TESTEXCHANGETIMEOUT; //in usecs + SetSocketOptionsSendTimeout(mSettings, sosndtimer); #if HAVE_DECL_TCP_NODELAY if (!isNoDelay(mSettings) && isPeerVerDetect(mSettings) && isTripTime(mSettings)) { int optflag=1; @@ -1781,9 +2011,19 @@ int Client::SendFirstPayload () { #else pktlen = send(mySocket, mSettings->mBuf, pktlen, 0); #endif + SetSocketOptionsSendTimeout(mSettings, mSettings->sosndtimer); if (isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) { PeerXchange(); } + if (!isFileInput(mSettings)) { + int buflen = (mSettings->mBufLen < (int) sizeof(struct client_tcp_testhdr)) ? mSettings->mBufLen \ + : sizeof(struct client_tcp_testhdr); + if (isTripTime(mSettings)) { + memset(mSettings->mBuf, 0xA5, buflen); + } else { + pattern(mSettings->mBuf, buflen); // reset the pattern in the payload for future writes + } + } #if HAVE_DECL_TCP_NODELAY if (!isNoDelay(mSettings) && isPeerVerDetect(mSettings) && isTripTime(mSettings)) { int optflag=0; @@ -1833,22 +2073,15 @@ void Client::PeerXchange () { /* * BarrierClient allows for multiple stream clients to be syncronized */ -int Client::BarrierClient (struct BarrierMutex *barrier) { - int last = 0; +#define BARRIER_MIN 100 // units is seconds to inform the server +bool Client::BarrierClient (struct BarrierMutex *barrier) { + bool last = false; #ifdef HAVE_THREAD assert(barrier != NULL); + Timestamp now; Condition_Lock(barrier->await); if (--barrier->count <= 0) { - // store the barrier release timer -#ifdef HAVE_CLOCK_GETTIME - struct timespec t1; - clock_gettime(CLOCK_REALTIME, &t1); - barrier->release_time.tv_sec = t1.tv_sec; - barrier->release_time.tv_usec = t1.tv_nsec / 1000; -#else - gettimeofday(&barrier->release_time, NULL); -#endif - last = 1; + last = true; // last one wake's up everyone else Condition_Broadcast(&barrier->await); #ifdef HAVE_THREAD_DEBUG @@ -1858,14 +2091,25 @@ int Client::BarrierClient (struct BarrierMutex *barrier) { #ifdef HAVE_THREAD_DEBUG thread_debug("Barrier WAIT on condition %p count=%d", (void *)&barrier->await, barrier->count); #endif - Condition_Wait(&barrier->await); + if (isModeTime(mSettings)) { + int barrier_wait_secs = int(mSettings->mAmount / 100); // convert from 10 ms to seconds + if (barrier_wait_secs <= 0) + barrier_wait_secs = 1; + Condition_TimedWait(&barrier->await, barrier_wait_secs); + } else { + Condition_Wait(&barrier->await); + } } Condition_Unlock(barrier->await); #ifdef HAVE_THREAD_DEBUG thread_debug("Barrier EXIT on condition %p", (void *)&barrier->await); #endif + mSettings->barrier_time = now.delta_usec(); + if (mSettings->barrier_time < BARRIER_MIN) { + mSettings->barrier_time = 0; + } #else - last = 1; + last = true; #endif // HAVE_THREAD return last; } diff --git a/src/Launch.cpp b/src/Launch.cpp index 9f0c633..721f95e 100644 --- a/src/Launch.cpp +++ b/src/Launch.cpp @@ -167,7 +167,7 @@ void server_spawn(struct thread_Settings *thread) { } static void clientside_client_basic (struct thread_Settings *thread, Client *theClient) { - setTransferID(thread, 0); + setTransferID(thread, NORMAL); #ifdef HAVE_THREAD_DEBUG if (isBounceBack(thread)) { thread_debug("Launch: spawn client bounce-back mode, size = %d", thread->mBurstSize); @@ -176,24 +176,12 @@ static void clientside_client_basic (struct thread_Settings *thread, Client *the } #endif SockAddr_remoteAddr(thread); - // Bounceback start delays the connect too - if (isBounceBack(thread)) { - if (isTxStartTime(thread)) { - theClient->mySockInit(); - clock_usleep_abstime(&thread->txstart_epoch); - } else if (isTxHoldback(thread)) { - unsetTxHoldback(thread); - theClient->mySockInit(); - clock_usleep(&thread->txholdback_timer); - } - } - theClient->my_connect(true); + theClient->my_connect(false); if ((thread->mThreads > 1) && !isNoConnectSync(thread) && !isCompat(thread)) // When -P > 1 then all threads finish connect before starting traffic theClient->BarrierClient(thread->connects_done); if (theClient->isConnected()) { - if ((thread->mThreads > 1) || isSumOnly(thread)) - Iperf_push_host(thread); + Iperf_push_host(thread); theClient->StartSynch(); theClient->Run(); } @@ -201,9 +189,9 @@ static void clientside_client_basic (struct thread_Settings *thread, Client *the static void clientside_client_reverse (struct thread_Settings *thread, \ struct thread_Settings *reverse_client, Client *theClient) { - setTransferID(thread, 0); + setTransferID(thread, NORMAL); SockAddr_remoteAddr(thread); - theClient->my_connect(true); + theClient->my_connect(false); #ifdef HAVE_THREAD_DEBUG thread_debug("Launch: client spawn thread reverse (sock=%d)", thread->mSock); #endif @@ -212,7 +200,7 @@ static void clientside_client_reverse (struct thread_Settings *thread, \ theClient->BarrierClient(thread->connects_done); if (theClient->isConnected()) { FAIL((!reverse_client || !(thread->mSock > 0)), "Reverse test failed to start per thread settings or socket problem", thread); - setTransferID(reverse_client, 1); + setTransferID(reverse_client, REVERSED); theClient->StartSynch(); reverse_client->mSock = thread->mSock; // use the same socket for both directions reverse_client->mThreadMode = kMode_Server; @@ -220,8 +208,7 @@ static void clientside_client_reverse (struct thread_Settings *thread, \ setNoUDPfin(reverse_client); // disable the fin report - no need reverse_client->size_local = sizeof(iperf_sockaddr); getsockname(reverse_client->mSock, reinterpret_cast<sockaddr*>(&reverse_client->local), &reverse_client->size_local); - if ((thread->mThreads > 1) || isSumOnly(thread)) - Iperf_push_host(reverse_client); + Iperf_push_host(reverse_client); thread_start(reverse_client); if (theClient->myJob) FreeReport(theClient->myJob); @@ -230,20 +217,17 @@ static void clientside_client_reverse (struct thread_Settings *thread, \ static void clientside_client_fullduplex (struct thread_Settings *thread, \ struct thread_Settings *reverse_client, Client *theClient) { - setTransferID(thread, 0); + setTransferID(thread, NORMAL); SockAddr_remoteAddr(thread); if (!isBounceBack(thread)) { - thread->mFullDuplexReport = InitSumReport(thread, -1, 1); + thread->mFullDuplexReport = InitSumReport(thread, -1, true); } Settings_Copy(thread, &reverse_client, SHALLOW_COPY); - if ((thread->mThreads > 1) || isSumOnly(thread) || \ - (!(thread->mThreads > 1) && !isEnhanced(thread))) { - Iperf_push_host(thread); - Iperf_push_host(reverse_client); - } + Iperf_push_host(thread); + Iperf_push_host(reverse_client); assert(reverse_client != NULL); - setTransferID(reverse_client, 1); - theClient->my_connect(true); + setTransferID(reverse_client, REVERSED); + theClient->my_connect(false); #ifdef HAVE_THREAD_DEBUG thread_debug("Launch: client spawn thread fullduplex (sock=%d)", thread->mSock); #endif @@ -270,7 +254,7 @@ static void serverside_client_fullduplex (struct thread_Settings *thread, Client #ifdef HAVE_THREAD_DEBUG thread_debug("Launch: Listener spawn client thread (fd sock=%d)", thread->mSock); #endif - setTransferID(thread, 1); + setTransferID(thread, REVERSED); if (theClient->StartSynch() != -1) { theClient->Run(); } @@ -280,7 +264,7 @@ static void serverside_client_bidir (struct thread_Settings *thread, Client *the #ifdef HAVE_THREAD_DEBUG thread_debug("Launch: Listener spawn client thread (bidir sock=%d)", thread->mSock); #endif - setTransferID(thread, 1); + setTransferID(thread, REVERSED); SockAddr_zeroAddress(&thread->peer); SockAddr_remoteAddr(thread); if (thread->mReportMode == kReport_CSV) { @@ -324,7 +308,7 @@ void client_spawn (struct thread_Settings *thread) { thread_setscheduler(thread); #endif // start up the client - setTransferID(thread, 0); + setTransferID(thread, NORMAL); theClient = new Client(thread); // let the reporter thread go first in the case of -P greater than 1 Condition_Lock(reporter_state.await); @@ -400,10 +384,21 @@ void client_init(struct thread_Settings *clients) { if (isIncrSrcIP(clients) && (clients->mLocalhost != NULL)) { next->incrsrcip = i; } + if (isTxStartTime(clients)) { + // break apart -P first pkt sends by some usecs + // this allows the listener thread to spawn a server, connect() and open + // a new socket for subsequent threads. This issue is most + // notable with --tx-starttime and -P > 1 + // use max cores & a max aggregate delay to limit this so it's bounded +#define MAXCORES 10 +#define MAXDELAY 20000 // 20 ms + next->sendfirst_pacing = (i % MAXCORES) * (MAXDELAY / MAXCORES); + } if (isIncrDstIP(clients)) { next->incrdstip = i; // force a setHostname SockAddr_zeroAddress(&next->peer); + setNoSettReport(next); } else if (clients->mBindPort) { // Increment the source port of none of the quintuple is being change or the user requests it if ((!isIncrDstPort(clients) && !isIncrDstIP(clients) && !isIncrSrcIP(clients)) || isIncrSrcPort(clients)) { @@ -419,11 +414,22 @@ void client_init(struct thread_Settings *clients) { itr->runNow = next; itr = next; } - if ((isBounceBack(clients) || isConnectOnly(clients) || isPeriodicBurst(clients)) \ - && (isWorkingLoadUp(clients) || isWorkingLoadDown(clients))) { + if (isWorkingLoadUp(clients) || isWorkingLoadDown(clients)) { int working_load_threads = (clients->mWorkingLoadThreads == 0) ? 1 : clients->mWorkingLoadThreads; while (working_load_threads--) { Settings_Copy(clients, &next, DEEP_COPY); + if (isUDP(next)) { + unsetUDP(next); + unsetBWSet(next); + setNoSettReport(next); + next->mAppRate=0; + } + if (isLoadCCA(next)) { + unsetCongestionControl(next); + } + if (isIsochronous(next)) { + unsetIsochronous(next); + } if (next != NULL) { unsetBounceBack(next); unsetConnectOnly(next); @@ -439,15 +445,24 @@ void client_init(struct thread_Settings *clients) { } else if (isWorkingLoadDown(clients)) { setReverse(next); } - if (isBounceBack(clients) && (clients->mWorkingLoadThreads > 1)) { - Iperf_push_host(clients); - } + Iperf_push_host(clients); // Bump the bounceback time to include the delay time if (next->txholdback_timer.tv_sec || next->txholdback_timer.tv_usec) { // mAmount units are 10 ms next->mAmount += (next->txholdback_timer.tv_sec * 100); next->mAmount += (next->txholdback_timer.tv_usec / 10000); } + if (isLoadCCA(next)) { + char *tmp = new char[strlen(next->mLoadCCA) + 1]; + if (tmp) { + if (next->mCongestion) + DELETE_ARRAY(next->mCongestion); + setCongestionControl(next); + strcpy(tmp, next->mLoadCCA); + tmp[strlen(next->mLoadCCA)] = '\0'; + next->mCongestion = tmp; + } + } itr->runNow = next; itr = next; } @@ -475,4 +490,16 @@ void listeners_init(struct thread_Settings *listener) { itr = next; } } + // See if a working load TCP listener is needed + if (isUDP(listener) && (isWorkingLoadUp(listener) || isWorkingLoadDown(listener))) { + Settings_Copy(listener, &next, DEEP_COPY); + if (next != NULL) { + unsetUDP(next); + next->mAppRate = 0; + unsetBWSet(next); + setNoSettReport(next); + itr->runNow = next; + itr = next; + } + } } diff --git a/src/Listener.cpp b/src/Listener.cpp index a517cbc..c6ea969 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -1,16 +1,15 @@ /*--------------------------------------------------------------- - * Copyright (c) 1999,2000,2001,2002,2003 - * The Board of Trustees of the University of Illinois - * All Rights Reserved. - *--------------------------------------------------------------- + * Copyright (c) 1999,2000,2001,2002,2003 The Board of Trustees of the + * University of Illinois All Rights Reserved. + * --------------------------------------------------------------- * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software (Iperf) and associated * documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do - * so, subject to the following conditions: + * without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: * * * Redistributions of source code must retain the above @@ -139,7 +138,8 @@ void Listener::Run () { } if (!isUDP(mSettings)) { // TCP needs just one listen - my_listen(); // This will set ListenSocket to a new sock fd + if (!my_listen()) // This will set ListenSocket to a new sock fd + return; } bool mMode_Time = isServerModeTime(mSettings) && !isDaemon(mSettings); if (mMode_Time) { @@ -150,7 +150,9 @@ void Listener::Run () { mEndTime.add(mSettings->mListenerTimeout); } Timestamp now; + bool need_listen = true; #define SINGLECLIENTDELAY_DURATION 50000 // units is microseconds + while (!sInterupted && mCount) { #ifdef HAVE_THREAD_DEBUG thread_debug("Listener main loop port %d ", mSettings->mPort); @@ -163,8 +165,8 @@ void Listener::Run () { break; } // Serialize in the event the -1 option or --singleclient is set - int tc; - if ((isSingleClient(mSettings) || isMulticast(mSettings)) && \ + int tc = 0; + if ((isSingleClient(mSettings) || (isUDP(mSettings) && isMulticast(mSettings))) && \ mCount && (tc = (thread_numtrafficthreads()) > 0)) { // Start with a delay in the event some traffic // threads are pending to be scheduled and haven't @@ -178,9 +180,14 @@ void Listener::Run () { #endif continue; } - if (isUDP(mSettings)) { + // This will set ListenSocket to a new sock fd + if (isUDP(mSettings) && need_listen) { // UDP needs a new listen per every new socket - my_listen(); // This will set ListenSocket to a new sock fd + if (!my_listen()) { + break; + } else { + need_listen = false; + } } // Use a select() with a timeout if -t is set or if this is a v1 -r or -d test fd_set set; @@ -195,12 +202,6 @@ void Listener::Run () { timeout.tv_sec = static_cast<long>(mSettings->mListenerTimeout); timeout.tv_usec = (static_cast<long>(mSettings->mListenerTimeout) * 1000000) % 1000000; } - if (isTxStartTime(mSettings)) { - now.setnow(); - long adjsecs = (mSettings->txstart_epoch.tv_sec - now.getSecs()); - if (adjsecs > 0) - timeout.tv_sec += adjsecs + 1; - } FD_ZERO(&set); FD_SET(ListenSocket, &set); if (!(select(ListenSocket + 1, &set, NULL, NULL, &timeout) > 0)) { @@ -234,7 +235,7 @@ void Listener::Run () { Settings_Destroy(server); continue; } - + need_listen = true; #ifdef HAVE_THREAD_DEBUG thread_debug("Listener thread accepted server sock=%d transferID", server->mSock, server->mTransferID); #endif @@ -309,7 +310,7 @@ void Listener::Run () { if (isUDP(server) && isCompat(mSettings)) { setSeqNo64b(server); } - setTransferID(server, 0); + setTransferID(server, NORMAL); if ((mSettings->mReportMode == kReport_CSV) && server->mSumReport && !server->mSumReport->sum_reverse_set) { format_ips_port_string(&server->mSumReport->info, 1); server->mSumReport->sum_reverse_set = true; @@ -334,7 +335,7 @@ void Listener::Run () { if (listener_client_settings) { if (server->mMode != kTest_Normal) listener_client_settings->mTransferID = 0; - setTransferID(listener_client_settings, 1); + setTransferID(listener_client_settings, REVERSED); if (isFullDuplex(listener_client_settings) || isReverse(listener_client_settings)) Iperf_push_host(listener_client_settings); if (isFullDuplex(server)) { @@ -344,9 +345,9 @@ void Listener::Run () { // now that it's know to be full duplex. This wasn't known // during accept() SetSumHandlers(server, server->mSumReport); - server->mSumReport->sum_fd_set = 1; + server->mSumReport->sum_fd_set = true; } - server->mFullDuplexReport = InitSumReport(server, server->mSock, 1); + server->mFullDuplexReport = InitSumReport(server, server->mSock, true); listener_client_settings->mFullDuplexReport = server->mFullDuplexReport; #if HAVE_THREAD_DEBUG thread_debug("FullDuplex report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport); @@ -368,7 +369,7 @@ void Listener::Run () { } } } - setTransferID(server, 0); + setTransferID(server, NORMAL); if (isConnectionReport(server) && !isSumOnly(server)) { struct ReportHeader *reporthdr = InitConnectionReport(server); struct ConnectionInfo *cr = static_cast<struct ConnectionInfo *>(reporthdr->this_report); @@ -393,7 +394,7 @@ void Listener::Run () { * wildcard server address, specifying what incoming interface to * accept connections on. * ------------------------------------------------------------------- */ -void Listener::my_listen () { +bool Listener::my_listen () { int rc; int type; int domain; @@ -414,20 +415,13 @@ void Listener::my_listen () { // for the case of L2 testing and UDP, a new AF_PACKET // will be created to supercede this one type = (isUDP(mSettings) ? SOCK_DGRAM : SOCK_STREAM); - domain = (SockAddr_isIPv6(&mSettings->local) ? -#if HAVE_IPV6 - AF_INET6 -#else - AF_INET -#endif - : AF_INET); + domain = SockAddr_getAFdomain(&mSettings->local); #ifdef WIN32 - if (SockAddr_isMulticast(&mSettings->local)) { + if (SockAddr_isMulticast(&mSettings->multicast_group)) { // Multicast on Win32 requires special handling ListenSocket = WSASocket(domain, type, 0, 0, 0, WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF); WARN_errno(ListenSocket == INVALID_SOCKET, "socket"); - } else #endif { @@ -436,31 +430,46 @@ void Listener::my_listen () { } mSettings->mSock = ListenSocket; SetSocketOptions(mSettings); - // reuse the address, so we can run if a former server was killed off - int boolean = 1; - Socklen_t len = sizeof(boolean); - rc = setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&boolean), len); - // bind socket to server address + SetSocketBindToDeviceIfNeeded(mSettings); + rc = SOCKET_ERROR; + if (isUDP(mSettings) && SockAddr_isMulticast(&mSettings->multicast_group)) { +#if HAVE_MULTICAST #ifdef WIN32 - if (SockAddr_isMulticast(&mSettings->local)) { // Multicast on Win32 requires special handling - rc = WSAJoinLeaf(ListenSocket, (sockaddr*) &mSettings->local, mSettings->size_local,0,0,0,0,JL_BOTH); + rc = WSAJoinLeaf(ListenSocket, reinterpret_cast<sockaddr*> (&mSettings->local), mSettings->size_local,0,0,0,0,JL_BOTH); WARN_errno(rc == SOCKET_ERROR, "WSAJoinLeaf (aka bind)"); - } else +#else +#if 0 && (HAVE_DECL_IP_ADD_MEMBERSHIP || HAVE_DECL_MCAST_JOIN_GROUP) // possible future, bind to all including unicast + iperf_sockaddr tmp; + memcpy(&tmp, &mSettings->local, sizeof(tmp)); + SockAddr_setAddressAny(&tmp); // the multicast join will take care of this + rc = bind(ListenSocket, reinterpret_cast<sockaddr*>(&tmp), mSettings->size_local); + printf("***** any bind\n"); +#else + rc = bind(ListenSocket, reinterpret_cast<sockaddr*> (&mSettings->local), mSettings->size_local); +// printf("***** single bind\n"); #endif - { - rc = bind(ListenSocket, reinterpret_cast<sockaddr*>(&mSettings->local), mSettings->size_local); - FAIL_errno(rc == SOCKET_ERROR, "listener bind", mSettings); + FAIL_errno(rc == SOCKET_ERROR, "listener bind", mSettings); + // if UDP and multicast, join the group + if (iperf_multicast_join(mSettings) != IPERF_MULTICAST_JOIN_SUCCESS) { + rc = SOCKET_ERROR; } +#endif +#else + fprintf(stderr, "Multicast not supported"); +#endif // HAVE_MULTICAST + } else { + // bind socket for unicast + rc = bind(ListenSocket, reinterpret_cast<sockaddr*>(&mSettings->local), mSettings->size_local); + } + FAIL_errno(rc == SOCKET_ERROR, "listener bind", mSettings); } - // update the reporter thread - if (isReport(mSettings) && isSettingsReport(mSettings)) { - struct ReportHeader *report_settings = InitSettingsReport(mSettings); - assert(report_settings != NULL); - // disable future settings reports, listener should only do it once - unsetReport(mSettings); - PostReport(report_settings); + if (isSettingsReport(mSettings)) { + struct ReportHeader *tmp = InitSettingsReport(mSettings); + setNoSettReport(mSettings); + assert(tmp!=NULL); + PostReport(tmp); } // listen for connections (TCP only). @@ -472,253 +481,10 @@ void Listener::my_listen () { rc = listen(ListenSocket, INT_MAX); } WARN_errno(rc == SOCKET_ERROR, "listen"); - } else { -#ifndef WIN32 - // if UDP and multicast, join the group - if (SockAddr_isMulticast(&mSettings->local)) { -#ifdef HAVE_MULTICAST - my_multicast_join(); -#else - fprintf(stderr, "Multicast not supported"); -#endif // HAVE_MULTICAST - } -#endif } + return true; } // end my_listen() -/* ------------------------------------------------------------------- - * Joins the multicast group or source and group (SSM S,G) - * - * taken from: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.hale001/ipv6d0141001708.htm - * - * Multicast function IPv4 IPv6 Protocol-independent - * ================== ==== ==== ==================== - * Level of specified option on setsockopt()/getsockopt() IPPROTO_IP IPPROTO_IPV6 IPPROTO_IP or IPPROTO_IPV6 - * Join a multicast group IP_ADD_MEMBERSHIP IPV6_JOIN_GROUP MCAST_JOIN_GROUP - * Leave a multicast group or leave all sources of that - * multicast group IP_DROP_MEMBERSHIP IPV6_LEAVE_GROUP MCAST_LEAVE_GROUP - * Select outbound interface for sending multicast datagrams IP_MULTICAST_IF IPV6_MULTICAST_IF NA - * Set maximum hop count IP_MULTICAST_TTL IPV6_MULTICAST_HOPS NA - * Enable multicast loopback IP_MULTICAST_LOOP IPV6_MULTICAST_LOOP NA - * Join a source multicast group IP_ADD_SOURCE_MEMBERSHIP NA MCAST_JOIN_SOURCE_GROUP - * Leave a source multicast group IP_DROP_SOURCE_MEMBERSHIP NA MCAST_LEAVE_SOURCE_GROUP - * Block data from a source to a multicast group IP_BLOCK_SOURCE NA MCAST_BLOCK_SOURCE - * Unblock a previously blocked source for a multicast group IP_UNBLOCK_SOURCE NA MCAST_UNBLOCK_SOURCE - * - * - * Reminder: The os will decide which version of IGMP or MLD to use. This may be controlled by system settings, e.g.: - * - * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep mld | grep force - * net.ipv6.conf.all.force_mld_version = 0 - * net.ipv6.conf.default.force_mld_version = 0 - * net.ipv6.conf.lo.force_mld_version = 0 - * net.ipv6.conf.eth0.force_mld_version = 0 - * - * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep igmp | grep force - * net.ipv4.conf.all.force_igmp_version = 0 - * net.ipv4.conf.default.force_igmp_version = 0 - * net.ipv4.conf.lo.force_igmp_version = 0 - * net.ipv4.conf.eth0.force_igmp_version = 0 - * - * ------------------------------------------------------------------- */ -void Listener::my_multicast_join () { - // This is the older mulitcast join code. Both SSM and binding the - // an interface requires the newer socket options. Using the older - // code here will maintain compatiblity with previous iperf versions - if (!isSSMMulticast(mSettings) && !mSettings->mIfrname) { - if (!SockAddr_isIPv6(&mSettings->local)) { - struct ip_mreq mreq; - memcpy(&mreq.imr_multiaddr, SockAddr_get_in_addr(&mSettings->local), \ - sizeof(mreq.imr_multiaddr)); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - int rc = setsockopt(ListenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, - reinterpret_cast<char*>(&mreq), sizeof(mreq)); - WARN_errno(rc == SOCKET_ERROR, "multicast join"); -#if HAVE_DECL_IP_MULTICAST_ALL - int mc_all = 0; - rc = setsockopt(ListenSocket, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all)); - WARN_errno(rc == SOCKET_ERROR, "ip_multicast_all"); -#endif - } else { -#if (HAVE_IPV6 && HAVE_IPV6_MULTICAST && (HAVE_DECL_IPV6_JOIN_GROUP || HAVE_DECL_IPV6_ADD_MEMBERSHIP)) - struct ipv6_mreq mreq; - memcpy(&mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr(&mSettings->local), sizeof(mreq.ipv6mr_multiaddr)); - mreq.ipv6mr_interface = 0; -#if HAVE_DECL_IPV6_JOIN_GROUP - int rc = setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, \ - reinterpret_cast<char*>(&mreq), sizeof(mreq)); -#else - int rc = setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, \ - reinterpret_cast<char*>(&mreq), sizeof(mreq)); -#endif - FAIL_errno(rc == SOCKET_ERROR, "multicast v6 join", mSettings); -#else - fprintf(stderr, "IPv6 multicast is not supported on this platform\n"); -#endif - } - } else { - int rc; -#ifdef HAVE_SSM_MULTICAST - // Here it's either an SSM S,G multicast join or a *,G with an interface specifier - // Use the newer socket options when these are specified - socklen_t socklen = sizeof(struct sockaddr_storage); - int iface=0; -#ifdef HAVE_NET_IF_H - /* Set the interface or any */ - if (mSettings->mIfrname) { - iface = if_nametoindex(mSettings->mIfrname); - FAIL_errno(!iface, "mcast if_nametoindex",mSettings); - } else { - iface = 0; - } -#endif - - if (isIPV6(mSettings)) { -#if HAVE_IPV6_MULTICAST - if (mSettings->mSSMMulticastStr) { - struct group_source_req group_source_req; - struct sockaddr_in6 *group; - struct sockaddr_in6 *source; - - memset(&group_source_req, 0, sizeof(struct group_source_req)); - - group_source_req.gsr_interface = iface; - group=reinterpret_cast<struct sockaddr_in6*>(&group_source_req.gsr_group); - source=reinterpret_cast<struct sockaddr_in6*>(&group_source_req.gsr_source); - source->sin6_family = AF_INET6; - group->sin6_family = AF_INET6; - /* Set the group */ - rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen); - FAIL_errno(rc == SOCKET_ERROR, "mcast join source group getsockname",mSettings); - group->sin6_port = 0; /* Ignored */ - - /* Set the source, apply the S,G */ - rc=inet_pton(AF_INET6, mSettings->mSSMMulticastStr,&source->sin6_addr); - FAIL_errno(rc != 1, "mcast v6 join source group pton",mSettings); - source->sin6_port = 0; /* Ignored */ -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - source->sin6_len = group->sin6_len; -#endif - rc = -1; -#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP - rc = setsockopt(ListenSocket,IPPROTO_IPV6,MCAST_JOIN_SOURCE_GROUP, reinterpret_cast<const char *>(&group_source_req), - sizeof(group_source_req)); -#endif - FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join source group",mSettings); - } else { - struct group_req group_req; - struct sockaddr_in6 *group; - - memset(&group_req, 0, sizeof(struct group_req)); - - group_req.gr_interface = iface; - group=reinterpret_cast<struct sockaddr_in6*>(&group_req.gr_group); - group->sin6_family = AF_INET6; - /* Set the group */ - rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen); - FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group getsockname",mSettings); - group->sin6_port = 0; /* Ignored */ - rc = -1; -#if HAVE_DECL_MCAST_JOIN_GROUP - rc = setsockopt(ListenSocket,IPPROTO_IPV6,MCAST_JOIN_GROUP, reinterpret_cast<const char *>(&group_req), - sizeof(group_source_req)); -#endif - FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group",mSettings); - } -#else - fprintf(stderr, "Unfortunately, IPv6 multicast is not supported on this platform\n"); -#endif - } else { - if (mSettings->mSSMMulticastStr) { - struct sockaddr_in *group; - struct sockaddr_in *source; - - // Fill out both structures because we don't which one will succeed - // and both may need to be tried -#ifdef HAVE_STRUCT_IP_MREQ_SOURCE - struct ip_mreq_source imr; - memset (&imr, 0, sizeof (imr)); -#endif -#ifdef HAVE_STRUCT_GROUP_SOURCE_REQ - struct group_source_req group_source_req; - memset(&group_source_req, 0, sizeof(struct group_source_req)); - group_source_req.gsr_interface = iface; - group=reinterpret_cast<struct sockaddr_in*>(&group_source_req.gsr_group); - source=reinterpret_cast<struct sockaddr_in*>(&group_source_req.gsr_source); -#else - struct sockaddr_in imrgroup; - struct sockaddr_in imrsource; - group = &imrgroup; - source = &imrsource; -#endif - source->sin_family = AF_INET; - group->sin_family = AF_INET; - /* Set the group */ - rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen); - FAIL_errno(rc == SOCKET_ERROR, "mcast join source group getsockname",mSettings); - group->sin_port = 0; /* Ignored */ - - /* Set the source, apply the S,G */ - rc=inet_pton(AF_INET,mSettings->mSSMMulticastStr,&source->sin_addr); - FAIL_errno(rc != 1, "mcast join source pton",mSettings); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - source->sin_len = group->sin_len; -#endif - source->sin_port = 0; /* Ignored */ - rc = -1; - -#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP - rc = setsockopt(ListenSocket,IPPROTO_IP,MCAST_JOIN_SOURCE_GROUP, reinterpret_cast<const char *>(&group_source_req), - sizeof(group_source_req)); -#endif - -#if HAVE_DECL_IP_ADD_SOURCE_MEMBERSHIP -#ifdef HAVE_STRUCT_IP_MREQ_SOURCE - // Some operating systems will have MCAST_JOIN_SOURCE_GROUP but still fail - // In those cases try the IP_ADD_SOURCE_MEMBERSHIP - if (rc < 0) { -#ifdef HAVE_STRUCT_IP_MREQ_SOURCE_IMR_MULTIADDR_S_ADDR - imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr; - imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr; -#else - // Some Android versions declare mreq_source without an s_addr - imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr.s_addr; - imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr.s_addr; -#endif - rc = setsockopt (ListenSocket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, reinterpret_cast<char*>(&imr), sizeof (imr)); - } -#endif -#endif - FAIL_errno(rc == SOCKET_ERROR, "mcast join source group",mSettings); - } else { - struct group_req group_req; - struct sockaddr_in *group; - - memset(&group_req, 0, sizeof(struct group_req)); - - group_req.gr_interface = iface; - group=reinterpret_cast<struct sockaddr_in*>(&group_req.gr_group); - group->sin_family = AF_INET; - /* Set the group */ - rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen); - FAIL_errno(rc == SOCKET_ERROR, "mcast join group getsockname",mSettings); - group->sin_port = 0; /* Ignored */ - rc = -1; -#if HAVE_DECL_MCAST_JOIN_GROUP - rc = setsockopt(ListenSocket,IPPROTO_IP,MCAST_JOIN_GROUP, reinterpret_cast<const char *>(&group_req), - sizeof(group_source_req)); -#endif - FAIL_errno(rc == SOCKET_ERROR, "mcast join group",mSettings); - } - } - -#else - fprintf(stderr, "Unfortunately, SSM is not supported on this platform\n"); - exit(-1); -#endif - } -} -// end my_multicast_join() bool Listener::L2_setup (thread_Settings *server, int sockfd) { #if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET) @@ -905,6 +671,7 @@ int Listener::udp_accept (thread_Settings *server) { // most likely not a new client thread requiring a new server thread, but remnants of an // old one that already ended. Hence, the Listener should ignore "first packets" when // they have negative seq numbers. + RETRYREAD: do { packetID = 0; nread = recvfrom(ListenSocket, server->mBuf, server->mBufLen, 0, \ @@ -925,22 +692,12 @@ int Listener::udp_accept (thread_Settings *server) { Timestamp now; server->accept_time.tv_sec = now.getSecs(); server->accept_time.tv_usec = now.getUsecs(); -#if HAVE_THREAD_DEBUG - { - char tmpaddr[200]; - size_t len=200; - unsigned short port = SockAddr_getPort(&server->peer); - SockAddr_getHostAddress(&server->peer, tmpaddr, len); - thread_debug("rcvfrom peer: %s port %d len=%d", tmpaddr, port, nread); - } -#endif - // Handle connection for UDP sockets - int gid = Iperf_push_host_port_conditional(server); -#if HAVE_THREAD_DEBUG - if (gid < 0) - thread_debug("rcvfrom peer: drop duplicate"); -#endif - if (gid > 0) { + // Drop duplicates, may need to use a BPF drop for better performance + // or ebfs + if (!Iperf_push_host(server)) { + packetID = 0; + goto RETRYREAD; + } else { int rc; // We have a new UDP flow (based upon key of quintuple) // so let's hand off this socket @@ -953,11 +710,28 @@ int Listener::udp_accept (thread_Settings *server) { // This connect() routing is only supported with AF_INET or AF_INET6 sockets, // e.g. AF_PACKET sockets can't do this. We'll handle packet sockets later // All UDP accepts here will use AF_INET. This is intentional and needed - rc = connect(server->mSock, reinterpret_cast<struct sockaddr*>(&server->peer), server->size_peer); - FAIL_errno(rc == SOCKET_ERROR, "connect UDP", mSettings); - server->size_local = sizeof(iperf_sockaddr); - getsockname(server->mSock, reinterpret_cast<sockaddr*>(&server->local), &server->size_local); - SockAddr_Ifrname(server); + if (!isMulticast(server)) { + rc = connect(server->mSock, reinterpret_cast<struct sockaddr*>(&server->peer), server->size_peer); + FAIL_errno(rc == SOCKET_ERROR, "connect UDP", mSettings); + server->size_local = sizeof(iperf_sockaddr); + getsockname(server->mSock, reinterpret_cast<sockaddr*>(&server->local), &server->size_local); + SockAddr_Ifrname(server); + } else { + server->size_multicast_group = sizeof(iperf_sockaddr); + iperf_sockaddr sent_dstaddr; + getsockname(server->mSock, reinterpret_cast<sockaddr*>(&sent_dstaddr), &server->size_multicast_group); + int join_send_match = SockAddr_Hostare_Equal(&sent_dstaddr, &server->multicast_group); +#if DEBUG_MCAST + char joinaddr[200]; + char pktaddr[200]; + size_t len=200; + SockAddr_getHostAddress(&sent_dstaddr, joinaddr, len); + SockAddr_getHostAddress(&server->multicast_group, pktaddr, len); + printf("mcast(%d): join addr %s pkt group addr %s\n", join_send_match, joinaddr, pktaddr); +#endif + WARN(!join_send_match, "mcast join and packet group addr"); + // RJM - use a cmesg to read the interface name + } server->firstreadbytes = nread; } } @@ -1046,8 +820,12 @@ bool Listener::apply_client_settings (thread_Settings *server) { bool rc = false; // Set the receive timeout for the very first read - int sorcvtimer = TESTEXCHANGETIMEOUT; // 4 sec in usecs - SetSocketOptionsReceiveTimeout(server, sorcvtimer); + if (mSettings->mListenerTimeout >= 0) { + SetSocketOptionsReceiveTimeout(server, (int) (mSettings->mListenerTimeout * 1000000)); + } else { + int sorcvtimer = DEFAULT_TESTEXCHANGETIMEOUT; // in usecs + SetSocketOptionsReceiveTimeout(server, sorcvtimer); + } server->peer_version_u = 0; server->peer_version_l = 0; server->mMode = kTest_Normal; @@ -1111,26 +889,39 @@ bool Listener::apply_client_settings_udp (thread_Settings *server) { #if HAVE_THREAD_DEBUG thread_debug("UDP small header"); #endif + struct client_udpsmall_testhdr *smallhdr = reinterpret_cast<struct client_udpsmall_testhdr *>(server->mBuf + server->l4payloadoffset); server->sent_time.tv_sec = ntohl(hdr->seqno_ts.tv_sec); server->sent_time.tv_usec = ntohl(hdr->seqno_ts.tv_usec); + server->txstart_epoch.tv_sec = ntohl(smallhdr->start_tv_sec); + server->txstart_epoch.tv_usec = ntohl(smallhdr->start_tv_usec); uint32_t seqno = ntohl(hdr->seqno_ts.id); + if (server->txstart_epoch.tv_sec > 0) { + setTxStartTime(server); + } if (seqno != 1) { fprintf(stderr, "WARN: first received packet (id=%d) was not first sent packet, report start time will be off\n", seqno); } Timestamp now; if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { - fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now (now=%ld send=%ld)\n", MAXDIFFTIMESTAMPSECS, now.getSecs(), server->sent_time.tv_sec); unsetTripTime(server); } else { setTripTime(server); } setEnhanced(server); } else if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND)) { - if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { - if (flags & RUN_NOW) - server->mMode = kTest_DualTest; - else - server->mMode = kTest_TradeOff; + if (flags & HEADER_VERSION1) { + uint32_t tidthreads = ntohl(hdr->base.numThreads); + if (tidthreads & HEADER_HASTRANSFERID) { + tidthreads &= (~HEADER_HASTRANSFERID & HEADER_TRANSFERIDMASK); + server->mPeerTransferID = tidthreads >> HEADER_TRANSFERIDSHIFT; + setSyncTransferID(server); + } else if (!(flags & HEADER_VERSION2)) { + if (flags & RUN_NOW) + server->mMode = kTest_DualTest; + else + server->mMode = kTest_TradeOff; + } } if (flags & HEADER_EXTEND) { upperflags = htons(hdr->extend.upperflags); @@ -1234,6 +1025,18 @@ bool Listener::apply_client_settings_tcp (thread_Settings *server) { } readptr += nread; server->mBounceBackBytes = ntohl(bbhdr->bbsize); + if (server->mBounceBackBytes > server->mBufLen) { + if (isBuflenSet(server)) { + WARN(1, "Buffer length (-l) too small for bounceback request. Increase -l size or don't set (for auto-adjust)"); + rc = false; + goto DONE; + } else { + int read_offset = readptr - server->mBuf; + Settings_Grow_mBuf(server, server->mBounceBackBytes); + readptr = server->mBuf + read_offset; + bbhdr = reinterpret_cast<struct bounceback_hdr *>(server->mBuf); + } + } server->mBounceBackHold = ntohl(bbhdr->bbhold); uint16_t bbflags = ntohs(bbhdr->bbflags); if (bbflags & HEADER_BBCLOCKSYNCED) { @@ -1249,6 +1052,23 @@ bool Listener::apply_client_settings_tcp (thread_Settings *server) { setTcpQuickAck(server); } #endif + if (bbflags & HEADER_BBREPLYSIZE) { + server->mBounceBackReplyBytes = ntohl(bbhdr->bbreplysize); + } else { + server->mBounceBackReplyBytes = server->mBounceBackBytes; + } + if (server->mBounceBackReplyBytes > server->mBufLen) { + if (isBuflenSet(server)) { + WARN(1, "Buffer length (-l) too small for bounceback reply. Increase -l size or don't set (for auto-adjust)"); + rc = false; + goto DONE; + } else { + int read_offset = readptr - server->mBuf; + Settings_Grow_mBuf(server, server->mBounceBackReplyBytes); + readptr = server->mBuf + read_offset; + bbhdr = reinterpret_cast<struct bounceback_hdr *>(server->mBuf); + } + } int remaining = server->mBounceBackBytes - (sizeof(struct bounceback_hdr) + sizeof(uint32_t)); if (remaining < 0) { WARN(1, "bounce back bytes too small"); @@ -1267,9 +1087,15 @@ bool Listener::apply_client_settings_tcp (thread_Settings *server) { bbhdr->bbserverRx_ts.usec = htonl(now.getUsecs()); } else { uint16_t upperflags = 0; + uint16_t lowerflags = 0; int readlen; // figure out the length of the test header if ((readlen = Settings_ClientTestHdrLen(flags, server)) > 0) { + if (readlen > (server->mBufLen - nread)) { + WARN(1, "read tcp header too large"); + rc = false; + goto DONE; + } // read the test settings passed to the server by the client nread += recvn(server->mSock, readptr, (readlen - (int) sizeof(uint32_t)), 0); FAIL_errno((nread < readlen), "read tcp test info", server); @@ -1285,14 +1111,22 @@ bool Listener::apply_client_settings_tcp (thread_Settings *server) { } server->firstreadbytes = nread; struct client_tcp_testhdr *hdr = reinterpret_cast<struct client_tcp_testhdr*>(server->mBuf); - if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { - if (flags & RUN_NOW) - server->mMode = kTest_DualTest; - else - server->mMode = kTest_TradeOff; + if (flags & HEADER_VERSION1) { + uint32_t tidthreads = ntohl(hdr->base.numThreads); + if (tidthreads & HEADER_HASTRANSFERID) { + tidthreads &= (~HEADER_HASTRANSFERID & HEADER_TRANSFERIDMASK); + server->mPeerTransferID = tidthreads >> HEADER_TRANSFERIDSHIFT; + setSyncTransferID(server); + } else if (!(flags & HEADER_VERSION2)) { + if (flags & RUN_NOW) + server->mMode = kTest_DualTest; + else + server->mMode = kTest_TradeOff; + } } if (flags & HEADER_EXTEND) { upperflags = htons(hdr->extend.upperflags); + lowerflags = htons(hdr->extend.lowerflags); server->mTOS = ntohs(hdr->extend.tos); server->peer_version_u = ntohl(hdr->extend.version_u); server->peer_version_l = ntohl(hdr->extend.version_l); @@ -1335,6 +1169,30 @@ bool Listener::apply_client_settings_tcp (thread_Settings *server) { server->mFPS = 1.0; } } + if ((lowerflags & HEADER_CCA) && !isCongestionControl(server)) { +#if HAVE_DECL_TCP_CONGESTION + int ccalen = ntohs(hdr->cca.cca_length); + setCongestionControl(server); + setEnhanced(server); + server->mCongestion = new char[ccalen+1]; + if (server->mCongestion) { + strncpy(server->mCongestion, hdr->cca.value, ccalen); + server->mCongestion[ccalen] = '\0'; + Socklen_t len = strlen(server->mCongestion) + 1; + int rc = setsockopt(server->mSock, IPPROTO_TCP, TCP_CONGESTION, + server->mCongestion, len); + if (rc == SOCKET_ERROR) { + fprintf(stderr, "Attempt to set '%s' congestion control failed: %s\n", + server->mCongestion, strerror(errno)); + unsetCongestionControl(server); + DELETE_ARRAY(server->mCongestion); + } + } +#endif + } + if (lowerflags & HEADER_BARRIER_TIME) { + server->barrier_time = ntohl(hdr->extend.barrier_usecs); + } if (flags & HEADER_VERSION2) { if (upperflags & HEADER_FULLDUPLEX) { setFullDuplex(server); diff --git a/src/Locale.c b/src/Locale.c index 09333b0..34c82dd 100644 --- a/src/Locale.c +++ b/src/Locale.c @@ -50,8 +50,25 @@ * ------------------------------------------------------------------- * Strings and other stuff that is locale specific. * ------------------------------------------------------------------- */ + +/* Format specifiers per inttypes.h + * + * macro description example + * PRIxMAX printf specifier for intmax_t PRIiMAX is the equivalent of i (in "%i") for intmax_t values + * PRIxN printf specifier for intN_t PRId16 is the equivalent of d (in "%d") for int16_t values + * PRIxLEASTN printf specifier for int_leastN_t PRIuLEAST32 is the equivalent of u (in "%u") for uint32_t values + * PRIxFASTN printf specifier for int_fastN_t PRIxFAST8 is the equivalent of x (in "%x") for uint8_t values + * PRIxPTR printf specifier for intptr_t PRIuPTR is the equivalent of u (in "%u") for uintptr_t values + * + Where: + * x is one of d, i, o,u or x (for the printf specifiers this can also be an uppercase X).* + * N is 8, 16, 32, 64, or any other type width supported by the library in <cstdint>. + * * The specifier assumes the type to be signed for i and d, and unsigned for o, u, x and X. + */ + #include "headers.h" #include "version.h" +#include "gettcpinfo.h" #ifdef __cplusplus extern "C" { @@ -73,12 +90,14 @@ Client/Server:\n\ -e, --enhanced use enhanced reporting giving more tcp/udp and traffic information\n\ -f, --format [kmgKMG] format to report: Kbits, Mbits, KBytes, MBytes\n\ --hide-ips hide ip addresses and host names within outputs\n\ + --histograms enable histograms (see client or server for more)\n\ -i, --interval # seconds between periodic bandwidth reports\n\ -l, --len #[kmKM] length of buffer in bytes to read or write (Defaults: TCP=128K, v4 UDP=1470, v6 UDP=1450)\n\ -m, --print_mss print TCP maximum segment size\n\ -o, --output <filename> output the report or error message to this specified file\n\ -p, --port # client/server port to listen/send on and to connect\n\ --permit-key permit key to be used to verify client and server (TCP only)\n\ + --tcp-tx-delay set transmit delay\n\ --sum-only output sum only reports\n\ -u, --udp use UDP rather than TCP\n\ --utc use coordinated universal time (UTC) with time output\n\ @@ -102,6 +121,7 @@ Server specific:\n\ --jitter-histograms enable jitter histograms\n\ --permit-key-timeout set the timeout for a permit key in seconds\n\ --tcp-rx-window-clamp set the TCP receive window clamp size in bytes\n\ + --test-exchange-timeout set the timeout on the test exchange, use 0 for no timeout\n\ --tap-dev #[<dev>] use TAP device to receive at L2 layer\n\ -t, --time # time in seconds to listen for new connections as well as to receive traffic (default not set)\n\ -B, --bind <ip>[%<dev>] bind to multicast address and optional device\n\ @@ -119,25 +139,34 @@ const char usage_long2[] = "\ Client specific:\n\ --bounceback request a bounceback test (use -l for size, defaults to 100 bytes)\n\ --bounceback-hold request the server to insert a delay of n milliseconds between its read and write\n\ - --bounceback-period request the client schedule a send every n milliseconds\n\ --bounceback-no-quickack request the server not set the TCP_QUICKACK socket option (disabling TCP ACK delays) during a bounceback test\n\ + --bounceback-period request the client schedule a send every n milliseconds\n \ + --bounceback-reply set the bounceback reply message size (defaults to symmetric)\n \ + --bounceback-txdelay request the bounceback server delay n seconds between the request and the reply\n \ -c, --client <host> run in client mode, connecting to <host>\n\ --connect-only run a connect only test\n\ - --connect-retries # number of times to retry tcp connect\n\ + --connect-retry-timer minimum time interval in seconds between application level connect retries\n\ + --connect-retry-time time interval in seconds to attempt application level connect retries \n\ + --dscp set the DSCP field (masking ECN bits) in TOS byte\n\ -d, --dualtest Do a bidirectional test simultaneously (multiple sockets)\n\ --fq-rate #[kmgKMG] bandwidth to socket pacing\n\ + --fq-rate-step #[kmgKMG] step value to socket pacing\n\ + --fq-rate-step-interval #[kmgKMG] step interval (in seconds) for socket pacing\n\ --full-duplex run full duplex test using same socket\n\ - --ipg set the the interpacket gap (milliseconds) for packets within an isochronous frame\n\ - --isochronous <frames-per-second>:<mean>,<stddev> send traffic in bursts (frames - emulate video traffic)\n\ + --histograms enable histograms for --tcp-write-times\n\ + --ignore-shutdown don't wait on the TCP shutdown or close (fin & finack) rather use the final write as the ending event\n\ --incr-dstip Increment the destination ip with parallel (-P) traffic threads\n\ --incr-dstport Increment the destination port with parallel (-P) traffic threads\n\ --incr-srcip Increment the source ip with parallel (-P) traffic threads\n\ --incr-srcport Increment the source port with parallel (-P) traffic threads\n\ + --ipg set the the interpacket gap (milliseconds) for packets within an isochronous frame\n\ + --isochronous <frames-per-second>:<mean>,<stddev> send traffic in bursts (frames - emulate video traffic)\n\ --local-only Set don't route on socket\n\ --near-congestion=[w] Use a weighted write delay per the sampled TCP RTT (experimental)\n\ --no-connect-sync No sychronization after connect when -P or parallel traffic threads\n\ --no-udp-fin No final server to client stats at end of UDP test\n\ -n, --num #[kmgKMG] number of bytes to transmit (instead of -t)\n\ + --sync-transfer-id pass the clients' transfer id(s) to the server so both will use the same id in their respective outputs\n\ -r, --tradeoff Do a fullduplexectional test individually\n\ --tcp-quickack set the socket's TCP_QUICKACK option (off by default)\n\ --tcp-write-prefetch set the socket's TCP_NOTSENT_LOWAT value in bytes and use event based writes\n\ @@ -160,7 +189,8 @@ Client specific:\n\ #endif " -S, --tos IP DSCP or tos settings\n\ -T, --ttl # time-to-live, for multicast (default 1)\n\ - --working-load request a concurrent TCP stream\n\ + --working-load request working load(s)\n\ + --working-load-cca set working load CCA\n\ -V, --ipv6_domain Set the domain to IPv6 (send packets over IPv6)\n\ -X, --peer-detect perform server version detection and version exchange\n\ \n\ @@ -196,6 +226,8 @@ Report bugs to <iperf-users@lists.sourceforge.net>\n"; const char version[] = "iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS "\n"; +const char branch_version[] = +"iperf (branch " IPERF_BRANCH ") (" IPERF_VERSION_DATE ") " IPERF_THREADS "\n"; /* ------------------------------------------------------------------- * settings @@ -213,14 +245,17 @@ const char client_port[] = const char server_pid_port[] = "Server listening on %s port %d with pid %d\n"; +const char server_working_load_port[] = +"Working load listening on %s port %d\n"; + const char server_pid_portrange[] = "Server listening on %s ports %d-%d with pid %d\n"; const char client_pid_port[] = -"Client connecting to %s, %s port %d with pid %d (%d flows)\n"; +"Client connecting to %s, %s port %d with pid %d (%d/%d flows/load)\n"; const char client_pid_port_dev[] = -"Client connecting to %s, %s port %d with pid %d via %s (%d flows)\n"; +"Client connecting to %s, %s port %d with pid %d via %s (%d/%d flows/load)\n"; const char bind_address[] = "Binding to local address %s\n"; @@ -277,10 +312,13 @@ const char client_burstperiodcount[] = "Bursting: %s writes %d times every %0.2f second(s)\n"; const char client_bounceback[] = -"Bounce-back test (size=%s) (server hold req=%d usecs & tcp_quickack)\n"; +"Bounceback test (req/reply size =%s/%s) (server hold req=%d usecs & tcp_quickack)\n"; + +const char client_bbburstperiodcount[] = +"Bursting request %d times every %0.2f second(s)\n"; const char client_bounceback_noqack[] = -"Bounce-back test (size=%s) (server hold req=%d usecs)\n"; +"Bounceback test (req/reply size =%s/%s) (server hold req=%d usecs)\n"; const char server_burstperiod[] = "Burst wait timeout set to (2 * %0.2f) seconds (use --burst-period=<n secs> to change)\n"; @@ -288,6 +326,9 @@ const char server_burstperiod[] = const char client_fq_pacing [] = "fair-queue socket pacing set to %s/s\n"; +const char client_fq_pacing_step [] = +"fair-queue socket pacing set to %s/s (stepping rate by %s/s)\n"; + /* ------------------------------------------------------------------- * Legacy reports * ------------------------------------------------------------------- */ @@ -296,28 +337,28 @@ const char report_bw_header[] = "[ ID] Interval Transfer Bandwidth\n"; const char report_bw_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_sum_bw_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_sumcnt_bw_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_bw_jitter_loss_header[] = "[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams\n"; const char report_bw_jitter_loss_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%)\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%)%s\n"; const char report_sum_bw_jitter_loss_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " (%.2g%%)\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " (%.2g%%)%s\n"; const char report_sumcnt_bw_jitter_loss_header[] = "[SUM-cnt] Interval Transfer Bandwidth Lost/Total PPS\n"; const char report_sumcnt_bw_jitter_loss_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps%s\n"; /* ------------------------------------------------------------------- * Enhanced reports (per -e) @@ -335,172 +376,250 @@ const char server_read_size[] = "Read buffer size"; const char report_bw_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_sum_bw_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_bw_read_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n"; const char report_bw_read_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_sumcnt_bw_read_enhanced_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n"; const char report_sumcnt_bw_read_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_sumcnt_bw_read_triptime_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth InP Reads=Dist\n"; const char report_sumcnt_bw_read_triptime_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_bw_read_enhanced_netpwr_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Burst Latency avg/min/max/stdev (cnt/size) inP NetPwr Reads=Dist\n"; const char report_bw_read_enhanced_netpwr_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_bw_isoch_enhanced_netpwr_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Burst Latency avg/min/max/stdev (cnt/size) NetPwr Reads=Dist\n"; const char report_bw_isoch_enhanced_netpwr_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_sum_bw_read_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; const char report_triptime_enhanced_format[] = -"%s" IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %.4f sec\n"; +"%s" IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %.4f sec%s\n"; #if HAVE_TCP_STATS +#if HAVE_TCP_INFLIGHT +const char report_bw_write_enhanced_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry InF(pkts)/Cwnd(pkts)/RTT(var) NetPwr\n"; + +const char report_bw_write_enhanced_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX " %8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s\n"; + +const char report_write_enhanced_nocwnd_write_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")%s\n"; + +const char report_bw_write_enhanced_fq_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry InF(pkts)/Cwnd(pkts)/RTT(var) fq-rate NetPwr\n"; + +const char report_bw_write_enhanced_fq_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX " %8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s/sec %s%s\n"; + const char report_client_bb_bw_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev Rtry Cwnd/RTT RPS\n"; +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev Rtry Cwnd(pkts)/RTT(var) RPS(avg)\n"; const char report_client_bb_bw_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4d %4dK/%u us %s rps\n"; -#else +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4" PRIuLEAST32 "%4" PRIdMAX "K/(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s rps%s\n"; + +const char report_client_bb_bw_final_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4" PRIuLEAST32 " -(-)/-(-) %s rps%s\n"; + +const char report_write_enhanced_write_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry InF(pkts)/Cwnd(pkts)/RTT(var) NetPwr write-times avg/min/max/stdev (cnt)\n"; + +const char report_write_enhanced_write_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8" PRIuLEAST32 "%7" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")%s\n"; + +#else // no inflight +const char report_bw_write_enhanced_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd(pkts)/RTT(var) NetPwr\n"; + +const char report_bw_write_enhanced_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIuLEAST32 " %8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s\n"; + +const char report_bw_write_enhanced_fq_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd(pkts)/RTT(var) fq-rate NetPwr\n"; + +const char report_bw_write_enhanced_fq_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIuLEAST32 " %8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s/sec %s%s\n"; + const char report_client_bb_bw_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev RPS\n"; +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev Rtry Cwnd(pkts)/RTT(var) RPS(avg)\n"; const char report_client_bb_bw_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %s rps\n"; -#endif -const char report_client_bb_bw_triptime_format[] = -"%s" IPERFTimeFrmt " sec OWD Delays (ms) Cnt=%" PRIdMAX " To=%.3f/%.3f/%.3f/%.3f From=%.3f/%.3f/%.3f/%.3f Asymmetry=%.3f/%.3f/%.3f/%.3f %s rps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4" PRIuLEAST32 " %4" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s rps%s\n"; + +const char report_client_bb_bw_final_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4" PRIuLEAST32 " -(-)/-(-) %s rps%s\n"; const char report_write_enhanced_write_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr write-times avg/min/max/stdev (cnt)\n"; +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd(pkts)/RTT(var) NetPwr write-times avg/min/max/stdev (cnt)\n"; const char report_write_enhanced_write_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX "%8dK/%u us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIuLEAST32 "%8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")%s\n"; const char report_write_enhanced_nocwnd_write_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIuLEAST32 " NA/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")%s\n"; - -#if HAVE_TCP_STATS -const char report_bw_write_enhanced_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT(var) NetPwr\n"; +#endif //HAVE_TCP_INFLIGHT const char report_sum_bw_write_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIuLEAST32 " %s\n"; -const char report_bw_write_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX " %8dK/%u(%u) us %s\n"; +const char report_bw_write_enhanced_fq_final_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIuLEAST32 " %8dK/%u(%u) us %s%s\n"; const char report_bw_write_enhanced_nocwnd_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIuLEAST32 " NA/%" PRIuLEAST32 "(%" PRIuLEAST32 ")us %s\n"; const char report_write_enhanced_isoch_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT isoch:tx/miss/slip NetPwr\n"; +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd(pkts)/RTT(var) isoch:tx/miss/slip NetPwr\n"; const char report_write_enhanced_isoch_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8d %8dK/%u us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8" PRIuLEAST32 " %8" PRIdMAX "K(%" PRIuLEAST32 ")/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s%s\n"; const char report_write_enhanced_isoch_nocwnd_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIuLEAST32 " NA/%" PRIuLEAST32 "(%" PRIuLEAST32 ") us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s%s\n"; -#else +#else //no tcpstat + +const char report_client_bb_bw_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev RPS(avg)\n"; + +const char report_client_bb_bw_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %s rps%s\n"; + +const char report_bw_write_enhanced_fq_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err fq-rate NetPwr\n"; + +const char report_bw_write_enhanced_fq_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%s/sec %s%s\n"; const char report_bw_write_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err\n"; const char report_bw_write_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%s\n"; const char report_sum_bw_write_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%s\n"; const char report_write_enhanced_isoch_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err isoch:tx/miss/slip\n"; const char report_write_enhanced_isoch_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "\n"; -#endif +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "%s\n"; + +const char report_write_enhanced_write_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err NetPwr write-times avg/min/max/stdev (cnt)\n"; + +const char report_write_enhanced_write_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")%s\n"; + +const char report_bw_write_fq_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err fq-rate\n"; + +const char report_bw_write_fq_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %s/sec%s\n"; + +#endif //no tcp stat + +const char report_client_bb_bw_triptime_format[] = +"%s" IPERFTimeFrmt " sec OWD (ms) Cnt=%" PRIdMAX " TX=%.3f/%.3f/%.3f/%.3f RX=%.3f/%.3f/%.3f/%.3f Asymmetry=%.3f/%.3f/%.3f/%.3f%s\n"; const char report_sumcnt_bw_write_enhanced_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry\n"; const char report_sumcnt_bw_write_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "%s\n"; const char report_bw_pps_enhanced_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS\n"; +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err/Timeo PPS\n"; const char report_bw_pps_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX "%8.0f pps%s\n"; const char report_sumcnt_bw_pps_enhanced_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS\n"; const char report_sumcnt_bw_pps_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps%s\n"; const char report_sumcnt_udp_enhanced_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Lost/Total PPS\n"; const char report_sumcnt_udp_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps%s\n"; const char report_sumcnt_udp_triptime_header[] = "[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Lost/Total Rx/inP PPS\n"; const char report_sumcnt_udp_triptime_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %" PRIdMAX "/%.0f %8.0f pps\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %" PRIdMAX "/%.0f %8.0f pps%s\n"; const char report_bw_pps_enhanced_isoch_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS isoch:tx/miss/slip\n"; const char report_bw_pps_enhanced_isoch_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps %3" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps %3" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "%s\n"; const char report_sum_bw_pps_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps%s\n"; const char report_bw_jitter_loss_pps_header[] = "[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams PPS\n"; const char report_bw_jitter_loss_pps_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %8.0f pps%s\n"; +#if HAVE_DECL_MSG_TRUNC const char report_bw_jitter_loss_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ - Latency avg/min/max/stdev PPS NetPwr\n"; + Latency avg/min/max/stdev PPS Read/Timeo/Trunc NetPwr\n"; const char report_bw_jitter_loss_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX " %s%s\n"; const char report_bw_jitter_loss_enhanced_triptime_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ - Latency avg/min/max/stdev PPS Rx/inP NetPwr\n"; + Latency avg/min/max/stdev PPS Rx/inP Read/Timeo/Trunc NetPwr\n"; const char report_bw_jitter_loss_enhanced_triptime_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%s %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%s %" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX " %s%s\n"; +#else +const char report_bw_jitter_loss_enhanced_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ + Latency avg/min/max/stdev PPS Read/Timeo NetPwr\n"; + +const char report_bw_jitter_loss_enhanced_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%" PRIdMAX " %s%s\n"; +const char report_bw_jitter_loss_enhanced_triptime_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ + Latency avg/min/max/stdev PPS Rx/inP Read/Timeo NetPwr\n"; + +const char report_bw_jitter_loss_enhanced_triptime_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%s %" PRIdMAX "/%" PRIdMAX "%s%s\n"; +#endif //msgtrunc const char report_bw_jitter_loss_enhanced_isoch_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \ Latency avg/min/max/stdev PPS NetPwr Isoch:rx/lost\n"; @@ -510,16 +629,16 @@ const char report_bw_jitter_loss_enhanced_isoch_triptime_header[] = Latency avg/min/max/stdev PPS Frame:rx/lost Frame-latency avg/min/max/stdev NetPwr\n"; const char report_bw_jitter_loss_enhanced_isoch_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %3d/%d %.3f/%.3f/%.3f/%.3f ms %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %3" PRIdMAX " /% " PRIdMAX " %.3f/%.3f/%.3f/%.3f ms %s%s\n"; const char report_sum_bw_jitter_loss_enhanced_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps%s\n"; const char report_sumcnt_bw_jitter_loss_enhanced_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps%s\n"; const char report_bw_jitter_loss_suppress_enhanced_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps %" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX "%s\n"; /* * Frame interval reports @@ -531,10 +650,10 @@ const char report_frame_jitter_loss_enhanced_header[] = Latency avg/min/max/stdev PPS inP NetPwr\n"; const char report_frame_jitter_loss_enhanced_format[] = -"%s" IPERFFTimeFrmt "(%0.4f) sec %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %2.0f pkts %s\n"; +"%s" IPERFFTimeFrmt "(%0.4f) sec %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %2.0f pkts %s%s\n"; const char report_frame_jitter_loss_suppress_enhanced_format[] = -"%s" IPERFTimeFrmt "(%0.4f) sec %" PRIdMAX " %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps\n"; +"%s" IPERFTimeFrmt "(%0.4f) sec %" PRIdMAX " %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps %s%s\n"; const char report_frame_tcp_enhanced_header[] = "[ ID] Interval(f-transit)" IPERFFTimeSpace "Transfer Bandwidth FrameID\n"; @@ -543,7 +662,7 @@ const char report_burst_read_tcp_header[] = "[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime (DC%) Reads=Dist NetPwr\n"; const char report_burst_read_tcp_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms (%.2g%%) %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms (%.2g%%) %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d %s%s\n"; const char report_burst_read_tcp_final_format[] = "%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; @@ -552,63 +671,57 @@ const char report_burst_write_tcp_header[] = "[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err Rtry Cwnd/RTT NetPwr\n"; const char report_burst_write_tcp_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "%8dK/%u %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "%8dK/%u %s%s\n"; #else const char report_burst_write_tcp_header[] = "[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err\n"; const char report_burst_write_tcp_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX"\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX"%s\n"; #endif const char report_burst_write_tcp_nocwnd_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "NA/%u %s\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "NA/%"PRIuLEAST32 "(%" PRIuLEAST32 ")%s\n"; const char report_burst_write_tcp_final_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d%s\n"; /* ------------------------------------------------------------------- * Fullduplex reports * ------------------------------------------------------------------- */ const char report_bw_sum_fullduplex_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_bw_sum_fullduplex_enhanced_format[] = -"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec\n"; +"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec%s\n"; const char report_udp_fullduplex_header[] = "[ ID] Interval Transfer Bandwidth Datagrams PPS\n"; -const char report_sumcnt_udp_fullduplex_header[] = -"[SUM-cnt] Interval Transfer Bandwidth Datagrams PPS\n"; - -const char report_sumcnt_udp_fullduplex_format[] = -"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n"; - const char report_udp_fullduplex_format[] = -"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n"; +"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps%s\n"; const char report_udp_fullduplex_enhanced_format[] = -"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n"; +"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps%s\n"; const char report_udp_fullduplex_sum_format[] = -"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n"; +"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps%s\n"; /* ------------------------------------------------------------------- * Misc reports * ------------------------------------------------------------------- */ const char report_outoforder[] = -"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order%s\n"; const char report_sumcnt_outoforder[] = -"[SUM-%d] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"[SUM-%d] " IPERFTimeFrmt " sec %d datagrams received out-of-order%s\n"; const char report_l2statistics[] = "%s" IPERFTimeFrmt " sec L2 processing detected errors, total(length/checksum/unknown) = %" PRIdMAX "(%" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX ")\n"; const char report_sum_outoforder[] = -"[SUM] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"[SUM] " IPERFTimeFrmt " sec %d datagrams received out-of-order%s\n"; const char report_peer [] = "%slocal %s port %u connected with %s port %u%s\n"; @@ -629,13 +742,13 @@ const char report_mss[] = "MSS req size %d bytes (per TCP_MAXSEG)\n"; const char report_datagrams[] = -"[%3d] Sent %d datagrams\n"; +"[%3d] Sent %" PRIdMAX " datagrams\n"; const char report_sumcnt_datagrams[] = -"[SUM-%d] Sent %d datagrams\n"; +"[SUM-%d] Sent %" PRIdMAX " datagrams\n"; const char report_sum_datagrams[] = -"[SUM] Sent %d datagrams\n"; +"[SUM] Sent %" PRIdMAX " datagrams\n"; const char server_reporting[] = "[%3d] Server Report:\n"; @@ -644,7 +757,9 @@ const char reportCSV_peer[] = "%s,%u,%s,%u"; const char report_l2length_error[] = -"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n"; +"%s" IPERFTimeFrmt " sec %" PRIdMAX " datagrams received out-of-order\n"; + +const char report_client_bb_triptime_clocksync_error[] ="%s" IPERFTimeFrmt " sec Clock sync error count = %" PRIdMAX "\n"; /*------------------------------------------------------------------- * CSV outputs @@ -652,17 +767,32 @@ const char report_l2length_error[] = const char reportCSV_bw_format[] = "%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX "\n"; +const char reportCSV_bw_read_enhanced_header[] = +"time,srcaddress,srcport,dstaddr,dstport,transferid,istart,iend,bytes,speed,readcnt,bin0,bin1,bin2,bin3,bin4,bin5,bin6,bin7\n"; + const char reportCSV_bw_read_enhanced_format[] = -"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%d,%d,%d,%d,%d,%d,%d,%d\n"; +"%s,%s,%d,%.1f,%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%d,%d,%d,%d,%d,%d,%d,%d\n"; + +const char reportCSV_bw_write_enhanced_header[] = +"time,srcaddress,srcport,dstaddr,dstport,transferid,istart,iend,bytes,speed,writecnt,writeerr,tcpretry,tcpcwnd,tcppcwnd,tcprtt,tcprttvar\n"; const char reportCSV_bw_write_enhanced_format[] = -"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%d,%u,%u\n"; +"%s,%s,%d,%.1f,%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIuLEAST32 ",%" PRIdMAX ",%" PRIuLEAST32 ",%" PRIuLEAST32 ",%" PRIuLEAST32 "\n"; const char reportCSV_bw_jitter_loss_format[] = "%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX "\n"; +const char reportCSV_bw_jitter_loss_pps_header[] = +"time,srcaddress,srcport,dstaddr,dstport,transferid,istart,iend,bytes,speed,jitter,errors,datagrams,errpercent,outoforder,writecnt,writeerr,pps\n"; + const char reportCSV_bw_jitter_loss_pps_format[] = -"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%f\n"; +"%s,%s,%d,%.1f,%.1f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%f\n"; + +const char reportCSV_client_bb_bw_tcp_header[] = +"time,srcaddress,srcport,dstaddr,dstport,transferid,istart,iend,bytes,speed,bbcnt,bbavg,bbmin,bbmax,bbstdev,tcpretry,tcpcwnd,tcprtt,rps\n"; + +const char reportCSV_client_bb_bw_tcp_format[] = +"%s,%s,%d,%.1f,%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%.3f,%.3f,%.3f,%.3f,%" PRIuLEAST32 ",%" PRIdMAX ",%" PRIuLEAST32 ",%0.1f\n"; /* ------------------------------------------------------------------- * warnings @@ -744,14 +874,16 @@ const char warn_compat_and_peer_exchange[] = "WARNING: Options of '-C' '--compatibility' AND '-X' '--peerdetect' are mutually exclusive, --peerdetect ignored\n"; const char warn_start_before_now[] = -"[%3d] WARNING: --txstart-time (%" PRIdMAX ".%" PRIdMAX ") %s is before now %s\n"; +"[%3d] WARNING: --txstart-time (%ld.%06ld) %s is before now %s\n"; const char error_starttime_exceeds[] = -"ERROR: --txstart-time (%" PRIdMAX ".%" PRIdMAX ") %s exceeds max scheduled delay of %d secs\n"; +"ERROR: --txstart-time %ld.%06ld %s exceeds max scheduled delay of %d secs\n"; const char error_delaytime_exceeds[] = "ERROR: --txdelay-time of %d seconds is more than the supported delay of %d seconds\n"; +const char report_omitted[] = " (omitted)"; + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/src/Makefile.am b/src/Makefile.am index 37db611..e6c8f86 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,7 +49,8 @@ iperf_SOURCES = \ tcp_window_size.c \ pdfs.c \ dscp.c \ - iperf_formattime.c + iperf_formattime.c \ + iperf_multicast_api.c iperf_LDADD = $(LIBCOMPAT_LDADDS) diff --git a/src/Makefile.in b/src/Makefile.in index a8ab595..ea6ccc7 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.2 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -138,7 +138,7 @@ am__iperf_SOURCES_DIST = Client.cpp Extractor.c isochronous.cpp \ Settings.cpp SocketAddr.c gnu_getopt.c gnu_getopt_long.c \ histogram.c main.cpp service.c socket_io.c stdio.c \ packet_ring.c tcp_window_size.c pdfs.c dscp.c \ - iperf_formattime.c checksums.c + iperf_formattime.c iperf_multicast_api.c checksums.c @AF_PACKET_TRUE@am__objects_1 = checksums.$(OBJEXT) am_iperf_OBJECTS = Client.$(OBJEXT) Extractor.$(OBJEXT) \ isochronous.$(OBJEXT) Launch.$(OBJEXT) active_hosts.$(OBJEXT) \ @@ -149,7 +149,8 @@ am_iperf_OBJECTS = Client.$(OBJEXT) Extractor.$(OBJEXT) \ histogram.$(OBJEXT) main.$(OBJEXT) service.$(OBJEXT) \ socket_io.$(OBJEXT) stdio.$(OBJEXT) packet_ring.$(OBJEXT) \ tcp_window_size.$(OBJEXT) pdfs.$(OBJEXT) dscp.$(OBJEXT) \ - iperf_formattime.$(OBJEXT) $(am__objects_1) + iperf_formattime.$(OBJEXT) iperf_multicast_api.$(OBJEXT) \ + $(am__objects_1) iperf_OBJECTS = $(am_iperf_OBJECTS) iperf_DEPENDENCIES = $(am__DEPENDENCIES_1) iperf_LINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(iperf_LDFLAGS) \ @@ -180,7 +181,8 @@ am__depfiles_remade = ./$(DEPDIR)/Client.Po ./$(DEPDIR)/Extractor.Po \ ./$(DEPDIR)/checksums.Po ./$(DEPDIR)/dscp.Po \ ./$(DEPDIR)/gnu_getopt.Po ./$(DEPDIR)/gnu_getopt_long.Po \ ./$(DEPDIR)/histogram.Po ./$(DEPDIR)/igmp_querier.Po \ - ./$(DEPDIR)/iperf_formattime.Po ./$(DEPDIR)/isochronous.Po \ + ./$(DEPDIR)/iperf_formattime.Po \ + ./$(DEPDIR)/iperf_multicast_api.Po ./$(DEPDIR)/isochronous.Po \ ./$(DEPDIR)/main.Po ./$(DEPDIR)/packet_ring.Po \ ./$(DEPDIR)/pdfs.Po ./$(DEPDIR)/service.Po \ ./$(DEPDIR)/socket_io.Po ./$(DEPDIR)/stdio.Po \ @@ -238,8 +240,6 @@ am__define_uniq_tagged_files = \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ @@ -254,6 +254,8 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ CXX = @CXX@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ @@ -264,6 +266,7 @@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ +ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL = @INSTALL@ @@ -373,7 +376,7 @@ iperf_SOURCES = Client.cpp Extractor.c isochronous.cpp Launch.cpp \ SocketAddr.c gnu_getopt.c gnu_getopt_long.c histogram.c \ main.cpp service.c socket_io.c stdio.c packet_ring.c \ tcp_window_size.c pdfs.c dscp.c iperf_formattime.c \ - $(am__append_5) + iperf_multicast_api.c $(am__append_5) iperf_LDADD = $(LIBCOMPAT_LDADDS) @CHECKPROGRAMS_TRUE@checkdelay_SOURCES = checkdelay.c @CHECKPROGRAMS_TRUE@checkdelay_LDADD = $(LIBCOMPAT_LDADDS) @@ -510,6 +513,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histogram.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/igmp_querier.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_formattime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_multicast_api.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isochronous.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_ring.Po@am__quote@ # am--include-marker @@ -604,7 +608,6 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -704,6 +707,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/histogram.Po -rm -f ./$(DEPDIR)/igmp_querier.Po -rm -f ./$(DEPDIR)/iperf_formattime.Po + -rm -f ./$(DEPDIR)/iperf_multicast_api.Po -rm -f ./$(DEPDIR)/isochronous.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/packet_ring.Po @@ -780,6 +784,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/histogram.Po -rm -f ./$(DEPDIR)/igmp_querier.Po -rm -f ./$(DEPDIR)/iperf_formattime.Po + -rm -f ./$(DEPDIR)/iperf_multicast_api.Po -rm -f ./$(DEPDIR)/isochronous.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/packet_ring.Po diff --git a/src/PerfSocket.cpp b/src/PerfSocket.cpp index bad4074..363e8b1 100644 --- a/src/PerfSocket.cpp +++ b/src/PerfSocket.cpp @@ -74,6 +74,7 @@ #include "PerfSocket.hpp" #include "SocketAddr.h" #include "util.h" +#include "iperf_multicast_api.h" /* ------------------------------------------------------------------- * Set socket options before the listen() or connect() calls. @@ -85,9 +86,8 @@ void SetSocketOptions (struct thread_Settings *inSettings) { // must occur before call to accept() for large window sizes setsock_tcp_windowsize(inSettings->mSock, inSettings->mTCPWin, (inSettings->mThreadMode == kMode_Client ? 1 : 0)); - +#if HAVE_DECL_TCP_CONGESTION if (isCongestionControl(inSettings)) { -#ifdef TCP_CONGESTION Socklen_t len = strlen(inSettings->mCongestion) + 1; int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, inSettings->mCongestion, len); @@ -95,12 +95,56 @@ void SetSocketOptions (struct thread_Settings *inSettings) { fprintf(stderr, "Attempt to set '%s' congestion control failed: %s\n", inSettings->mCongestion, strerror(errno)); unsetCongestionControl(inSettings); + thread_stop(inSettings); + } + char cca[TCP_CCA_NAME_MAX] = ""; + len = sizeof(cca); + if (getsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[TCP_CCA_NAME_MAX-1]='\0'; + if (strcmp(cca, inSettings->mCongestion) != 0) { + fprintf(stderr, "Failed to set '%s' congestion control got '%s'\n", inSettings->mCongestion, cca); + thread_stop(inSettings); + } } + } else if (isLoadCCA(inSettings)) { + Socklen_t len = strlen(inSettings->mLoadCCA) + 1; + int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, + inSettings->mLoadCCA, len); + if (rc == SOCKET_ERROR) { + fprintf(stderr, "Attempt to set '%s' load congestion control failed: %s\n", + inSettings->mLoadCCA, strerror(errno)); + unsetLoadCCA(inSettings); + thread_stop(inSettings); + } + char cca[TCP_CCA_NAME_MAX] = ""; + len = sizeof(cca); + if (getsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[TCP_CCA_NAME_MAX-1]='\0'; + if (strcmp(cca, inSettings->mLoadCCA) != 0) { + fprintf(stderr, "Failed to set '%s' load congestion control got '%s'\n", inSettings->mLoadCCA, cca); + thread_stop(inSettings); + } + } + } #else - fprintf(stderr, "The -Z option is not available on this operating system\n"); -#endif + if (isCongestionControl(inSettings) || isLoadCCA(inSettings)) { + fprintf(stderr, "TCP congestion control not supported\n"); + thread_stop(inSettings); } +#endif + int boolean = 1; + int rc; + Socklen_t len = sizeof(boolean); +#if HAVE_DECL_SO_REUSEADDR + // reuse the address, so we can run if a former server was killed off + rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&boolean), len); + WARN_errno(rc == SOCKET_ERROR, "SO_REUSEADDR"); +#endif +#if HAVE_DECL_SO_REUSEPORT + setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEPORT, (char*) &boolean, len); + WARN_errno(rc == SOCKET_ERROR, "SO_REUSEADDR"); +#endif #if ((HAVE_TUNTAP_TAP) && (HAVE_TUNTAP_TUN)) if (isTunDev(inSettings) || isTapDev(inSettings)) { @@ -140,28 +184,6 @@ void SetSocketOptions (struct thread_Settings *inSettings) { #endif } else #endif -#if (HAVE_DECL_SO_BINDTODEVICE) - { - char **device = (inSettings->mThreadMode == kMode_Client) ? &inSettings->mIfrnametx : &inSettings->mIfrname; - if (*device) { - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", *device); - if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { - char *buf; - int len = snprintf(NULL, 0, "%s %s", "bind to device", *device); - len++; // Trailing null byte + extra - buf = static_cast<char *>(malloc(len)); - len = snprintf(buf, len, "%s %s", "bind to device", *device); - WARN_errno(1, buf); - free(buf); - free(*device); - *device = NULL; - FAIL(1, "setsockopt() SO_BINDTODEVICE", inSettings); - } - } - } -#endif // check if we're sending multicast if (isMulticast(inSettings)) { @@ -201,9 +223,7 @@ void SetSocketOptions (struct thread_Settings *inSettings) { WARN_errno(rc == SOCKET_ERROR, "v4 ttl"); } -#if HAVE_DECL_IP_TOS SetSocketOptionsIPTos(inSettings, inSettings->mTOS); -#endif if (!isUDP(inSettings)) { if (isTCPMSS(inSettings)) { @@ -255,12 +275,18 @@ void SetSocketOptions (struct thread_Settings *inSettings) { WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT"); } #endif +#if HAVE_DECL_TCP_TX_DELAY + if (isTcpTxDelay(inSettings) && (inSettings->mTcpTxDelayProb >= 1.0)) { + SetSocketTcpTxDelay(inSettings, inSettings->mTcpTxDelayMean * 1000); + } +#endif } #if HAVE_DECL_SO_MAX_PACING_RATE /* If socket pacing is specified try to enable it. */ if (isFQPacing(inSettings) && inSettings->mFQPacingRate > 0) { int rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &inSettings->mFQPacingRate, sizeof(inSettings->mFQPacingRate)); + inSettings->mFQPacingRateCurrent = inSettings->mFQPacingRate; WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE"); } #endif /* HAVE_SO_MAX_PACING_RATE */ @@ -310,37 +336,149 @@ void SetSocketOptionsReceiveTimeout (struct thread_Settings *mSettings, int time void SetSocketOptionsIPTos (struct thread_Settings *mSettings, int tos) { -#if HAVE_DECL_IP_TOS -#ifdef HAVE_THREAD_DEBUG - thread_debug("Set socket IP_TOS to 0x%x", tos); + bool supported = true; + // set IP TOS (type-of-service) field + if (isOverrideTOS(mSettings) || isSetTOS(mSettings) || (tos > 0)) { + // IPV6_TCLASS is defined on Windows but not implemented. +#if !HAVE_DECL_IPV6_TCLASS || HAVE_WINSOCK2_H + if (isIPV6(mSettings)) { + WARN(1, "WARN: IPV6_TCLASS not supported, setting --tos"); + mSettings->mTOS = 0; + supported = false; + } #endif -#if HAVE_DECL_IPV6_TCLASS && ! defined HAVE_WINSOCK2_H - // IPV6_TCLASS is defined on Windows but not implemented. - if (isIPV6(mSettings)) { - const int dscp = tos; - int rc = setsockopt(mSettings->mSock, IPPROTO_IPV6, IPV6_TCLASS, (char*) &dscp, sizeof(dscp)); - WARN_errno(rc == SOCKET_ERROR, "setsockopt IPV6_TCLASS"); - } else +#if !HAVE_DECL_IP_TOS + if (!isIPV6(mSettings)) { + WARN(1, "WARN: IP_TOS not supported, setting --tos"); + mSettings->mTOS = 0; + supported = false; + } #endif - // set IP TOS (type-of-service) field - if (isOverrideTOS(mSettings) || (tos > 0)) { + if (supported) { int reqtos = tos; Socklen_t len = sizeof(reqtos); - int rc = setsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, - reinterpret_cast<char*>(&reqtos), len); - WARN_errno(rc == SOCKET_ERROR, "setsockopt IP_TOS"); - rc = getsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, - reinterpret_cast<char*>(&reqtos), &len); - WARN_errno(rc == SOCKET_ERROR, "getsockopt IP_TOS"); - WARN((reqtos != tos), "IP_TOS setting failed"); + int rc = setsockopt(mSettings->mSock, (isIPV6(mSettings) ? IPPROTO_IPV6 : IPPROTO_IP), \ + (isIPV6(mSettings) ? IPV6_TCLASS : IP_TOS), reinterpret_cast<char*>(&reqtos), len); + WARN_errno(rc == SOCKET_ERROR, (isIPV6(mSettings) ? "setsockopt IPV6_TCLASS" : "setsockopt IP_TOS")); + rc = getsockopt(mSettings->mSock, (isIPV6(mSettings) ? IPPROTO_IPV6 : IPPROTO_IP), \ + (isIPV6(mSettings) ? IPV6_TCLASS : IP_TOS), reinterpret_cast<char*>(&reqtos), &len); + WARN_errno(rc == SOCKET_ERROR, (isIPV6(mSettings) ? "getsockopt IPV6_TCLASS" : "getsockopt IP_TOS")); + if (reqtos != tos) { + char warnbuf[256]; + snprintf(warnbuf, sizeof(warnbuf), "Warning: IP_TOS set to 0x%x, request for setting to 0x%x", reqtos, tos); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN(1, warnbuf); + mSettings->mTOS = reqtos; + } } + } +} + +/* + * Networking tools can now establish thousands of flows, each of them + * with a different delay, simulating real world conditions. + * + * This requires FQ packet scheduler or a EDT-enabled NIC. + * + * TCP_TX_DELAY socket option, to set a delay in usec units. + * Note that FQ packet scheduler limits might need some tweaking : + * + * man tc-fq + * + * PARAMETERS + * limit + * Hard limit on the real queue size. When this limit is + * reached, new packets are dropped. If the value is lowered, + * packets are dropped so that the new limit is met. Default + * is 10000 packets. + * + * flow_limit + * Hard limit on the maximum number of packets queued per + * flow. Default value is 100. + * + * Use of TCP_TX_DELAY option will increase number of skbs in FQ qdisc, + * so packets would be dropped if any of the previous limit is hit. + * Using big delays might very well trigger + * old bugs in TSO auto defer logic and/or sndbuf limited detection. + ^ + * [root@rjm-nas rjmcmahon]# tc qdisc replace dev enp4s0 root fq + * [root@rjm-nas rjmcmahon]# tc qdisc replace dev enp2s0 root fq + */ +void SetSocketTcpTxDelay(struct thread_Settings *mSettings, int delay) { +#ifdef TCP_TX_DELAY +#if HAVE_DECL_TCP_TX_DELAY + int rc = setsockopt(mSettings->mSock, IPPROTO_TCP, TCP_TX_DELAY, &delay, sizeof(delay)); + if (rc == SOCKET_ERROR) { + fprintf(stderr, "Fail on TCP_TX_DELAY for sock %d\n", mSettings->mSock); + } +#ifdef HAVE_THREAD_DEBUG + else { + Socklen_t len = sizeof(delay); + rc = getsockopt(mSettings->mSock, IPPROTO_TCP, TCP_TX_DELAY, reinterpret_cast<char*>(&delay), &len); + thread_debug("TCP_TX_DELAY set to %d for sock %d", (int) delay, mSettings->mSock); + } +#endif +#endif #endif } +void sol_bindtodevice (struct thread_Settings *inSettings) { + char *device = (inSettings->mThreadMode == kMode_Client) ? inSettings->mIfrnametx : inSettings->mIfrname; + if (device) { +#if (HAVE_DECL_SO_BINDTODEVICE) + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device); + if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { + char *buf; + int len = snprintf(NULL, 0, "%s %s", "bind to device", device); + len++; // Trailing null byte + extra + buf = static_cast<char *>(malloc(len)); + len = snprintf(buf, len, "%s %s", "bind to device", device); + WARN_errno(1, buf); + free(buf); + } +#else + fprintf(stderr, "bind to device not supported\n"); +#endif + + } +} + +void SetSocketBindToDeviceIfNeeded (struct thread_Settings *inSettings) { + if (!isMulticast(inSettings)) { + // typically requires root privileges for unicast bind to device + sol_bindtodevice(inSettings); + } +#ifndef WIN32 + else { // multicast bind below + if (inSettings->mThreadMode != kMode_Client) { + // multicast on the server uses iperf_multicast_join for device binding + // found in listener code, do nothing and return + return; + } + // Handle client side bind to device for multicast + if (!isIPV6(inSettings)) { + // v4 tries with the -B ip first, then legacy socket bind + if (!((inSettings->mLocalhost != NULL) && iperf_multicast_sendif_v4(inSettings))) { + if (inSettings->mIfrnametx != NULL) { + sol_bindtodevice(inSettings); + } + } + } else { + if (!((inSettings->mIfrnametx != NULL) && iperf_multicast_sendif_v6(inSettings))) { + if (inSettings->mIfrnametx != NULL) { + sol_bindtodevice(inSettings); + } + } + } + } +#endif +} /* * Set a socket to blocking or non-blocking -* + * * Returns true on success, or false if there was an error */ bool setsock_blocking (int fd, bool blocking) { diff --git a/src/ReportOutputs.c b/src/ReportOutputs.c index e977433..2e34cb5 100644 --- a/src/ReportOutputs.c +++ b/src/ReportOutputs.c @@ -51,6 +51,7 @@ #include "Locale.h" #include "SocketAddr.h" #include "iperf_formattime.h" +#include "dscp.h" // These static variables are not thread safe but ok to use becase only // the repoter thread usses them @@ -68,6 +69,8 @@ static int HEADING_FLAG(report_bw_jitter_loss) = 0; static int HEADING_FLAG(report_bw_read_enhanced) = 0; static int HEADING_FLAG(report_bw_read_enhanced_netpwr) = 0; static int HEADING_FLAG(report_bw_write_enhanced) = 0; +static int HEADING_FLAG(report_bw_write_enhanced_fq) = 0; +static int HEADING_FLAG(report_bw_write_fq) = 0; static int HEADING_FLAG(report_write_enhanced_write) = 0; static int HEADING_FLAG(report_bw_write_enhanced_netpwr) = 0; static int HEADING_FLAG(report_bw_pps_enhanced) = 0; @@ -81,7 +84,6 @@ static int HEADING_FLAG(report_frame_tcp_enhanced) = 0; static int HEADING_FLAG(report_frame_read_tcp_enhanced_triptime) = 0; static int HEADING_FLAG(report_udp_fullduplex) = 0; static int HEADING_FLAG(report_sumcnt_bw) = 0; -static int HEADING_FLAG(report_sumcnt_udp_fullduplex) = 0; static int HEADING_FLAG(report_sumcnt_bw_read_enhanced) = 0; static int HEADING_FLAG(report_sumcnt_bw_read_triptime) = 0; static int HEADING_FLAG(report_sumcnt_bw_write_enhanced) = 0; @@ -94,16 +96,21 @@ static int HEADING_FLAG(report_burst_write_tcp) = 0; static int HEADING_FLAG(report_bw_isoch_enhanced_netpwr) = 0; static int HEADING_FLAG(report_sumcnt_udp_enhanced) = 0; static int HEADING_FLAG(report_sumcnt_udp_triptime) = 0; +static int HEADING_FLAG(reportCSV_bw_read_enhanced) = 0; +static int HEADING_FLAG(reportCSV_bw_write_enhanced) = 0; +static int HEADING_FLAG(reportCSV_bw_jitter_loss_pps) = 0; +static int HEADING_FLAG(reportCSV_client_bb_bw_tcp) = 0; void reporter_default_heading_flags (int flag) { HEADING_FLAG(report_bw) = flag; HEADING_FLAG(report_client_bb_bw) = flag; HEADING_FLAG(report_sumcnt_bw) = flag; - HEADING_FLAG(report_sumcnt_udp_fullduplex) = flag; HEADING_FLAG(report_bw_jitter_loss) = flag; HEADING_FLAG(report_bw_read_enhanced) = flag; HEADING_FLAG(report_bw_read_enhanced_netpwr) = flag; HEADING_FLAG(report_bw_write_enhanced) = flag; + HEADING_FLAG(report_bw_write_enhanced_fq) = flag; + HEADING_FLAG(report_bw_write_fq) = flag; HEADING_FLAG(report_write_enhanced_write) = flag; HEADING_FLAG(report_write_enhanced_isoch) = flag; HEADING_FLAG(report_bw_write_enhanced_netpwr) = flag; @@ -126,7 +133,30 @@ void reporter_default_heading_flags (int flag) { HEADING_FLAG(report_bw_isoch_enhanced_netpwr) = flag; HEADING_FLAG(report_sumcnt_udp_enhanced) = flag; HEADING_FLAG(report_sumcnt_udp_triptime) = flag; + HEADING_FLAG(reportCSV_bw_read_enhanced) = 0; + HEADING_FLAG(reportCSV_bw_write_enhanced) = 0; + HEADING_FLAG(reportCSV_bw_jitter_loss_pps) = 0; + HEADING_FLAG(reportCSV_client_bb_bw_tcp) = 0; } + +// +// flush when +// +// o) it's a final report +// o) this is the sum report (all preceding interval reports need flush) +// o) below the flush rate limiter +// +#define FLUSH_RATE_LIMITER 1000 //units is microseconds +static inline void cond_flush (struct TransferInfo *stats) { + static struct timeval prev={0,0}; + struct timeval now; + TimeGetNow(now); + if (stats->final || (stats->type == SUM_REPORT) || !(TimeDifferenceUsec(now, prev) < FLUSH_RATE_LIMITER)) { + fflush(stdout); + prev = now; + } +} + static inline void _print_stats_common (struct TransferInfo *stats) { assert(stats!=NULL); outbuffer[0] = '\0'; @@ -145,13 +175,13 @@ static inline void _output_outoforder(struct TransferInfo *stats) { if (stats->cntOutofOrder > 0) { printf(report_outoforder, stats->common->transferIDStr, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } if (stats->l2counts.cnt) { printf(report_l2statistics, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, stats->l2counts.cnt, stats->l2counts.lengtherr, - stats->l2counts.udpcsumerr, stats->l2counts.unknown); + stats->l2counts.udpcsumerr, stats->l2counts.unknown, (stats->common->Omit ? report_omitted : "")); } } @@ -209,29 +239,29 @@ static inline void set_netpowerbuf(double meantransit, struct TransferInfo *stat void tcp_output_fullduplex (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_sum_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); - fflush(stdout); + printf(report_bw_sum_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_fullduplex_sum (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); - fflush(stdout); + printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_fullduplex_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_sum_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); - fflush(stdout); + printf(report_bw_sum_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); - printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); - fflush(stdout); + printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } //TCP read or server output void tcp_output_read_enhanced (struct TransferInfo *stats) { @@ -248,8 +278,9 @@ void tcp_output_read_enhanced (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); - fflush(stdout); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_read_triptime (struct TransferInfo *stats) { double meantransit; @@ -279,7 +310,8 @@ void tcp_output_read_triptime (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); } else { meantransit = (stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0; set_netpowerbuf(meantransit, stats); @@ -302,12 +334,13 @@ void tcp_output_read_triptime (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); } if (stats->framelatency_histogram) { histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd); } - fflush(stdout); + cond_flush(stats); } void tcp_output_read_enhanced_isoch (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_isoch_enhanced_netpwr); @@ -334,7 +367,8 @@ void tcp_output_read_enhanced_isoch (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); } else { meantransit = (stats->isochstats.transit.total.cnt > 0) ? (stats->isochstats.transit.total.sum / stats->isochstats.transit.total.cnt) : 0; set_netpowerbuf(meantransit, stats); @@ -356,12 +390,13 @@ void tcp_output_read_enhanced_isoch (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); } if (stats->framelatency_histogram) { histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd); } - fflush(stdout); + cond_flush(stats); } void tcp_output_frame_read (struct TransferInfo *stats) { @@ -378,8 +413,9 @@ void tcp_output_frame_read (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); - fflush(stdout); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_frame_read_triptime (struct TransferInfo *stats) { fprintf(stderr, "FIXME\n"); @@ -403,7 +439,8 @@ void tcp_output_burst_read (struct TransferInfo *stats) { stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], stats->sock_callstats.read.bins[7], - netpower_buf); + netpower_buf, + (stats->common->Omit ? report_omitted : "")); } else { printf(report_burst_read_tcp_final_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, @@ -420,9 +457,10 @@ void tcp_output_burst_read (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], + (stats->common->Omit ? report_omitted : "")); } - fflush(stdout); + cond_flush(stats); } //TCP write or client output @@ -431,8 +469,8 @@ void tcp_output_write (struct TransferInfo *stats) { _print_stats_common(stats); printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_write_bb (struct TransferInfo *stats) { @@ -448,18 +486,35 @@ void tcp_output_write_bb (struct TransferInfo *stats) { rps_string[sizeof(rps_string) - 1] = '\0'; #if HAVE_TCP_STATS - printf(report_client_bb_bw_format, stats->common->transferIDStr, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->bbrtt.total.cnt, - (stats->bbrtt.total.mean * 1e3), - (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), - (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), - (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), - stats->sock_callstats.write.tcpstats.retry, - stats->sock_callstats.write.tcpstats.cwnd, - stats->sock_callstats.write.tcpstats.rtt, - rps_string); + if (!stats->final) { + printf(report_client_bb_bw_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.total.cnt, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + rps_string, + (stats->common->Omit ? report_omitted : "")); + } else { + printf(report_client_bb_bw_final_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.total.cnt, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + rps_string, + (stats->common->Omit ? report_omitted : "")); + } #else printf(report_client_bb_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, @@ -469,7 +524,8 @@ void tcp_output_write_bb (struct TransferInfo *stats) { (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), - rps_string); + rps_string, + (stats->common->Omit ? report_omitted : "")); #endif if (isTripTime(stats->common)) { printf(report_client_bb_bw_triptime_format, stats->common->transferIDStr, @@ -487,11 +543,23 @@ void tcp_output_write_bb (struct TransferInfo *stats) { (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.min * 1e3), (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.max * 1e3), (stats->bbasym.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbasym.total.m2 / (stats->bbasym.total.cnt - 1))), - rps_string); + (stats->common->Omit ? report_omitted : "")); + } + if (stats->bbowdto_histogram) { + stats->bbowdto_histogram->final = 1; + histogram_print(stats->bbowdto_histogram, stats->ts.iStart, stats->ts.iEnd); + } + if (stats->bbowdfro_histogram) { + stats->bbowdfro_histogram->final = 1; + histogram_print(stats->bbowdfro_histogram, stats->ts.iStart, stats->ts.iEnd); } if (stats->bbrtt_histogram) { + stats->bbrtt_histogram->final = 1; histogram_print(stats->bbrtt_histogram, stats->ts.iStart, stats->ts.iEnd); } + if (isTripTime(stats->common) && (stats->bb_clocksync_error > 0)) { + printf(report_client_bb_triptime_clocksync_error, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, stats->bb_clocksync_error); + } } else { double rps = ((stats->bbrtt.current.cnt > 0) && (stats->iBBrunning > 0)) ? ((double) stats->bbrtt.current.cnt / stats->iBBrunning) : 0; if (rps < 10) @@ -511,8 +579,11 @@ void tcp_output_write_bb (struct TransferInfo *stats) { (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))), stats->sock_callstats.write.tcpstats.retry, stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, stats->sock_callstats.write.tcpstats.rtt, - rps_string); + stats->sock_callstats.write.tcpstats.rttvar, + rps_string, + (stats->common->Omit ? report_omitted : "")); #else printf(report_client_bb_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, @@ -522,10 +593,43 @@ void tcp_output_write_bb (struct TransferInfo *stats) { (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3), (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3), (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))), - rps_string); + rps_string, + (stats->common->Omit ? report_omitted : "")); #endif + if (isTripTime(stats->common)) { + printf(report_client_bb_bw_triptime_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + stats->bbowdto.current.cnt, + (stats->bbowdto.current.mean * 1e3), + (stats->bbowdto.current.cnt < 2) ? 0 : (stats->bbowdto.current.min * 1e3), + (stats->bbowdto.current.cnt < 2) ? 0 : (stats->bbowdto.current.max * 1e3), + (stats->bbowdto.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdto.current.m2 / (stats->bbowdto.current.cnt - 1))), + (stats->bbowdfro.current.mean * 1e3), + (stats->bbowdfro.current.cnt < 2) ? 0 : (stats->bbowdfro.current.min * 1e3), + (stats->bbowdfro.current.cnt < 2) ? 0 : (stats->bbowdfro.current.max * 1e3), + (stats->bbowdfro.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdfro.current.m2 / (stats->bbowdfro.current.cnt - 1))), + (stats->bbasym.current.mean * 1e3), + (stats->bbasym.current.cnt < 2) ? 0 : (stats->bbasym.current.min * 1e3), + (stats->bbasym.current.cnt < 2) ? 0 : (stats->bbasym.current.max * 1e3), + (stats->bbasym.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbasym.current.m2 / (stats->bbasym.current.cnt - 1))), + (stats->common->Omit ? report_omitted : "")); + } + if (isHistogram(stats->common)) { + if (stats->bbowdto_histogram) { + stats->bbowdto_histogram->final = 0; + histogram_print(stats->bbowdto_histogram, stats->ts.iStart, stats->ts.iEnd); + } + if (stats->bbowdfro_histogram) { + stats->bbowdfro_histogram->final = 0; + histogram_print(stats->bbowdfro_histogram, stats->ts.iStart, stats->ts.iEnd); + } + if (stats->bbrtt_histogram) { + stats->bbrtt_histogram->final = 0; + histogram_print(stats->bbrtt_histogram, stats->ts.iStart, stats->ts.iEnd); + } + } } - fflush(stdout); + cond_flush(stats); } void tcp_output_burst_write (struct TransferInfo *stats) { @@ -542,16 +646,18 @@ void tcp_output_burst_write (struct TransferInfo *stats) { stats->sock_callstats.write.tcpstats.retry, stats->sock_callstats.write.tcpstats.cwnd, stats->sock_callstats.write.tcpstats.rtt, - netpower_buf); + netpower_buf, + (stats->common->Omit ? report_omitted : "")); #else printf(report_burst_write_tcp_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->transit.current.mean, stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr); + stats->sock_callstats.write.WriteErr, + (stats->common->Omit ? report_omitted : "")); #endif - fflush(stdout); + cond_flush(stats); } void tcp_output_write_enhanced (struct TransferInfo *stats) { @@ -562,37 +668,121 @@ void tcp_output_write_enhanced (struct TransferInfo *stats) { stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr); + stats->sock_callstats.write.WriteErr, + (stats->common->Omit ? report_omitted : "")); #else set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); - if (stats->sock_callstats.write.tcpstats.cwnd > 0) { - printf(report_bw_write_enhanced_format, +#if HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + printf(report_bw_write_enhanced_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, +#if HAVE_TCP_INFLIGHT + stats->sock_callstats.write.tcpstats.bytes_in_flight, + stats->sock_callstats.write.tcpstats.packets_in_flight, +#endif + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + (stats->common->Omit ? report_omitted : "")); +#else + printf(report_bw_write_enhanced_nocwnd_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + (stats->common->Omit ? report_omitted : "")); +#endif +#endif +#if HAVE_DECL_TCP_NOTSENT_LOWAT + if (stats->latency_histogram) { + histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); + } +#endif + cond_flush(stats); +} + +void tcp_output_write_enhanced_fq (struct TransferInfo *stats) { +#if (HAVE_DECL_SO_MAX_PACING_RATE) + _print_stats_common(stats); + char pacingrate[40]; + if (!stats->final) { + byte_snprintf(pacingrate, sizeof(pacingrate), stats->FQPacingRateCurrent, 'a'); + pacingrate[39] = '\0'; + } else { + pacingrate[0] = '\0'; + } +#if !(HAVE_TCP_STATS) + HEADING_PRINT_COND(report_bw_write_fq); + printf(report_bw_write_fq_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + pacingrate, + (stats->common->Omit ? report_omitted : "")); +#else + HEADING_PRINT_COND(report_bw_write_enhanced_fq); + set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); +#if HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + if (!stats->final) { + printf(report_bw_write_enhanced_fq_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.tcpstats.retry, +#if HAVE_TCP_INFLIGHT + stats->sock_callstats.write.tcpstats.bytes_in_flight, + stats->sock_callstats.write.tcpstats.packets_in_flight, +#endif stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, stats->sock_callstats.write.tcpstats.rtt, stats->sock_callstats.write.tcpstats.rttvar, - netpower_buf); + pacingrate, netpower_buf, + (stats->common->Omit ? report_omitted : "")); } else { - printf(report_bw_write_enhanced_nocwnd_format, + printf(report_bw_write_enhanced_fq_final_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, stats->sock_callstats.write.tcpstats.rtt, - netpower_buf); + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + (stats->common->Omit ? report_omitted : "")); } +#else + printf(report_bw_write_enhanced_nocwnd_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + (stats->common->Omit ? report_omitted : "")); +#endif #endif #if HAVE_DECL_TCP_NOTSENT_LOWAT if (stats->latency_histogram) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); } #endif - fflush(stdout); + cond_flush(stats); +#endif } void tcp_output_write_enhanced_write (struct TransferInfo *stats) { @@ -608,39 +798,48 @@ void tcp_output_write_enhanced_write (struct TransferInfo *stats) { stats->write_mmm.current.min * 1e3, stats->write_mmm.current.max * 1e3, (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), - stats->write_mmm.current.cnt); + stats->write_mmm.current.cnt, + (stats->common->Omit ? report_omitted : "")); #else set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); - if (stats->sock_callstats.write.tcpstats.cwnd > 0) { - printf(report_write_enhanced_write_format, - stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.tcpstats.retry, - stats->sock_callstats.write.tcpstats.cwnd, - stats->sock_callstats.write.tcpstats.rtt, - netpower_buf, - stats->write_mmm.current.mean * 1e-3, - stats->write_mmm.current.min * 1e-3, - stats->write_mmm.current.max * 1e-3, - (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), - stats->write_mmm.current.cnt); - } else { - printf(report_write_enhanced_nocwnd_write_format, - stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.tcpstats.retry, - stats->sock_callstats.write.tcpstats.rtt, - netpower_buf, - stats->write_mmm.current.mean * 1e3, - stats->write_mmm.current.min * 1e3, - stats->write_mmm.current.max * 1e3, - (stats->write_mmm.current.cnt < 2) ? 0 : (1e3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), - stats->write_mmm.current.cnt); - } +#if HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + printf(report_write_enhanced_write_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, +#if HAVE_TCP_INFLIGHT + stats->sock_callstats.write.tcpstats.bytes_in_flight, + stats->sock_callstats.write.tcpstats.packets_in_flight, +#endif + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + stats->write_mmm.current.mean * 1e-3, + stats->write_mmm.current.min * 1e-3, + stats->write_mmm.current.max * 1e-3, + (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), + stats->write_mmm.current.cnt, + (stats->common->Omit ? report_omitted : "")); +#else + printf(report_write_enhanced_nocwnd_write_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + netpower_buf, + stats->write_mmm.current.mean * 1e3, + stats->write_mmm.current.min * 1e3, + stats->write_mmm.current.max * 1e3, + (stats->write_mmm.current.cnt < 2) ? 0 : (1e3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), + stats->write_mmm.current.cnt, (stats->common->Omit ? report_omitted : "")); +#endif #endif if (stats->latency_histogram) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); @@ -648,7 +847,7 @@ void tcp_output_write_enhanced_write (struct TransferInfo *stats) { if (stats->write_histogram) { histogram_print(stats->write_histogram, stats->ts.iStart, stats->ts.iEnd); } - fflush(stdout); + cond_flush(stats); } void tcp_output_write_enhanced_isoch (struct TransferInfo *stats) { @@ -660,38 +859,41 @@ void tcp_output_write_enhanced_isoch (struct TransferInfo *stats) { outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips); + stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, (stats->common->Omit ? report_omitted : "")); #else set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); - if (stats->sock_callstats.write.tcpstats.cwnd > 0) { - printf(report_write_enhanced_isoch_format, - stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.tcpstats.retry, - stats->sock_callstats.write.tcpstats.cwnd, - stats->sock_callstats.write.tcpstats.rtt, - stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, - netpower_buf); - } else { - printf(report_write_enhanced_isoch_nocwnd_format, - stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->sock_callstats.write.WriteCnt, - stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.tcpstats.retry, - stats->sock_callstats.write.tcpstats.rtt, - stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, - netpower_buf); - } +#if HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + printf(report_write_enhanced_isoch_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, + netpower_buf,(stats->common->Omit ? report_omitted : "")); +#else + printf(report_write_enhanced_isoch_nocwnd_format, + stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, + stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, + netpower_buf,(stats->common->Omit ? report_omitted : "")); +#endif #endif #if HAVE_DECL_TCP_NOTSENT_LOWAT if (stats->latency_histogram) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); } #endif - fflush(stdout); + cond_flush(stats); } @@ -700,50 +902,65 @@ void udp_output_fullduplex (struct TransferInfo *stats) { HEADING_PRINT_COND(report_udp_fullduplex); _print_stats_common(stats); printf(report_udp_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ - stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0),(stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_fullduplex_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_udp_fullduplex); _print_stats_common(stats); printf(report_udp_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ - stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0),(stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_fullduplex_sum (struct TransferInfo *stats) { HEADING_PRINT_COND(report_udp_fullduplex); _print_stats_common(stats); printf(report_udp_fullduplex_sum_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ - stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0),(stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss); _print_stats_common(stats); - printf(report_bw_jitter_loss_format, stats->common->transferIDStr, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \ - stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams); + if (!stats->cntIPG) { + printf(report_bw_jitter_loss_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + 0.0, stats->cntError, + stats->cntDatagrams, + 0.0,(stats->common->Omit ? report_omitted : "")); + } else { + printf(report_bw_jitter_loss_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \ + stats->cntError, stats->cntDatagrams, + (100.0 * stats->cntError) / stats->cntDatagrams, (stats->common->Omit ? report_omitted : "")); + } _output_outoforder(stats); - fflush(stdout); + cond_flush(stats); } void udp_output_read_triptime (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_triptime); _print_stats_common(stats); + if (!stats->cntIPG) { printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, 0.0, stats->cntError, stats->cntDatagrams, - 0.0,0.0,0.0,0.0,0.0,0.0); + stats->sock_callstats.read.cntRead, + stats->sock_callstats.read.cntReadTimeo, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadErrLen, +#endif + 0.0,0.0,0.0,0.0,0.0,0.0,(stats->common->Omit ? report_omitted : "")); } else { if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) || (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) { @@ -753,7 +970,14 @@ void udp_output_read_triptime (struct TransferInfo *stats) { (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, - (stats->cntIPG / stats->IPGsum)); + (stats->cntIPG / stats->IPGsum), + stats->sock_callstats.read.cntRead, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadTimeo, + stats->sock_callstats.read.cntReadErrLen,(stats->common->Omit ? report_omitted : "")); +#else + stats->sock_callstats.read.cntReadTimeo, (stats->common->Omit ? report_omitted : "")); +#endif } else { double meantransit; double variance; @@ -785,7 +1009,12 @@ void udp_output_read_triptime (struct TransferInfo *stats) { (stats->cntIPG / stats->IPGsum), stats->cntIPG, llaw_bufstr, - netpower_buf); + stats->sock_callstats.read.cntRead, + stats->sock_callstats.read.cntReadTimeo, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadErrLen, +#endif + netpower_buf, (stats->common->Omit ? report_omitted : "")); } } if (stats->latency_histogram) { @@ -795,7 +1024,7 @@ void udp_output_read_triptime (struct TransferInfo *stats) { histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd); } _output_outoforder(stats); - fflush(stdout); + cond_flush(stats); } void udp_output_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss_enhanced); @@ -806,7 +1035,12 @@ void udp_output_read_enhanced (struct TransferInfo *stats) { outbuffer, outbufferext, 0.0, stats->cntError, stats->cntDatagrams, - 0.0,0.0,0.0,0.0,0.0,0.0); + stats->sock_callstats.read.cntRead, + stats->sock_callstats.read.cntReadTimeo, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadErrLen, +#endif + 0.0,0.0,0.0,0.0,0.0,0.0, (stats->common->Omit ? report_omitted : "")); } else { if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) || (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) { @@ -816,7 +1050,14 @@ void udp_output_read_enhanced (struct TransferInfo *stats) { (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, - (stats->cntIPG / stats->IPGsum)); + (stats->cntIPG / stats->IPGsum), + stats->sock_callstats.read.cntRead, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadTimeo, + stats->sock_callstats.read.cntReadErrLen, (stats->common->Omit ? report_omitted : "")); +#else + stats->sock_callstats.read.cntReadTimeo, (stats->common->Omit ? report_omitted : "")); +#endif } else { double meantransit; double variance; @@ -841,7 +1082,12 @@ void udp_output_read_enhanced (struct TransferInfo *stats) { ((stats->final ? stats->transit.total.max : stats->transit.current.max) * 1e3), (stats->final ? (stats->transit.total.cnt < 2) : (stats->transit.current.cnt < 2)) ? 0 : (1e3 * variance), // convert from sec to ms (stats->cntIPG / stats->IPGsum), - netpower_buf); + stats->sock_callstats.read.cntRead, + stats->sock_callstats.read.cntReadTimeo, +#if HAVE_DECL_MSG_TRUNC + stats->sock_callstats.read.cntReadErrLen, +#endif + netpower_buf, (stats->common->Omit ? report_omitted : "")); } } if (stats->latency_histogram) { @@ -851,7 +1097,7 @@ void udp_output_read_enhanced (struct TransferInfo *stats) { histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd); } _output_outoforder(stats); - fflush(stdout); + cond_flush(stats); } void udp_output_read_triptime_isoch (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_isoch_triptime); @@ -876,7 +1122,7 @@ void udp_output_read_triptime_isoch (struct TransferInfo *stats) { (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, - (stats->cntIPG / stats->IPGsum)); + (stats->cntIPG / stats->IPGsum), (stats->common->Omit ? report_omitted : "")); } else { double frame_meantransit = (stats->isochstats.transit.current.cnt > 0) ? (stats->isochstats.transit.current.sum / stats->isochstats.transit.current.cnt) : 0; double meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0; @@ -897,7 +1143,7 @@ void udp_output_read_triptime_isoch (struct TransferInfo *stats) { stats->isochstats.transit.current.min * 1e3, stats->isochstats.transit.current.max * 1e3, (stats->isochstats.transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->isochstats.transit.current.m2 / (stats->isochstats.transit.current.cnt - 1))), - netpower_buf); + netpower_buf, (stats->common->Omit ? report_omitted : "")); #if 0 if (stats->final) { printf("***** Jitter MMM = %f/%f/%f\n",stats->inline_jitter.total.mean, stats->inline_jitter.total.min, stats->inline_jitter.total.max); @@ -915,15 +1161,15 @@ void udp_output_read_triptime_isoch (struct TransferInfo *stats) { histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd); } _output_outoforder(stats); - fflush(stdout); + cond_flush(stats); } void udp_output_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_write_enhanced (struct TransferInfo *stats) { @@ -934,8 +1180,9 @@ void udp_output_write_enhanced (struct TransferInfo *stats) { outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + stats->sock_callstats.write.WriteTimeo, + (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_write_enhanced_isoch (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced_isoch); @@ -946,8 +1193,8 @@ void udp_output_write_enhanced_isoch (struct TransferInfo *stats) { stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0), - stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips); - fflush(stdout); + stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } // Sum reports @@ -958,121 +1205,123 @@ void udp_output_sum_read (struct TransferInfo *stats) { stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams); + ((100.0 * stats->cntError) / stats->cntDatagrams), + (stats->common->Omit ? report_omitted : "")); if ((stats->cntOutofOrder > 0) && stats->final) { printf(report_sum_outoforder, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } - fflush(stdout); + cond_flush(stats); } void udp_output_sumcnt (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); - printf(report_sumcnt_bw_format, stats->threadcnt, + printf(report_sumcnt_bw_format, stats->slot_thread_downcount, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - if ((stats->cntOutofOrder > 0) && stats->final) { + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + if ((stats->cntOutofOrder > 0) && stats->final) { if (isSumOnly(stats->common)) { printf(report_sumcnt_outoforder, - stats->threadcnt, + stats->threadcnt_final, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } else { printf(report_outoforder, stats->common->transferIDStr, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } } - fflush(stdout); + cond_flush(stats); } void udp_output_sumcnt_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_jitter_loss); _print_stats_common(stats); - printf(report_sumcnt_bw_jitter_loss_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ - stats->cntError, stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); + printf(report_sumcnt_bw_jitter_loss_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ + stats->cntError, stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); if ((stats->cntOutofOrder > 0) && stats->final) { if (isSumOnly(stats->common)) { printf(report_sumcnt_outoforder, - stats->threadcnt, + (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } else { printf(report_sum_outoforder, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder,(stats->common->Omit ? report_omitted : "")); } } - fflush(stdout); + cond_flush(stats); } void udp_output_sumcnt_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced); _print_stats_common(stats); - printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt, + printf(report_sumcnt_bw_read_enhanced_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \ stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams); + (100.0 * stats->cntError) / stats->cntDatagrams, (stats->common->Omit ? report_omitted : "")); if ((stats->cntOutofOrder > 0) && stats->final) { if (isSumOnly(stats->common)) { printf(report_sumcnt_outoforder, - stats->threadcnt, + (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } else { printf(report_sum_outoforder, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } } - fflush(stdout); + cond_flush(stats); } void udp_output_sumcnt_read_triptime (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_udp_triptime); _print_stats_common(stats); - printf(report_sumcnt_udp_triptime_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ - stats->cntError, stats->cntDatagrams, stats->cntIPG, (stats->final ? stats->fInP : stats->iInP), (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - if ((stats->cntOutofOrder > 0) && stats->final) { + printf(report_sumcnt_udp_triptime_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \ + stats->cntError, stats->cntDatagrams, stats->cntIPG, (stats->final ? stats->fInP : stats->iInP), \ + (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); + if ((stats->cntOutofOrder > 0) && stats->final) { if (isSumOnly(stats->common)) { printf(report_sumcnt_outoforder, - stats->threadcnt, + stats->threadcnt_final, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } else { printf(report_sum_outoforder, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } } - fflush(stdout); + cond_flush(stats); } void udp_output_sum_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_sumcnt_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); - printf(report_sumcnt_bw_format, stats->threadcnt, + printf(report_sumcnt_bw_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_sum_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_udp_enhanced); _print_stats_common(stats); - printf(report_sumcnt_udp_enhanced_format, stats->threadcnt, + printf(report_sumcnt_udp_enhanced_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->cntError, stats->cntDatagrams, - (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0)); + (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); if (stats->latency_histogram && stats->final) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); } @@ -1082,16 +1331,16 @@ void udp_output_sum_read_enhanced (struct TransferInfo *stats) { if ((stats->cntOutofOrder > 0) && stats->final) { if (isSumOnly(stats->common)) { printf(report_sumcnt_outoforder, - stats->threadcnt, + stats->threadcnt_final, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } else { printf(report_sum_outoforder, stats->ts.iStart, - stats->ts.iEnd, stats->cntOutofOrder); + stats->ts.iEnd, stats->cntOutofOrder, (stats->common->Omit ? report_omitted : "")); } } - fflush(stdout); + cond_flush(stats); } void udp_output_sum_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced); @@ -1101,19 +1350,19 @@ void udp_output_sum_write_enhanced (struct TransferInfo *stats) { outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void udp_output_sumcnt_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_pps_enhanced); _print_stats_common(stats); - printf(report_sumcnt_bw_pps_enhanced_format, stats->threadcnt, + printf(report_sumcnt_bw_pps_enhanced_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0)); - fflush(stdout); + ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0), (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sum_read (struct TransferInfo *stats) { @@ -1121,8 +1370,8 @@ void tcp_output_sum_read (struct TransferInfo *stats) { _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sum_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_read_enhanced); @@ -1138,24 +1387,24 @@ void tcp_output_sum_read_enhanced (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); + stats->sock_callstats.read.bins[7], (stats->common->Omit ? report_omitted : "")); if (stats->framelatency_histogram && stats->final) { histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd); } - fflush(stdout); + cond_flush(stats); } void tcp_output_sumcnt_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); - printf(report_sumcnt_bw_format, stats->threadcnt, + printf(report_sumcnt_bw_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sumcnt_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced); _print_stats_common(stats); - printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt, + printf(report_sumcnt_bw_read_enhanced_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.read.cntRead, @@ -1166,15 +1415,15 @@ void tcp_output_sumcnt_read_enhanced (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); - fflush(stdout); + stats->sock_callstats.read.bins[7], (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sumcnt_read_triptime (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_read_triptime); _print_stats_common(stats); char llaw_bufstr[LLAWBUFSIZE]; human_format_llawbuf(llaw_bufstr, sizeof(llaw_bufstr), ((stats->final) ? stats->fInP : stats->iInP)); - printf(report_sumcnt_bw_read_triptime_format, stats->threadcnt, + printf(report_sumcnt_bw_read_triptime_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, llaw_bufstr, @@ -1186,8 +1435,8 @@ void tcp_output_sumcnt_read_triptime (struct TransferInfo *stats) { stats->sock_callstats.read.bins[4], stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], - stats->sock_callstats.read.bins[7]); - fflush(stdout); + stats->sock_callstats.read.bins[7], (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sum_write (struct TransferInfo *stats) { @@ -1195,16 +1444,16 @@ void tcp_output_sum_write (struct TransferInfo *stats) { _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sumcnt_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw); _print_stats_common(stats); - printf(report_sumcnt_bw_format, stats->threadcnt, + printf(report_sumcnt_bw_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext); - fflush(stdout); + outbuffer, outbufferext, (stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sum_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_write_enhanced); @@ -1217,13 +1466,13 @@ void tcp_output_sum_write_enhanced (struct TransferInfo *stats) { #if HAVE_TCP_STATS ,stats->sock_callstats.write.tcpstats.retry #endif - ); - fflush(stdout); + ,(stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_sumcnt_bw_write_enhanced); _print_stats_common(stats); - printf(report_sumcnt_bw_write_enhanced_format, stats->threadcnt, + printf(report_sumcnt_bw_write_enhanced_format, (stats->final ? stats->threadcnt_final: stats->slot_thread_downcount), stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, @@ -1231,8 +1480,8 @@ void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats) { #if HAVE_TCP_STATS ,stats->sock_callstats.write.tcpstats.retry #endif - ); - fflush(stdout); + ,(stats->common->Omit ? report_omitted : "")); + cond_flush(stats); } // CSV outputs @@ -1313,12 +1562,28 @@ void format_ips_port_string (struct TransferInfo *stats, bool sum) { #endif } +static inline void _print_stats_csv_timestr(struct TransferInfo *stats, + char *timestr, int buflen) +{ + iperf_formattime(timestr, + buflen, + (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), + isEnhanced(stats->common), + isUTC(stats->common), + (isEnhanced(stats->common) ? CSVTZ : CSV)); +} + +static inline intmax_t _print_stats_csv_speed(struct TransferInfo *stats) +{ + return (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) + ? (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) + : 0); +} + void udp_output_basic_csv (struct TransferInfo *stats) { char timestr[120]; - iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \ - isEnhanced(stats->common), isUTC(stats->common), (isEnhanced(stats->common) ? CSVTZ : CSV)); - intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \ - (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0); + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); printf(reportCSV_bw_jitter_loss_format, timestr, stats->csv_peer, @@ -1331,15 +1596,14 @@ void udp_output_basic_csv (struct TransferInfo *stats) { stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, stats->cntOutofOrder ); + cond_flush(stats); } void udp_output_enhanced_csv (struct TransferInfo *stats) { - char timestr[80]; - iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \ - isEnhanced(stats->common), isUTC(stats->common), - isEnhanced(stats->common) ? CSVTZ : CSV); - intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \ - (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0); + HEADING_PRINT_COND(reportCSV_bw_jitter_loss_pps); + char timestr[120]; + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); printf(reportCSV_bw_jitter_loss_pps_format, timestr, stats->csv_peer, @@ -1356,14 +1620,13 @@ void udp_output_enhanced_csv (struct TransferInfo *stats) { stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0)); + cond_flush(stats); } void tcp_output_basic_csv (struct TransferInfo *stats) { - char timestr[80]; - iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \ - isEnhanced(stats->common), isUTC(stats->common), (isEnhanced(stats->common) ? CSVTZ : CSV)); - intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \ - (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0); + char timestr[120]; + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); printf(reportCSV_bw_format, timestr, stats->csv_peer, @@ -1372,15 +1635,14 @@ void tcp_output_basic_csv (struct TransferInfo *stats) { stats->ts.iEnd, stats->cntBytes, speed); + cond_flush(stats); } void tcp_output_read_enhanced_csv (struct TransferInfo *stats) { + HEADING_PRINT_COND(reportCSV_bw_read_enhanced); char timestr[80]; - iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \ - isEnhanced(stats->common), isUTC(stats->common), - isEnhanced(stats->common) ? CSVTZ : CSV); - intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \ - (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0); + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); printf(reportCSV_bw_read_enhanced_format, timestr, stats->csv_peer, @@ -1398,15 +1660,14 @@ void tcp_output_read_enhanced_csv (struct TransferInfo *stats) { stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], stats->sock_callstats.read.bins[7]); + cond_flush(stats); } void tcp_output_write_enhanced_csv (struct TransferInfo *stats) { - char timestr[80]; - iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \ - isEnhanced(stats->common), isUTC(stats->common), - isEnhanced(stats->common) ? CSVTZ : CSV); - intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \ - (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0); + HEADING_PRINT_COND(reportCSV_bw_write_enhanced); + char timestr[120]; + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); #if !(HAVE_TCP_STATS) printf(reportCSV_bw_write_enhanced_format, timestr, @@ -1420,6 +1681,7 @@ void tcp_output_write_enhanced_csv (struct TransferInfo *stats) { -1, -1, -1, + -1, 0, 0); #else @@ -1437,9 +1699,11 @@ void tcp_output_write_enhanced_csv (struct TransferInfo *stats) { stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.tcpstats.retry, -1, + -1, 0, 0); - } else if (stats->sock_callstats.write.tcpstats.cwnd > 0) { + } else { +#if HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND printf(reportCSV_bw_write_enhanced_format, timestr, stats->csv_peer, @@ -1452,9 +1716,10 @@ void tcp_output_write_enhanced_csv (struct TransferInfo *stats) { stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.tcpstats.retry, stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.cwnd_packets, stats->sock_callstats.write.tcpstats.rtt, stats->sock_callstats.write.tcpstats.rttvar); - } else { +#else printf(reportCSV_bw_write_enhanced_format, timestr, stats->csv_peer, @@ -1467,10 +1732,101 @@ void tcp_output_write_enhanced_csv (struct TransferInfo *stats) { stats->sock_callstats.write.WriteErr, stats->sock_callstats.write.tcpstats.retry, -1, + -1, stats->sock_callstats.write.tcpstats.rtt, 0); +#endif } #endif + cond_flush(stats); +} + +void tcp_output_write_bb_csv (struct TransferInfo *stats) { + HEADING_PRINT_COND(reportCSV_client_bb_bw_tcp); + char timestr[120]; + _print_stats_csv_timestr(stats, timestr, sizeof(timestr)); + intmax_t speed = _print_stats_csv_speed(stats); + if (stats->final) { + double rps = ((stats->fBBrunning > 0) && (stats->bbrtt.total.cnt > 0)) ? ((double) stats->bbrtt.total.cnt / stats->fBBrunning) : 0; + +#if HAVE_TCP_STATS + printf(reportCSV_client_bb_bw_tcp_format, + timestr, + stats->csv_peer, + stats->common->transferID, + stats->ts.iStart, + stats->ts.iEnd, + stats->cntBytes, + speed, + stats->bbrtt.total.cnt, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, + rps); +#else + printf(reportCSV_client_bb_bw_tcp_format, + timestr, + stats->csv_peer, + stats->common->transferID, + stats->ts.iStart, + stats->ts.iEnd, + stats->cntBytes, + speed, + stats->bbrtt.total.cnt, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), + -1, + -1, + -1, + rps); +#endif + } else { + double rps = ((stats->bbrtt.current.cnt > 0) && (stats->iBBrunning > 0)) ? ((double) stats->bbrtt.current.cnt / stats->iBBrunning) : 0; +#if HAVE_TCP_STATS + printf(reportCSV_client_bb_bw_tcp_format, + timestr, + stats->csv_peer, + stats->common->transferID, + stats->ts.iStart, + stats->ts.iEnd, + stats->cntBytes, + speed, + stats->bbrtt.current.cnt, + (stats->bbrtt.current.mean * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, + rps); +#else + printf(reportCSV_client_bb_bw_tcp_format, + timestr, + stats->csv_peer, + stats->common->transferID, + stats->ts.iStart, + stats->ts.iEnd, + stats->cntBytes, + speed, + stats->bbrtt.current.cnt, + (stats->bbrtt.current.mean * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))), + -1, + -1, + -1, + rps); +#endif + } + cond_flush(stats); } /* @@ -1498,7 +1854,11 @@ static void reporter_output_listener_settings (struct ReportSettings *report) { report->common->Port, report->common->PortLast, report->pid); } else { printf(isEnhanced(report->common) ? server_pid_port : server_port, - (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid); + (isUDP(report->common) ? ((isIPV6(report->common) && isEnhanced(report->common)) ? "UDP (v6)" : "UDP") : \ + ((isIPV6(report->common) && isEnhanced(report->common)) ? "TCP (v6)" : "TCP")), report->common->Port, report->pid); + } + if (isUDP(report->common) && isWorkingLoadUp(report->common) && isWorkingLoadDown(report->common)) { + printf(server_working_load_port, "TCP", report->common->Port); } if (report->common->Localhost != NULL) { if (isEnhanced(report->common) && !SockAddr_isMulticast(&report->local)) { @@ -1543,17 +1903,36 @@ static void reporter_output_listener_settings (struct ReportSettings *report) { printf(bind_address_iface_taptun, report->common->Ifrname); } if (isEnhanced(report->common)) { - byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, toupper((int)report->common->Format)); - byte_snprintf(outbufferext, sizeof(outbufferext), report->common->BufLen / 8, 'A'); - outbuffer[(sizeof(outbuffer)-1)] = '\0'; - outbufferext[(sizeof(outbufferext)-1)] = '\0'; - printf("%s: %s (Dist bin width=%s)\n", server_read_size, outbuffer, outbufferext); + if (!(isUDP(report->common))) { + byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, toupper((int)report->common->Format)); + byte_snprintf(outbufferext, sizeof(outbufferext), report->common->BufLen / 8, 'A'); + outbuffer[(sizeof(outbuffer)-1)] = '\0'; + outbufferext[(sizeof(outbufferext)-1)] = '\0'; + printf("%s: %s (Dist bin width=%s)\n", server_read_size, outbuffer, outbufferext); + } else { + byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, 'B'); + outbuffer[(sizeof(outbuffer)-1)] = '\0'; + printf("%s: %s \n", server_read_size, outbuffer); + } + } - if (isCongestionControl(report->common) && report->common->Congestion) { - fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion); +#if HAVE_DECL_TCP_CONGESTION + if (isCongestionControl(report->common) || isEnhanced(report->common)) { + char cca[40] = ""; + Socklen_t len = sizeof(cca); + if (getsockopt(report->common->socket, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[len]='\0'; + } + if (report->common->Congestion) { + fprintf(stdout,"TCP congestion control default set to %s using %s\n", report->common->Congestion, cca); + } else if (strlen(cca)) { + fprintf(stdout,"TCP congestion control default %s\n", cca); + } } +#endif if (isOverrideTOS(report->common)) { - fprintf(stdout, "Reflected TOS will be set to 0x%x\n", report->common->RTOS); + fprintf(stdout, "Reflected TOS will be set to 0x%x (dscp=%d,ecn=%d)\n", report->common->RTOS, \ + DSCP_VALUE(report->common->RTOS), ECN_VALUE(report->common->RTOS)); } if (isPrintMSS(report->common)) { if (isTCPMSS(report->common)) { @@ -1563,7 +1942,8 @@ static void reporter_output_listener_settings (struct ReportSettings *report) { } } if (report->common->TOS) { - fprintf(stdout, "TOS will be set to 0x%x\n", report->common->TOS); + fprintf(stdout, "TOS will be set to 0x%x (dscp=%d,ecn=%d)\n", report->common->TOS, \ + DSCP_VALUE(report->common->RTOS), ECN_VALUE(report->common->RTOS)); } if (isUDP(report->common)) { if (isSingleClient(report->common)) { @@ -1574,7 +1954,7 @@ static void reporter_output_listener_settings (struct ReportSettings *report) { } else if (isSingleClient(report->common)) { fprintf(stdout, "Server set to single client traffic mode (serialize traffic tests)\n"); } - if (isMulticast(report->common)) { + if (isMulticast(report->common) && (report->common->Port == report->common->PortLast)) { fprintf(stdout, "Server set to single client traffic mode (per multicast receive)\n"); } if (isHistogram(report->common)) { @@ -1608,19 +1988,31 @@ static void reporter_output_client_settings (struct ReportSettings *report) { if (!report->common->Ifrnametx) { printf(isEnhanced(report->common) ? client_pid_port : client_port, hoststr, (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid, \ - (!report->common->threads ? 1 : report->common->threads)); + (!report->common->threads ? 1 : report->common->threads), + (!report->common->threads ? 1 : report->common->working_load_threads)); } else { printf(client_pid_port_dev, hoststr, (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid, \ - report->common->Ifrnametx, (!report->common->threads ? 1 : report->common->threads)); + report->common->Ifrnametx, (!report->common->threads ? 1 : report->common->threads), + (!report->common->threads ? 1 : report->common->working_load_threads)); } - if ((isEnhanced(report->common) || isNearCongest(report->common)) && !isUDP(report->common)) { + if ((isEnhanced(report->common) || isNearCongest(report->common)) && !isUDP(report->common) && !isBounceBack(report->common)) { byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, 'B'); outbuffer[(sizeof(outbuffer)-1)] = '\0'; - if (isTcpWriteTimes(report->common)) { - printf("%s: %s (writetimer-enabled)\n", client_write_size, outbuffer); + if (!isBurstSize(report->common)) { + if (isTcpWriteTimes(report->common)) { + printf("%s: %s (write timer enabled)\n", client_write_size, outbuffer); + } else { + printf("%s: %s\n", client_write_size, outbuffer); + } } else { - printf("%s: %s\n", client_write_size, outbuffer); + byte_snprintf(outbufferext, sizeof(outbufferext), report->common->BurstSize, 'B'); + outbufferext[(sizeof(outbufferext)-1)] = '\0'; + if (isTcpWriteTimes(report->common)) { + printf("%s: %s Burst size: %s (write timer enabled)\n", client_write_size, outbuffer, outbufferext); + } else { + printf("%s: %s Burst size: %s\n", client_write_size, outbuffer, outbufferext); + } } } if (isIsochronous(report->common)) { @@ -1631,30 +2023,47 @@ static void reporter_output_client_settings (struct ReportSettings *report) { meanbuf[39]='\0'; variancebuf[39]='\0'; printf(client_isochronous, report->isochstats.mFPS, meanbuf, variancebuf, (report->isochstats.mBurstInterval/1000.0), (report->isochstats.mBurstIPG/1000.0)); } - if (isPeriodicBurst(report->common)) { - char tmpbuf[40]; - byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->BurstSize, 'A'); - tmpbuf[39]='\0'; - if (report->common->bbcount) { - printf(client_burstperiodcount, tmpbuf, report->common->bbcount, (1.0 / report->common->FPS)); - } else { - printf(client_burstperiod, tmpbuf, (1.0 / report->common->FPS)); - } - } if (isBounceBack(report->common)) { - char tmpbuf[40]; - byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->bbsize, 'A'); - tmpbuf[39]='\0'; + char tmplbuf[40]; + byte_snprintf(tmplbuf, sizeof(tmplbuf), report->common->bbsize, 'A'); + tmplbuf[39]='\0'; + char tmprbuf[40]; + byte_snprintf(tmprbuf, sizeof(tmprbuf), report->common->bbreplysize, 'A'); + tmprbuf[39]='\0'; if (isTcpQuickAck(report->common)) { - printf(client_bounceback, tmpbuf, report->common->bbhold); + printf(client_bounceback, tmplbuf, tmprbuf, report->common->bbhold); } else { - printf(client_bounceback_noqack, tmpbuf, report->common->bbhold); + printf(client_bounceback_noqack, tmplbuf, tmprbuf, report->common->bbhold); + } + if (report->common->FPS > 0) { + printf(client_bbburstperiodcount, report->common->bbcount, (1.0 / report->common->FPS)); + } + } else { + if (isPeriodicBurst(report->common) && (report->common->FPS > 0)) { + char tmpbuf[40]; + byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->BurstSize, 'A'); + tmpbuf[39]='\0'; + if (report->common->bbcount) { + printf(client_burstperiodcount, tmpbuf, report->common->bbcount, (1.0 / report->common->FPS)); + } else { + printf(client_burstperiod, tmpbuf, (1.0 / report->common->FPS)); + } } } if (isFQPacing(report->common)) { - byte_snprintf(outbuffer, sizeof(outbuffer), report->common->FQPacingRate, 'a'); - outbuffer[(sizeof(outbuffer)-1)] = '\0'; - printf(client_fq_pacing,outbuffer); + char prate[40]; + byte_snprintf(prate, sizeof(prate), report->common->FQPacingRate, 'a'); + prate[39] = '\0'; + if (isFQPacingStep(report->common)) { + char pratestep[40]; + byte_snprintf(pratestep, sizeof(pratestep), report->common->FQPacingRateStep, 'a'); + pratestep[39] = '\0'; + printf(client_fq_pacing_step,prate, pratestep); + } else { + byte_snprintf(outbuffer, sizeof(outbuffer), report->common->FQPacingRate, 'a'); + outbuffer[(sizeof(outbuffer)-1)] = '\0'; + printf(client_fq_pacing,outbuffer); + } } if (isPrintMSS(report->common)) { if (isTCPMSS(report->common)) { @@ -1663,16 +2072,41 @@ static void reporter_output_client_settings (struct ReportSettings *report) { printf(report_default_mss, report->sockmaxseg); } } - - if (isCongestionControl(report->common) && report->common->Congestion) { - fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion); +#if HAVE_DECL_TCP_CONGESTION + if (isCongestionControl(report->common) || isEnhanced(report->common)) { + char cca[40] = ""; + Socklen_t len = sizeof(cca); + if (getsockopt(report->common->socket, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[len]='\0'; + } + if (report->common->Congestion) { + fprintf(stdout,"TCP congestion control set to %s using %s\n", report->common->Congestion, cca); + } else if (strlen(cca)) { + fprintf(stdout,"TCP congestion control using %s\n", cca); + } + } + if ((isWorkingLoadUp(report->common) || isWorkingLoadDown(report->common)) && isLoadCCA(report->common)) { + fprintf(stdout,"TCP working load congestion control set to %s\n", report->common->LoadCCA); } +#endif if (isEnhanced(report->common)) { + char strtext[10]; + if (isSetTOS(report->common)) { + strcpy(strtext, "set"); + } else { + strcpy(strtext, "defaults"); + } + fprintf(stdout, "TOS %s to 0x%x (dscp=%d,ecn=%d)", strtext, report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS)); + if (ECN_VALUE(report->common->TOS)) { + fprintf(stdout, " (warn ecn bits set)"); + } if (isNoDelay(report->common)) { - fprintf(stdout, "TOS set to 0x%x and nodelay (Nagle off)\n", report->common->TOS); + fprintf(stdout," and nodelay (Nagle off)"); } else { - fprintf(stdout, "TOS set to 0x%x (Nagle on)\n", report->common->TOS); + fprintf(stdout," (Nagle on)"); } + fprintf(stdout, "\n"); } if (isNearCongest(report->common)) { if (report->common->rtt_weight == NEARCONGEST_DEFAULT) { @@ -1716,15 +2150,23 @@ static void reporter_output_client_settings (struct ReportSettings *report) { fflush(stdout); } +#define MINSAMPLES_FORVARIANCE 25 void reporter_connect_printf_tcp_final (struct ConnectionInfo * report) { - if (report->connect_times.cnt > 1) { - double variance = (report->connect_times.cnt < 2) ? 0 : 1e3* (sqrt(report->connect_times.m2 / (report->connect_times.cnt - 1))); + if (report->connect_times.cnt >= MINSAMPLES_FORVARIANCE) { + double variance = (sqrt(report->connect_times.m2 / (report->connect_times.cnt - 1))); fprintf(stdout, "[ CT] final connect times (min/avg/max/stdev) = %0.3f/%0.3f/%0.3f/%0.3f ms (tot/err) = %" PRIdMAX "/%" PRIdMAX "\n", \ report->connect_times.min, \ (report->connect_times.sum / report->connect_times.cnt), \ report->connect_times.max, variance, \ (report->connect_times.cnt + report->connect_times.err), \ report->connect_times.err); + } else if (report->connect_times.cnt > 2) { + fprintf(stdout, "[ CT] final connect times (min/avg/max) = %0.3f/%0.3f/%0.3f ms (tot/err) = %" PRIdMAX "/%" PRIdMAX "\n", \ + report->connect_times.min, \ + (report->connect_times.sum / report->connect_times.cnt), \ + report->connect_times.max, \ + (report->connect_times.cnt + report->connect_times.err), \ + report->connect_times.err); } fflush(stdout); } @@ -1739,7 +2181,6 @@ void reporter_print_connection_report (struct ConnectionInfo *report) { outbuffer[0]='\0'; outbufferext[0]='\0'; char *b = &outbuffer[0]; - #if HAVE_DECL_TCP_WINDOW_CLAMP if (!isUDP(report->common) && isRxClamp(report->common)) { snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "clamp=", report->common->ClampSize); @@ -1781,9 +2222,11 @@ void reporter_print_connection_report (struct ConnectionInfo *report) { } if (isBounceBack(report->common)) { if (isTcpQuickAck(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (bb w/quickack len/hold=%d/%d)", report->common->bbsize, report->common->bbhold); + snprintf(b, SNBUFFERSIZE-strlen(b), " (bb w/quickack req/reply/hold=%d/%d/%d)", report->common->bbsize, \ + report->common->bbreplysize, report->common->bbhold); } else { - snprintf(b, SNBUFFERSIZE-strlen(b), " (bb len/hold=%d/%d)", report->common->bbsize, report->common->bbhold); + snprintf(b, SNBUFFERSIZE-strlen(b), " (bb req/reply/hold=%d/%d/%d)", report->common->bbsize, \ + report->common->bbreplysize, report->common->bbhold); } b += strlen(b); } @@ -1800,23 +2243,47 @@ void reporter_print_connection_report (struct ConnectionInfo *report) { b += strlen(b); } if (isEnhanced(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (sock=%d)", report->common->socket);; - b += strlen(b); + if (isCongestionControl(report->common)) { +#if HAVE_DECL_TCP_CONGESTION + char cca[40] = ""; + Socklen_t len = sizeof(cca); + int rc; + if ((rc = getsockopt(report->common->socket, IPPROTO_TCP, TCP_CONGESTION, &cca, &len)) == 0) { + cca[len]='\0'; + } + if (rc != SOCKET_ERROR) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (sock=%d/%s)", report->common->socket, cca); + b += strlen(b); + } +#endif + } else { + snprintf(b, SNBUFFERSIZE-strlen(b), " (sock=%d)", report->common->socket); + b += strlen(b); + } } if (isOverrideTOS(report->common)) { if (isFullDuplex(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x/0x%x)", report->common->TOS, report->common->RTOS); + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x,dscp=%d,ecn=%d, /0x%x,dscp=%d,ecn=%d)", report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS), \ + report->common->RTOS, \ + DSCP_VALUE(report->common->RTOS), ECN_VALUE(report->common->RTOS)); } else if (isReverse(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x)", report->common->TOS); + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx=0x%x,dscp=%d,ecn=%d)", report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS)); } b += strlen(b); } else if (report->common->TOS) { if (isFullDuplex(report->common) || isBounceBack(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x/0x%x)", report->common->TOS, report->common->TOS); + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x,dscp=%d,ecn=%d/0x%x,dscp=%d,ecn=%d)", report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS), \ + report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS)); } else if (isReverse(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x)", report->common->TOS); + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx=0x%x,dscp=%d,ecn=%d)", report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS)); } else { - snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx=0x%x)", report->common->TOS); + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x,dscp=%d,ecn=%d)", report->common->TOS, \ + DSCP_VALUE(report->common->TOS), ECN_VALUE(report->common->TOS)); } b += strlen(b); } @@ -1833,13 +2300,12 @@ void reporter_print_connection_report (struct ConnectionInfo *report) { } #endif #if HAVE_TCP_STATS - if (!isUDP(report->common) && (report->tcpinitstats.isValid)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (icwnd/mss/irtt=%u/%u/%u)", \ + if (!isUDP(report->common) && (report->tcpinitstats.isValid) && isEnhanced(report->common)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (icwnd/mss/irtt=%" PRIdMAX "/%" PRIuLEAST32 "/%" PRIuLEAST32 ")", \ report->tcpinitstats.cwnd, report->tcpinitstats.mss_negotiated, report->tcpinitstats.rtt); b += strlen(b); } #endif - if ((isFullDuplex(report->common) || !isServerReverse(report->common)) \ && (isEnhanced(report->common) || isConnectOnly(report->common))) { if (report->connect_timestamp.tv_sec > 0) { @@ -1923,14 +2389,7 @@ void reporter_print_connection_report (struct ConnectionInfo *report) { if ((report->common->ThreadMode == kMode_Client) && !isServerReverse(report->common)) { if (isTxHoldback(report->common) || isTxStartTime(report->common)) { struct timeval now; -#ifdef HAVE_CLOCK_GETTIME - struct timespec t1; - clock_gettime(CLOCK_REALTIME, &t1); - now.tv_sec = t1.tv_sec; - now.tv_usec = t1.tv_nsec / 1000; -#else - gettimeofday(&now, NULL); -#endif + TimeGetNow(now); int seconds_from_now; if (isTxHoldback(report->common)) { seconds_from_now = report->txholdbacktime.tv_sec; diff --git a/src/Reporter.c b/src/Reporter.c index bbd6fd5..e200a80 100644 --- a/src/Reporter.c +++ b/src/Reporter.c @@ -82,12 +82,12 @@ static void reporter_reset_transfer_stats_client_tcp(struct TransferInfo *stats) static void reporter_reset_transfer_stats_client_udp(struct TransferInfo *stats); static void reporter_reset_transfer_stats_server_udp(struct TransferInfo *stats); static void reporter_reset_transfer_stats_server_tcp(struct TransferInfo *stats); +static void reporter_reset_transfer_stats_sum(struct TransferInfo *stats); // code for welfornd's algorithm to produce running mean/min/max/var static void reporter_update_mmm (struct MeanMinMaxStats *stats, double value); static void reporter_reset_mmm (struct MeanMinMaxStats *stats); - // one way delay (OWD) calculations static void reporter_handle_packet_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet); static void reporter_handle_isoch_oneway_transit_tcp(struct TransferInfo *stats, struct ReportStruct *packet); @@ -160,8 +160,10 @@ bool ReportPacket (struct ReporterData* data, struct ReportStruct *packet) { if (!TimeZero(stats->ts.nextTCPSampleTime) && (TimeDifference(stats->ts.nextTCPSampleTime, packet->packetTime) < 0)) { gettcpinfo(data->info.common->socket, &packet->tcpstats); TimeAdd(stats->ts.nextTCPSampleTime, stats->ts.intervalTime); + rc = true; } else { gettcpinfo(data->info.common->socket, &packet->tcpstats); + rc = true; } } #endif @@ -194,14 +196,14 @@ bool ReportPacket (struct ReporterData* data, struct ReportStruct *packet) { * thread to print a final report and to remove the data report from its jobq. * It also handles the freeing reports and other closing actions */ -int EndJob (struct ReportHeader *reporthdr, struct ReportStruct *finalpacket) { +bool EndJob (struct ReportHeader *reporthdr, struct ReportStruct *finalpacket) { assert(reporthdr!=NULL); assert(finalpacket!=NULL); struct ReporterData *report = (struct ReporterData *) reporthdr->this_report; struct ReportStruct packet; memset(&packet, 0, sizeof(struct ReportStruct)); - int do_close = 1; + bool do_close = true; /* * Using PacketID of -1 ends reporting * It pushes a "special packet" through @@ -217,24 +219,25 @@ int EndJob (struct ReportHeader *reporthdr, struct ReportStruct *finalpacket) { } #endif // clear the reporter done predicate - report->packetring->consumerdone = 0; + report->packetring->consumerdone = false; // the negative packetID is used to inform the report thread this traffic thread is done packet.packetID = -1; packet.packetLen = finalpacket->packetLen; packet.packetTime = finalpacket->packetTime; + packet.err_readwrite = NullEvent; // this is not a real event if (isSingleUDP(report->info.common)) { packetring_enqueue(report->packetring, &packet); reporter_process_transfer_report(report); } else { ReportPacket(report, &packet); -#ifdef HAVE_THREAD_DEBUG - thread_debug( "Traffic thread awaiting reporter to be done with %p and cond %p", (void *)report, (void *) report->packetring->awake_producer); -#endif Condition_Lock((*(report->packetring->awake_producer))); while (!report->packetring->consumerdone) { // This wait time is the lag between the reporter thread // and the traffic thread, a reporter thread with lots of // reports (e.g. fastsampling) can lag per the i/o +#ifdef HAVE_THREAD_DEBUG + thread_debug( "Traffic thread awaiting reporter to be done with %p and cond %p", (void *)report, (void *) report->packetring->awake_producer); +#endif Condition_TimedWait(report->packetring->awake_producer, 1); // printf("Consumer done may be stuck\n"); } @@ -249,7 +252,7 @@ int EndJob (struct ReportHeader *reporthdr, struct ReportStruct *finalpacket) { #endif FreeSumReport(report->FullDuplexReport); } else { - do_close = 0; + do_close = false; } } return do_close; @@ -422,7 +425,7 @@ void reporter_spawn (struct thread_Settings *thread) { #ifdef HAVE_THREAD_DEBUG thread_debug( "Reporter thread started"); #endif - if (isEnhanced(thread)) { + if (isEnhanced(thread) && (thread->mThreadMode == kMode_Client)) { myConnectionReport = InitConnectOnlyReport(thread); } /* @@ -480,8 +483,9 @@ void reporter_spawn (struct thread_Settings *thread) { // Report process report returns true // when a report needs to be removed // from the jobq. Also, work item might - // be removed as part of processing - // Store a cached pointer for the linked list maitenance + // be free as part of its processing + // Store a cached pointer tmp + // for the next work item struct ReportHeader *tmp = (*work_item)->next; if (reporter_process_report(*work_item)) { #ifdef HAVE_THREAD_DEBUG @@ -510,11 +514,11 @@ void reporter_spawn (struct thread_Settings *thread) { } // The Transfer or Data report is by far the most complicated report -int reporter_process_transfer_report (struct ReporterData *this_ireport) { +bool reporter_process_transfer_report (struct ReporterData *this_ireport) { assert(this_ireport != NULL); struct TransferInfo *sumstats = (this_ireport->GroupSumReport ? &this_ireport->GroupSumReport->info : NULL); struct TransferInfo *fullduplexstats = (this_ireport->FullDuplexReport ? &this_ireport->FullDuplexReport->info : NULL); - int need_free = 0; + bool need_free = false; // The consumption detector applies delay to the reporter // thread when its consumption rate is too low. This allows // the traffic threads to send aggregates vs thrash @@ -530,7 +534,7 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { apply_consumption_detector(); // If there are more packets to process then handle them struct ReportStruct *packet = NULL; - int advance_jobq = 0; + bool advance_jobq = false; while (!advance_jobq && (packet = packetring_dequeue(this_ireport->packetring))) { // Increment the total packet count processed by this thread // this will be used to make decisions on if the reporter @@ -543,9 +547,21 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { reporter_handle_packet_tcpistats(this_ireport, packet); } #endif + if (this_ireport->transfer_interval_handler) { + if (sumstats && (this_ireport->packetring->uplevel != sumstats->uplevel) \ + && (TimeDifference(sumstats->ts.nextTime, packet->packetTime) > 0)) { + sumstats->slot_thread_upcount++; +#if HAVE_SUMMING_DEBUG + printf("**** %s upcnt (%p) pkt=%ld.%ld (up/down)=%d/%d uplevel (sum/pkt)=%d/%d\n", this_ireport->info.common->transferIDStr, (void *)this_ireport->packetring, \ + (long) packet->packetTime.tv_sec, (long) packet->packetTime.tv_usec, sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, this_ireport->packetring->uplevel); +#endif + this_ireport->packetring->uplevel = toggleLevel(this_ireport->packetring->uplevel); + } + } if (!(packet->packetID < 0)) { // Check to output any interval reports, - // bursts need to report the packet first + // bursts need to report the packet first if (this_ireport->packet_handler_pre_report) { (*this_ireport->packet_handler_pre_report)(this_ireport, packet); } @@ -564,8 +580,8 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { if (fullduplexstats) fullduplexstats->ts.packetTime = packet->packetTime; } else { - need_free = 1; - advance_jobq = 1; + need_free = true; + advance_jobq = true; // A last packet event was detected // printf("last packet event detected\n"); fflush(stdout); this_ireport->reporter_thread_suspends = consumption_detector.reporter_thread_suspends; @@ -577,7 +593,7 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { } this_ireport->info.ts.packetTime = packet->packetTime; assert(this_ireport->transfer_protocol_handler != NULL); - (*this_ireport->transfer_protocol_handler)(this_ireport, 1); + (*this_ireport->transfer_protocol_handler)(this_ireport, true); // This is a final report so set the sum report header's packet time // Note, the thread with the max value will set this if (fullduplexstats && isEnhanced(this_ireport->info.common)) { @@ -587,20 +603,22 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { } if (DecrSumReportRefCounter(this_ireport->FullDuplexReport) == 0) { if (this_ireport->FullDuplexReport->transfer_protocol_sum_handler) { - (*this_ireport->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, 1); + (*this_ireport->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, true); } // FullDuplex report gets freed by a traffic thread (per its barrier) } } if (sumstats) { - if (!this_ireport->GroupSumReport->threads_cntr_fsum) - this_ireport->GroupSumReport->threads_cntr_fsum = this_ireport->GroupSumReport->reference.maxcount; if (TimeDifference(sumstats->ts.packetTime, packet->packetTime) > 0) { sumstats->ts.packetTime = packet->packetTime; } - if (this_ireport->GroupSumReport->transfer_protocol_sum_handler && \ - (--this_ireport->GroupSumReport->threads_cntr_fsum == 0) && (this_ireport->GroupSumReport->reference.maxcount > 1)) { - (*this_ireport->GroupSumReport->transfer_protocol_sum_handler)(&this_ireport->GroupSumReport->info, 1); + if (this_ireport->GroupSumReport->transfer_protocol_sum_handler) { + Mutex_Lock(&this_ireport->GroupSumReport->reference.lock); + if ((++this_ireport->GroupSumReport->final_thread_upcount == this_ireport->GroupSumReport->reference.maxcount) && \ + ((this_ireport->GroupSumReport->reference.maxcount > 1) || isSumOnly(this_ireport->info.common))) { + (*this_ireport->GroupSumReport->transfer_protocol_sum_handler)(&this_ireport->GroupSumReport->info, true); + } + Mutex_Unlock(&this_ireport->GroupSumReport->reference.lock); } } } @@ -614,17 +632,20 @@ int reporter_process_transfer_report (struct ReporterData *this_ireport) { * can't use them anymore (except for the DATA REPORT); * */ -inline int reporter_process_report (struct ReportHeader *reporthdr) { +inline bool reporter_process_report (struct ReportHeader *reporthdr) { assert(reporthdr != NULL); - int done = 1; + bool done = true; switch (reporthdr->type) { case DATA_REPORT: done = reporter_process_transfer_report((struct ReporterData *)reporthdr->this_report); - fflush(stdout); if (done) { struct ReporterData *tmp = (struct ReporterData *)reporthdr->this_report; struct PacketRing *pr = tmp->packetring; - pr->consumerdone = 1; + pr->consumerdone = true; +# ifdef HAVE_THREAD_DEBUG + struct ReporterData *report = (struct ReporterData *) reporthdr->this_report; + thread_debug( "Reporter thread signal traffic thread %p %p", (void *)report, (void *) report->packetring->awake_producer); +#endif // Data Reports are special because the traffic thread needs to free them, just signal Condition_Signal(pr->awake_producer); } @@ -642,20 +663,23 @@ inline int reporter_process_report (struct ReportHeader *reporthdr) { } } reporter_print_connection_report(creport); - fflush(stdout); FreeReport(reporthdr); } break; case SETTINGS_REPORT: reporter_print_settings_report((struct ReportSettings *)reporthdr->this_report); - fflush(stdout); FreeReport(reporthdr); break; case SERVER_RELAY_REPORT: reporter_print_server_relay_report((struct ServerRelay *)reporthdr->this_report); - fflush(stdout); FreeReport(reporthdr); break; + case STRING_REPORT: + if (reporthdr->this_report) { + printf("%s\n", (char *)reporthdr->this_report); + free((char *)reporthdr->this_report); + } + break; default: fprintf(stderr,"Invalid report type in process report %p\n", reporthdr->this_report); assert(0); @@ -675,7 +699,7 @@ inline int reporter_process_report (struct ReportHeader *reporthdr) { // Reporter private routines void reporter_handle_packet_null (struct ReporterData *data, struct ReportStruct *packet) { } -void reporter_transfer_protocol_null (struct ReporterData *data, int final){ +void reporter_transfer_protocol_null (struct ReporterData *data, bool final){ } static inline void reporter_compute_packet_pps (struct TransferInfo *stats, struct ReportStruct *packet) { @@ -694,7 +718,7 @@ static void reporter_handle_packet_oneway_transit (struct TransferInfo *stats, s // Transit or latency updates done inline below double transit = TimeDifference(packet->packetTime, packet->sentTime); if (stats->latency_histogram) { - histogram_insert(stats->latency_histogram, transit, NULL); + histogram_insert(stats->latency_histogram, transit, &packet->packetTime); } double deltaTransit; deltaTransit = transit - stats->transit.current.last; @@ -756,7 +780,6 @@ static void reporter_handle_packet_oneway_transit (struct TransferInfo *stats, s } } - static void reporter_handle_isoch_oneway_transit_tcp (struct TransferInfo *stats, struct ReportStruct *packet) { // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining); if (packet->frameID && packet->transit_ready) { @@ -908,12 +931,12 @@ static void reporter_handle_frame_isoch_oneway_transit (struct TransferInfo *sta void reporter_handle_packet_client (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; stats->ts.packetTime = packet->packetTime; - if (!packet->emptyreport) { + switch (packet->err_readwrite) { + case WriteSelectRetry : + stats->sock_callstats.write.WriteErr++; + stats->sock_callstats.write.totWriteErr++; + case WriteSuccess : stats->total.Bytes.current += packet->packetLen; - if (packet->errwrite && (packet->errwrite != WriteErrNoAccount)) { - stats->sock_callstats.write.WriteErr++; - stats->sock_callstats.write.totWriteErr++; - } // These are valid packets that need standard iperf accounting stats->sock_callstats.write.WriteCnt += packet->writecnt; stats->sock_callstats.write.totWriteCnt += packet->writecnt; @@ -929,7 +952,20 @@ void reporter_handle_packet_client (struct ReporterData *data, struct ReportStru histogram_insert(stats->write_histogram, (1e-6 * packet->write_time), NULL); } } + break; + case WriteTimeo: + stats->sock_callstats.write.WriteTimeo++; + stats->sock_callstats.write.totWriteTimeo++; + case WriteErrAccount : + stats->sock_callstats.write.WriteErr++; + stats->sock_callstats.write.totWriteErr++; + case WriteNoAccount: + case NullEvent: + break; + default : + fprintf(stderr, "Program error: invalid client packet->err_readwrite %d\n", packet->err_readwrite); } + if (isUDP(stats->common)) { stats->PacketID = packet->packetID; reporter_compute_packet_pps(stats, packet); @@ -945,25 +981,47 @@ void reporter_handle_packet_bb_client (struct ReporterData *data, struct ReportS if (!packet->emptyreport && (packet->packetLen > 0)) { stats->total.Bytes.current += packet->packetLen; double bbrtt = TimeDifference(packet->packetTime, packet->sentTime); - double bbowdto = TimeDifference(packet->sentTimeRX, packet->sentTime); - double bbowdfro = TimeDifference(packet->packetTime, packet->sentTimeTX); - double asym = bbowdfro - bbowdto; - stats->ts.prevpacketTime = packet->packetTime; -#if DEBUG_BB_TIMESTAMPS - fprintf(stderr, "BB Debug: ctx=%lx.%lx srx=%lx.%lx stx=%lx.%lx crx=%lx.%lx (hex)\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); - fprintf(stderr, "BB Debug: ctx=%ld.%ld srx=%ld.%ld stx=%ld.%ld crx=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); - fprintf(stderr, "BB RTT=%f OWDTo=%f OWDFro=%f Asym=%f\n", bbrtt, bbowdto, bbowdfro, asym); -#endif stats->iBBrunning += bbrtt; stats->fBBrunning += bbrtt; reporter_update_mmm(&stats->bbrtt.current, bbrtt); reporter_update_mmm(&stats->bbrtt.total, bbrtt); - reporter_update_mmm(&stats->bbowdto.total, bbowdto); - reporter_update_mmm(&stats->bbowdfro.total, bbowdfro); - reporter_update_mmm(&stats->bbasym.total, fabs(asym)); if (stats->bbrtt_histogram) { histogram_insert(stats->bbrtt_histogram, bbrtt, NULL); } + if (isTripTime(stats->common)) { + double bbowdto = TimeDifference(packet->sentTimeRX, packet->sentTime); + double bbowdfro = TimeDifference(packet->packetTime, packet->sentTimeTX); + double asym = bbowdfro - bbowdto; + double bbturnaround = TimeDifference(packet->sentTimeTX, packet->sentTimeRX); + double bbadj = TimeDifference(packet->packetTime, packet->sentTimeRX); + // If you measure RTT, you can detect when clock are unsync. + // If you have the sent-time, rcv-time and return-time, you can check that + // sent-time < rcv-time < return-time. As sent-time and return-time use + // the same clock, you can detect any drift bigger than RTT. JT + // + // Adjust this clock A write < clock B read < Clock A read - (clock B write - clock B read) + if ((bbowdto < 0) || ((bbadj - bbturnaround) < 0)) { + stats->bb_clocksync_error++; + } + reporter_update_mmm(&stats->bbowdto.total, bbowdto); + reporter_update_mmm(&stats->bbowdfro.total, bbowdfro); + reporter_update_mmm(&stats->bbasym.total, fabs(asym)); + reporter_update_mmm(&stats->bbowdto.current, bbowdto); + reporter_update_mmm(&stats->bbowdfro.current, bbowdfro); + reporter_update_mmm(&stats->bbasym.current, fabs(asym)); + if (stats->bbowdto_histogram) { + histogram_insert(stats->bbowdto_histogram, bbowdto, NULL); + } + if (stats->bbowdfro_histogram) { + histogram_insert(stats->bbowdfro_histogram, bbowdfro, NULL); + } + } + stats->ts.prevpacketTime = packet->packetTime; +#if DEBUG_BB_TIMESTAMPS + fprintf(stderr, "BB Debug: ctx=%lx.%lx srx=%lx.%lx stx=%lx.%lx crx=%lx.%lx (hex)\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); + fprintf(stderr, "BB Debug: ctx=%ld.%ld srx=%ld.%ld stx=%ld.%ld crx=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); + fprintf(stderr, "BB RTT=%f OWDTo=%f OWDFro=%f Asym=%f\n", bbrtt, bbowdto, bbowdfro, asym); +#endif } } @@ -981,8 +1039,7 @@ inline void reporter_handle_packet_server_tcp (struct ReporterData *data, struct int bin; stats->total.Bytes.current += packet->packetLen; // mean min max tests - stats->sock_callstats.read.cntRead++; - stats->sock_callstats.read.totcntRead++; + stats->sock_callstats.read.ReadCnt.current++; bin = (int)floor((packet->packetLen -1)/stats->sock_callstats.read.binsize); if (bin < TCPREADBINCOUNT) { stats->sock_callstats.read.bins[bin]++; @@ -1007,8 +1064,21 @@ inline void reporter_handle_packet_server_udp (struct ReporterData *data, struct // Hence, set the per interval min to infinity // and the per interval max and sum to zero reporter_reset_mmm(&stats->transit.current); - } else if (packet->packetID > 0) { - stats->total.Bytes.current += packet->packetLen; + } else if (!packet->emptyreport && (packet->packetID > 0)) { + bool ooo_packet = false; + // packet loss occured if the datagram numbers aren't sequential + if (packet->packetID != stats->PacketID + 1) { + if (packet->packetID < stats->PacketID + 1) { + stats->total.OutofOrder.current++; + ooo_packet = true; + } else { + stats->total.Lost.current += packet->packetID - stats->PacketID - 1; + } + } + // never decrease datagramID (e.g. if we get an out-of-order packet) + if (packet->packetID > stats->PacketID) { + stats->PacketID = packet->packetID; + } // These are valid packets that need standard iperf accounting // Do L2 accounting first (if needed) if (packet->l2errors && (stats->total.Datagrams.current > L2DROPFILTERCOUNTER)) { @@ -1027,20 +1097,17 @@ inline void reporter_handle_packet_server_udp (struct ReporterData *data, struct stats->l2counts.tot_udpcsumerr++; } } - // packet loss occured if the datagram numbers aren't sequential - if (packet->packetID != stats->PacketID + 1) { - if (packet->packetID < stats->PacketID + 1) { - stats->total.OutofOrder.current++; - } else { - stats->total.Lost.current += packet->packetID - stats->PacketID - 1; - } + if (packet->err_readwrite == ReadErrLen) { + stats->sock_callstats.read.ReadErrLenCnt.current++; } - // never decrease datagramID (e.g. if we get an out-of-order packet) - if (packet->packetID > stats->PacketID) { - stats->PacketID = packet->packetID; + if (!ooo_packet && \ + ((packet->err_readwrite == ReadSuccess) || + ((packet->err_readwrite == ReadErrLen) && (packet->packetLen >= sizeof(struct UDP_datagram))))) { + reporter_handle_packet_oneway_transit(stats, packet); } + stats->total.Bytes.current += packet->packetLen; reporter_compute_packet_pps(stats, packet); - reporter_handle_packet_oneway_transit(stats, packet); + if (packet->transit_ready) { if (isIsochronous(stats->common)) { reporter_handle_isoch_oneway_transit_udp(stats, packet); @@ -1049,9 +1116,14 @@ inline void reporter_handle_packet_server_udp (struct ReporterData *data, struct } } } + if (packet->err_readwrite != ReadNoAccount) { + if (packet->emptyreport) { + stats->sock_callstats.read.ReadTimeoCnt.current++; + } else { + stats->sock_callstats.read.ReadCnt.current++; + } + } } - -// This is done in reporter thread context #if HAVE_TCP_STATS static inline void reporter_handle_packet_tcpistats (struct ReporterData *data, struct ReportStruct *packet) { assert(data!=NULL); @@ -1060,12 +1132,22 @@ static inline void reporter_handle_packet_tcpistats (struct ReporterData *data, stats->sock_callstats.write.tcpstats.retry_prev = packet->tcpstats.retry_tot; stats->sock_callstats.write.tcpstats.retry_tot = packet->tcpstats.retry_tot; stats->sock_callstats.write.tcpstats.cwnd = packet->tcpstats.cwnd; + stats->sock_callstats.write.tcpstats.cwnd_packets = packet->tcpstats.cwnd_packets; stats->sock_callstats.write.tcpstats.rtt = packet->tcpstats.rtt; stats->sock_callstats.write.tcpstats.rttvar = packet->tcpstats.rttvar; +#if HAVE_TCP_INFLIGHT + stats->sock_callstats.write.tcpstats.packets_in_flight = packet->tcpstats.packets_in_flight; + stats->sock_callstats.write.tcpstats.bytes_in_flight = packet->tcpstats.bytes_in_flight; +#else + stats->sock_callstats.write.tcpstats.bytes_in_flight = -1; + stats->sock_callstats.write.tcpstats.packets_in_flight = -1; +#endif +#if (HAVE_DECL_SO_MAX_PACING_RATE) + stats->FQPacingRateCurrent = packet->FQPacingRate; +#endif } #endif - /* * Report printing routines below */ @@ -1110,6 +1192,22 @@ static inline void reporter_set_timestamps_time (struct TransferInfo *stats, enu } } +#if HAVE_SUMMING_DEBUG +static void reporter_dump_timestamps (struct ReportStruct *packet, struct TransferInfo *stats, struct TransferInfo *sumstats) { + if (packet) + printf("**** %s pkt =%ld.%ld (up/down)=%d/%d\n", stats->common->transferIDStr, (long) packet->packetTime.tv_sec, \ + (long) packet->packetTime.tv_usec, sumstats->slot_thread_upcount, sumstats->slot_thread_downcount); + else { + printf("**** %s pkt ts =%ld.%ld prev=%ld.%ld (up/down)=%d/%d\n", stats->common->transferIDStr, (long) stats->ts.packetTime.tv_sec, \ + (long) stats->ts.packetTime.tv_usec, (long) stats->ts.prevpacketTime.tv_sec, (long) stats->ts.prevpacketTime.tv_usec, sumstats->slot_thread_upcount, sumstats->slot_thread_downcount); + } + printf("**** %s stats =%ld.%ld next=%ld.%ld prev=%ld.%ld\n", stats->common->transferIDStr, (long) stats->ts.packetTime.tv_sec, \ + (long) stats->ts.packetTime.tv_usec, (long) stats->ts.nextTime.tv_sec, (long) stats->ts.nextTime.tv_usec, (long) stats->ts.prevpacketTime.tv_sec, (long) stats->ts.prevpacketTime.tv_usec); + printf("**** %s sum stats=%ld.%ld next=%ld.%ld prev=%ld.%ld \n", stats->common->transferIDStr, (long) sumstats->ts.packetTime.tv_sec, \ + (long) sumstats->ts.packetTime.tv_usec, (long) sumstats->ts.nextTime.tv_sec, (long) sumstats->ts.nextTime.tv_usec, (long) sumstats->ts.prevTime.tv_sec, (long) sumstats->ts.prevTime.tv_usec); +} +#endif + // If reports were missed, catch up now static inline void reporter_transfer_protocol_missed_reports (struct TransferInfo *stats, struct ReportStruct *packet) { while (TimeDifference(packet->packetTime, stats->ts.nextTime) > TimeDouble(stats->ts.intervalTime)) { @@ -1125,10 +1223,24 @@ static inline void reporter_transfer_protocol_missed_reports (struct TransferInf } } +static inline void reporter_reset_transfer_stats_sum (struct TransferInfo *sumstats) { +#if HAVE_SUMMING_DEBUG + printf("***** [SUM] RESET %ld.%ld nxt %ld.%ld down=%d up=%d\n", (long) sumstats->ts.prevTime.tv_sec, (long) sumstats->ts.prevTime.tv_usec, \ + (long) sumstats->ts.nextTime.tv_sec, (long) sumstats->ts.nextTime.tv_usec, sumstats->slot_thread_downcount, sumstats->slot_thread_upcount); +#endif + sumstats->slot_thread_upcount -= sumstats->slot_thread_downcount; + sumstats->slot_thread_downcount = 0; + sumstats->ts.prevTime = sumstats->ts.nextTime; + sumstats->iInP = 0; + sumstats->uplevel = toggleLevel(sumstats->uplevel); + sumstats->downlevel = toggleLevel(sumstats->downlevel); +} + static inline void reporter_reset_transfer_stats_client_tcp (struct TransferInfo *stats) { stats->total.Bytes.prev = stats->total.Bytes.current; stats->sock_callstats.write.WriteCnt = 0; stats->sock_callstats.write.WriteErr = 0; + stats->sock_callstats.write.WriteTimeo = 0; stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current; stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current; stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current; @@ -1144,13 +1256,7 @@ static inline void reporter_reset_transfer_stats_client_tcp (struct TransferInfo reporter_reset_mmm(&stats->bbasym.current); } if (isTcpWriteTimes(stats->common)) { - stats->write_mmm.current.cnt = 0; - stats->write_mmm.current.min = FLT_MAX; - stats->write_mmm.current.max = FLT_MIN; - stats->write_mmm.current.sum = 0; - stats->write_mmm.current.vd = 0; - stats->write_mmm.current.mean = 0; - stats->write_mmm.current.m2 = 0; + reporter_reset_mmm(&stats->write_mmm.current); } } @@ -1164,6 +1270,7 @@ static inline void reporter_reset_transfer_stats_client_udp (struct TransferInfo stats->total.IPG.prev = stats->total.IPG.current; stats->sock_callstats.write.WriteCnt = 0; stats->sock_callstats.write.WriteErr = 0; + stats->sock_callstats.write.WriteTimeo = 0; stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current; stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current; stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current; @@ -1174,7 +1281,7 @@ static inline void reporter_reset_transfer_stats_client_udp (struct TransferInfo static inline void reporter_reset_transfer_stats_server_tcp (struct TransferInfo *stats) { int ix; stats->total.Bytes.prev = stats->total.Bytes.current; - stats->sock_callstats.read.cntRead = 0; + stats->sock_callstats.read.ReadCnt.prev = stats->sock_callstats.read.ReadCnt.current; for (ix = 0; ix < 8; ix++) { stats->sock_callstats.read.bins[ix] = 0; } @@ -1198,8 +1305,10 @@ static inline void reporter_reset_transfer_stats_server_udp (struct TransferInfo stats->l2counts.unknown = 0; stats->l2counts.udpcsumerr = 0; stats->l2counts.lengtherr = 0; - stats->threadcnt = 0; stats->iInP = 0; + stats->sock_callstats.read.ReadCnt.prev = stats->sock_callstats.read.ReadCnt.current; + stats->sock_callstats.read.ReadTimeoCnt.prev = stats->sock_callstats.read.ReadTimeoCnt.current; + stats->sock_callstats.read.ReadErrLenCnt.prev = stats->sock_callstats.read.ReadErrLenCnt.current; if (stats->cntDatagrams) stats->IPGsum = 0; } @@ -1209,7 +1318,7 @@ static inline void reporter_reset_transfer_stats_server_udp (struct TransferInfo // o) set the TransferInfo struct and then calls the individual report output handler // o) updates the sum and fullduplex reports // -void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_server_udp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL; struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL; @@ -1223,6 +1332,9 @@ void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final stats->cntError = 0; stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev; stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current - stats->sock_callstats.read.ReadCnt.prev; + stats->sock_callstats.read.cntReadTimeo = stats->sock_callstats.read.ReadTimeoCnt.current - stats->sock_callstats.read.ReadTimeoCnt.prev; + stats->sock_callstats.read.cntReadErrLen = stats->sock_callstats.read.ReadErrLenCnt.current - stats->sock_callstats.read.ReadErrLenCnt.prev; if (stats->latency_histogram) { stats->latency_histogram->final = final; @@ -1258,8 +1370,24 @@ void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final sumstats->total.IPG.current += stats->cntIPG; if (sumstats->IPGsum < stats->IPGsum) sumstats->IPGsum = stats->IPGsum; - sumstats->threadcnt++; sumstats->iInP += stats->iInP; + sumstats->sock_callstats.read.cntRead += stats->sock_callstats.read.cntRead; + sumstats->sock_callstats.read.cntReadTimeo += stats->sock_callstats.read.cntReadTimeo; + sumstats->sock_callstats.read.cntReadErrLen += stats->sock_callstats.read.cntReadErrLen; + if (final) { + sumstats->threadcnt_final++; + if (data->packetring->downlevel != sumstats->downlevel) { + sumstats->slot_thread_downcount++; + } + if (data->packetring->uplevel != sumstats->uplevel){ + sumstats->slot_thread_upcount++; + } +#if HAVE_SUMMING_DEBUG + printf("**** %s downcnt (%p) (up/down)=%d/%d final true uplevel (sum/pkt)=%d/%d\n", stats->common->transferIDStr, (void *)data->packetring, \ + sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, data->packetring->uplevel); +#endif + } } if (fullduplexstats) { fullduplexstats->total.Bytes.current += stats->cntBytes; @@ -1321,20 +1449,23 @@ void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final if (stats->latency_histogram) { if (sumstats && sumstats->latency_histogram) { histogram_add(sumstats->latency_histogram, stats->latency_histogram); - sumstats->latency_histogram->final = 1; + sumstats->latency_histogram->final = true; } - stats->latency_histogram->final = 1; + stats->latency_histogram->final = true; } if (stats->jitter_histogram) { if (sumstats && sumstats->jitter_histogram) { histogram_add(sumstats->jitter_histogram, stats->jitter_histogram); - sumstats->jitter_histogram->final = 1; + sumstats->jitter_histogram->final = true; } - stats->jitter_histogram->final = 1; + stats->jitter_histogram->final = true; } if (stats->framelatency_histogram) { - stats->framelatency_histogram->final = 1; + stats->framelatency_histogram->final = true; } + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current; + stats->sock_callstats.read.cntReadTimeo = stats->sock_callstats.read.ReadTimeoCnt.current; + stats->sock_callstats.read.cntReadErrLen = stats->sock_callstats.read.ReadErrLenCnt.current; } if ((stats->output_handler) && !(stats->isMaskOutput)) (*stats->output_handler)(stats); @@ -1342,7 +1473,7 @@ void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final reporter_reset_transfer_stats_server_udp(stats); } -void reporter_transfer_protocol_sum_server_udp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_sum_server_udp (struct TransferInfo *stats, bool final) { if (final) { reporter_set_timestamps_time(stats, TOTAL); stats->cntOutofOrder = stats->total.OutofOrder.current; @@ -1368,15 +1499,16 @@ void reporter_transfer_protocol_sum_server_udp (struct TransferInfo *stats, int stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; } - if ((stats->output_handler) && !(stats->isMaskOutput)) + if ((stats->output_handler) && !(stats->isMaskOutput)) { (*stats->output_handler)(stats); + } if (!final) { // there is no packet ID for sum server reports, set it to total cnt for calculation stats->PacketID = stats->total.Datagrams.current; reporter_reset_transfer_stats_server_udp(stats); } } -void reporter_transfer_protocol_sum_client_udp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_sum_client_udp (struct TransferInfo *stats, bool final) { if (final) { reporter_set_timestamps_time(stats, TOTAL); stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr; @@ -1390,19 +1522,18 @@ void reporter_transfer_protocol_sum_client_udp (struct TransferInfo *stats, int stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev; stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; } - if ((stats->output_handler) && !(stats->isMaskOutput)) + if ((stats->output_handler) && !(stats->isMaskOutput)) { (*stats->output_handler)(stats); - + } if (!final) { - stats->threadcnt = 0; reporter_reset_transfer_stats_client_udp(stats); } else if ((stats->common->ReportMode != kReport_CSV) && !(stats->isMaskOutput)) { - printf(report_sumcnt_datagrams, stats->threadcnt, stats->total.Datagrams.current); + printf(report_sumcnt_datagrams, stats->threadcnt_final, stats->total.Datagrams.current); fflush(stdout); } } -void reporter_transfer_protocol_client_udp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_client_udp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL; struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL; @@ -1418,13 +1549,28 @@ void reporter_transfer_protocol_client_udp (struct ReporterData *data, int final sumstats->total.Bytes.current += stats->cntBytes; sumstats->sock_callstats.write.WriteErr += stats->sock_callstats.write.WriteErr; sumstats->sock_callstats.write.WriteCnt += stats->sock_callstats.write.WriteCnt; + sumstats->sock_callstats.write.WriteTimeo += stats->sock_callstats.write.WriteTimeo; sumstats->sock_callstats.write.totWriteErr += stats->sock_callstats.write.WriteErr; sumstats->sock_callstats.write.totWriteCnt += stats->sock_callstats.write.WriteCnt; + sumstats->sock_callstats.write.totWriteTimeo += stats->sock_callstats.write.WriteTimeo; sumstats->total.Datagrams.current += stats->cntDatagrams; if (sumstats->IPGsum < stats->IPGsum) sumstats->IPGsum = stats->IPGsum; sumstats->total.IPG.current += stats->cntIPG; - sumstats->threadcnt++; + if (final) { + sumstats->threadcnt_final++; + if (data->packetring->downlevel != sumstats->downlevel) { + sumstats->slot_thread_downcount++; + } + if (data->packetring->uplevel != sumstats->uplevel){ + sumstats->slot_thread_upcount++; + } +#if HAVE_SUMMING_DEBUG + printf("**** %s downcnt (%p) (up/down)=%d/%d final true level (sum/pkt)=%d/%d\n", stats->common->transferIDStr, (void *)data->packetring, \ + sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, data->packetring->uplevel); +#endif + } } if (fullduplexstats) { fullduplexstats->total.Bytes.current += stats->cntBytes; @@ -1438,6 +1584,7 @@ void reporter_transfer_protocol_client_udp (struct ReporterData *data, int final stats->cntBytes = stats->total.Bytes.current; stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr; stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt; + stats->sock_callstats.write.WriteTimeo = stats->sock_callstats.write.totWriteTimeo; stats->cntIPG = stats->total.IPG.current; stats->cntDatagrams = stats->PacketID; stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime); @@ -1463,14 +1610,14 @@ void reporter_transfer_protocol_client_udp (struct ReporterData *data, int final reporter_reset_transfer_stats_client_udp(stats); } -void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_server_tcp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL; struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL; stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; int ix; if (stats->framelatency_histogram) { - stats->framelatency_histogram->final = 0; + stats->framelatency_histogram->final = false; } double thisInP; if (!final) { @@ -1479,18 +1626,18 @@ void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final double meantransit = (double) ((stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0.0); thisInP = lambda * meantransit; stats->iInP = thisInP; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current - stats->sock_callstats.read.ReadCnt.prev; } else { double bytecnt = (double) stats->cntBytes; double lambda = (stats->IPGsum > 0.0) ? (bytecnt / stats->IPGsum) : 0.0; double meantransit = (double) ((stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0.0); thisInP = lambda * meantransit; stats->fInP = thisInP; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current; } if (sumstats) { - sumstats->threadcnt++; sumstats->total.Bytes.current += stats->cntBytes; - sumstats->sock_callstats.read.cntRead += stats->sock_callstats.read.cntRead; - sumstats->sock_callstats.read.totcntRead += stats->sock_callstats.read.cntRead; + sumstats->sock_callstats.read.ReadCnt.current += stats->sock_callstats.read.cntRead; for (ix = 0; ix < TCPREADBINCOUNT; ix++) { sumstats->sock_callstats.read.bins[ix] += stats->sock_callstats.read.bins[ix]; sumstats->sock_callstats.read.totbins[ix] += stats->sock_callstats.read.bins[ix]; @@ -1499,6 +1646,18 @@ void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final sumstats->iInP += thisInP; } else { sumstats->fInP += thisInP; + sumstats->threadcnt_final++; + if (data->packetring->downlevel != sumstats->downlevel) { + sumstats->slot_thread_downcount++; + } + if (data->packetring->uplevel != sumstats->uplevel){ + sumstats->slot_thread_upcount++; + } +#if HAVE_SUMMING_DEBUG + printf("**** %s downcnt (%p) (up/down)=%d/%d final true level (sum/pkt)=%d/%d\n", stats->common->transferIDStr, (void *)data->packetring, \ + sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, data->packetring->uplevel); +#endif } } if (fullduplexstats) { @@ -1520,12 +1679,12 @@ void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final } } if (stats->framelatency_histogram) { - stats->framelatency_histogram->final = 1; + stats->framelatency_histogram->final = true; } reporter_set_timestamps_time(stats, TOTAL); stats->cntBytes = stats->total.Bytes.current; stats->IPGsum = stats->ts.iEnd; - stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current; for (ix = 0; ix < TCPREADBINCOUNT; ix++) { stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix]; } @@ -1538,9 +1697,9 @@ void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final if (stats->framelatency_histogram) { if (sumstats && sumstats->framelatency_histogram) { histogram_add(sumstats->framelatency_histogram, stats->framelatency_histogram); - sumstats->framelatency_histogram->final = 1; + sumstats->framelatency_histogram->final = true; } - stats->framelatency_histogram->final = 1; + stats->framelatency_histogram->final = true; } } else if (isIsochronous(stats->common)) { stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev; @@ -1557,7 +1716,7 @@ void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final reporter_reset_transfer_stats_server_tcp(stats); } -void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_client_tcp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL; struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL; @@ -1585,7 +1744,24 @@ void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final sumstats->sock_callstats.write.WriteCnt += stats->sock_callstats.write.WriteCnt; sumstats->sock_callstats.write.totWriteErr += stats->sock_callstats.write.WriteErr; sumstats->sock_callstats.write.totWriteCnt += stats->sock_callstats.write.WriteCnt; - sumstats->threadcnt++; + if (final) { + sumstats->threadcnt_final++; + if (data->packetring->downlevel != sumstats->downlevel) { + sumstats->slot_thread_downcount++; + } + if (data->packetring->uplevel != sumstats->uplevel){ + sumstats->slot_thread_upcount++; + } +#if HAVE_SUMMING_DEBUG + printf("**** %s downcnt (%p) (up/down)=%d/%d final true level (sum/pkt)=%d/%d\n", stats->common->transferIDStr, (void *)data->packetring, \ + sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, data->packetring->uplevel); +#endif + } +#if HAVE_SUMMING_DEBUG + reporter_dump_timestamps(NULL, stats, sumstats); +#endif + #if HAVE_TCP_STATS sumstats->sock_callstats.write.tcpstats.retry += stats->sock_callstats.write.tcpstats.retry; sumstats->sock_callstats.write.tcpstats.retry_tot += stats->sock_callstats.write.tcpstats.retry; @@ -1596,10 +1772,10 @@ void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final } if (final) { if (stats->latency_histogram) { - stats->latency_histogram->final = 1; + stats->latency_histogram->final = true; } if (stats->write_histogram) { - stats->write_histogram->final = 1; + stats->write_histogram->final = true; } if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { // print a partial interval report if enable and this a final @@ -1626,7 +1802,7 @@ void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; #endif if (stats->framelatency_histogram) { - stats->framelatency_histogram->final = 1; + stats->framelatency_histogram->final = true; } stats->cntBytes = stats->total.Bytes.current; stats->write_mmm.current = stats->write_mmm.total; @@ -1646,19 +1822,20 @@ void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final /* * Handles summing of threads */ -void reporter_transfer_protocol_sum_client_tcp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_sum_client_tcp (struct TransferInfo *stats, bool final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; if (final) { if ((stats->output_handler) && !(stats->isMaskOutput)) { reporter_set_timestamps_time(stats, FINALPARTIAL); - if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) { (*stats->output_handler)(stats); + } reporter_reset_transfer_stats_client_tcp(stats); } } else if ((stats->output_handler) && !(stats->isMaskOutput)) { (*stats->output_handler)(stats); - stats->threadcnt = 0; + reporter_reset_transfer_stats_sum(stats); } reporter_reset_transfer_stats_client_tcp(stats); } @@ -1675,7 +1852,7 @@ void reporter_transfer_protocol_sum_client_tcp (struct TransferInfo *stats, int } } -void reporter_transfer_protocol_client_bb_tcp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_client_bb_tcp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; if (final) { @@ -1703,7 +1880,7 @@ void reporter_transfer_protocol_client_bb_tcp (struct ReporterData *data, int fi } } -void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, int final) { +void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, bool final) { struct TransferInfo *stats = &data->info; if (final) { if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { @@ -1716,7 +1893,6 @@ void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, int fi } } #if HAVE_TCP_STATS - stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; #endif stats->cntBytes = stats->total.Bytes.current; @@ -1730,26 +1906,27 @@ void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, int fi reporter_reset_transfer_stats_client_tcp(stats); } -void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, bool final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current - stats->sock_callstats.read.ReadCnt.prev; if (final) { if ((stats->output_handler) && !(stats->isMaskOutput)) { reporter_set_timestamps_time(stats, FINALPARTIAL); - if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) { (*stats->output_handler)(stats); + } } } else if ((stats->output_handler) && !(stats->isMaskOutput)) { (*stats->output_handler)(stats); - stats->threadcnt = 0; - stats->iInP = 0; + reporter_reset_transfer_stats_sum(stats); } reporter_reset_transfer_stats_server_tcp(stats); } if (final) { int ix; stats->cntBytes = stats->total.Bytes.current; - stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current; for (ix = 0; ix < TCPREADBINCOUNT; ix++) { stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix]; } @@ -1759,7 +1936,7 @@ void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, int (*stats->output_handler)(stats); } } -void reporter_transfer_protocol_fullduplex_tcp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_fullduplex_tcp (struct TransferInfo *stats, bool final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; if (final) { @@ -1781,7 +1958,7 @@ void reporter_transfer_protocol_fullduplex_tcp (struct TransferInfo *stats, int (*stats->output_handler)(stats); } -void reporter_transfer_protocol_fullduplex_udp (struct TransferInfo *stats, int final) { +void reporter_transfer_protocol_fullduplex_udp (struct TransferInfo *stats, bool final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev; @@ -1814,11 +1991,11 @@ void reporter_transfer_protocol_fullduplex_udp (struct TransferInfo *stats, int } // Conditional print based on time -int reporter_condprint_time_interval_report (struct ReporterData *data, struct ReportStruct *packet) { +bool reporter_condprint_time_interval_report (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; assert(stats!=NULL); // printf("***sum handler = %p\n", (void *) data->GroupSumReport->transfer_protocol_sum_handler); - int advance_jobq = 0; + bool advance_jobq = false; // Print a report if packet time exceeds the next report interval time, // Also signal to the caller to move to the next report (or packet ring) // if there was output. This will allow for more precise interval sum accounting. @@ -1826,7 +2003,7 @@ int reporter_condprint_time_interval_report (struct ReporterData *data, struct R // printf("***** nt %ld.%ld pt %ld.%ld pid=%lld empty=%d\n", stats->ts.nextTime.tv_sec, stats->ts.nextTime.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID, packet->emptyreport); if (TimeDifference(stats->ts.nextTime, packet->packetTime) < 0) { assert(data->transfer_protocol_handler!=NULL); - advance_jobq = 1; + advance_jobq = true; struct TransferInfo *sumstats = (data->GroupSumReport ? &data->GroupSumReport->info : NULL); struct TransferInfo *fullduplexstats = (data->FullDuplexReport ? &data->FullDuplexReport->info : NULL); stats->ts.packetTime = packet->packetTime; @@ -1838,10 +2015,19 @@ int reporter_condprint_time_interval_report (struct ReporterData *data, struct R if (fullduplexstats && ((++data->FullDuplexReport->threads) == 2) && isEnhanced(stats->common)) { data->FullDuplexReport->threads = 0; assert(data->FullDuplexReport->transfer_protocol_sum_handler != NULL); - (*data->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, 0); + (*data->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, false); } if (sumstats) { - if ((++data->GroupSumReport->threads) == data->GroupSumReport->reference.count) { + if (data->packetring->downlevel != sumstats->downlevel) { + sumstats->slot_thread_downcount++; + data->packetring->downlevel = toggleLevel(data->packetring->downlevel); +#if HAVE_SUMMING_DEBUG + printf("**** %s downcnt (%p) pkt=%ld.%ld (up/down)=%d/%d final false level (sum/pkt)=%d/%d\n", stats->common->transferIDStr, (void *)data->packetring, \ + (long) packet->packetTime.tv_sec, (long) packet->packetTime.tv_usec, sumstats->slot_thread_upcount, sumstats->slot_thread_downcount, \ + sumstats->uplevel, data->packetring->uplevel); +#endif + } + if ((sumstats->slot_thread_downcount) == sumstats->slot_thread_upcount) { data->GroupSumReport->threads = 0; if ((data->GroupSumReport->reference.count > (fullduplexstats ? 2 : 1)) || \ isSumOnly(data->info.common)) { @@ -1849,9 +2035,12 @@ int reporter_condprint_time_interval_report (struct ReporterData *data, struct R } else { sumstats->isMaskOutput = true; } +#if HAVE_SUMMING_DEBUG + reporter_dump_timestamps(packet, stats, sumstats); +#endif reporter_set_timestamps_time(sumstats, INTERVAL); assert(data->GroupSumReport->transfer_protocol_sum_handler != NULL); - (*data->GroupSumReport->transfer_protocol_sum_handler)(sumstats, 0); + (*data->GroupSumReport->transfer_protocol_sum_handler)(sumstats, false); } } // In the (hopefully unlikely event) the reporter fell behind @@ -1863,9 +2052,9 @@ int reporter_condprint_time_interval_report (struct ReporterData *data, struct R } // Conditional print based on bursts or frames -int reporter_condprint_frame_interval_report_server_udp (struct ReporterData *data, struct ReportStruct *packet) { +bool reporter_condprint_frame_interval_report_server_udp (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; - int advance_jobq = 0; + bool advance_jobq = false; // first packet of a burst and not a duplicate if ((packet->burstsize == (packet->remaining + packet->packetLen)) && (stats->matchframeID != packet->frameID)) { stats->matchframeID=packet->frameID; @@ -1887,35 +2076,36 @@ int reporter_condprint_frame_interval_report_server_udp (struct ReporterData *da if ((stats->output_handler) && !(stats->isMaskOutput)) (*stats->output_handler)(stats); reporter_reset_transfer_stats_server_udp(stats); - advance_jobq = 1; + advance_jobq = true; } return advance_jobq; } -int reporter_condprint_frame_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) { +bool reporter_condprint_frame_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) { fprintf(stderr, "FIX ME\n"); - return 1; + return true; } -int reporter_condprint_burst_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) { +bool reporter_condprint_burst_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; - int advance_jobq = 0; + int advance_jobq = false; if (packet->transit_ready) { stats->ts.prevpacketTime = packet->sentTime; stats->ts.packetTime = packet->packetTime; reporter_set_timestamps_time(stats, INTERVALPARTIAL); stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + stats->sock_callstats.read.cntRead = stats->sock_callstats.read.ReadCnt.current - stats->sock_callstats.read.ReadCnt.prev; if ((stats->output_handler) && !(stats->isMaskOutput)) (*stats->output_handler)(stats); reporter_reset_transfer_stats_server_tcp(stats); - advance_jobq = 1; + advance_jobq = true; } return advance_jobq; } -int reporter_condprint_burst_interval_report_client_tcp (struct ReporterData *data, struct ReportStruct *packet) { +bool reporter_condprint_burst_interval_report_client_tcp (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; - int advance_jobq = 0; + int advance_jobq = false; // first packet of a burst and not a duplicate if (packet->transit_ready) { reporter_handle_packet_oneway_transit(stats, packet); @@ -1927,7 +2117,7 @@ int reporter_condprint_burst_interval_report_client_tcp (struct ReporterData *da if ((stats->output_handler) && !(stats->isMaskOutput)) (*stats->output_handler)(stats); reporter_reset_transfer_stats_client_tcp(stats); - advance_jobq = 1; + advance_jobq = true; } return advance_jobq; } diff --git a/src/Reports.c b/src/Reports.c index 008f7f4..296078e 100644 --- a/src/Reports.c +++ b/src/Reports.c @@ -59,6 +59,7 @@ #include "Locale.h" #include "active_hosts.h" #include "payloads.h" + static int transferid_counter = 0; static inline int my_str_copy(char **dst, char *src) { @@ -90,6 +91,7 @@ static void common_copy (struct ReportCommon **common, struct thread_Settings *i my_str_copy(&(*common)->Ifrnametx, inSettings->mIfrnametx); my_str_copy(&(*common)->SSMMulticastStr, inSettings->mSSMMulticastStr); my_str_copy(&(*common)->Congestion, inSettings->mCongestion); + my_str_copy(&(*common)->LoadCCA, inSettings->mLoadCCA); my_str_copy(&(*common)->transferIDStr, inSettings->mTransferIDStr); my_str_copy(&(*common)->PermitKey, inSettings->mPermitKey); @@ -107,6 +109,9 @@ static void common_copy (struct ReportCommon **common, struct thread_Settings *i (*common)->MSS = inSettings->mMSS; (*common)->TCPWin = inSettings->mTCPWin; (*common)->FQPacingRate = inSettings->mFQPacingRate; +#if (HAVE_DECL_SO_MAX_PACING_RATE) + (*common)->FQPacingRateStep = inSettings->mFQPacingRateStep; +#endif (*common)->Port = inSettings->mPort; (*common)->PortLast = inSettings->mPortLast; (*common)->BindPort = inSettings->mBindPort; @@ -116,7 +121,9 @@ static void common_copy (struct ReportCommon **common, struct thread_Settings *i (*common)->AppRateUnits = inSettings->mAppRateUnits; (*common)->socket = inSettings->mSock; (*common)->transferID = inSettings->mTransferID; + (*common)->peertransferID = inSettings->mPeerTransferID; (*common)->threads = inSettings->mThreads; + (*common)->working_load_threads = inSettings->mWorkingLoadThreads; (*common)->winsize_requested = inSettings->mTCPWin; #if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET) (*common)->socketdrop = inSettings->mSockDrop; @@ -126,18 +133,22 @@ static void common_copy (struct ReportCommon **common, struct thread_Settings *i (*common)->jitter_binwidth = inSettings->jitter_binwidth; (*common)->local = inSettings->local; (*common)->size_local = inSettings->size_local; + (*common)->multicast_group = inSettings->multicast_group; + (*common)->size_multicast_group = inSettings->size_multicast_group; (*common)->HistBins =inSettings->mHistBins; (*common)->HistBinsize =inSettings->mHistBinsize; (*common)->HistUnits =inSettings->mHistUnits; (*common)->pktIPG =inSettings->mBurstIPG; (*common)->rtt_weight = inSettings->rtt_nearcongest_weight_factor; (*common)->ListenerTimeout =inSettings->mListenerTimeout; - (*common)->FPS = inSettings->mFPS; + (*common)->FPS = (inSettings->mFPS > 0) ? inSettings->mFPS : 0; (*common)->TOS = inSettings->mTOS; (*common)->RTOS = inSettings->mRTOS; (*common)->bbsize = inSettings->mBounceBackBytes; + (*common)->bbreplysize = inSettings->mBounceBackReplyBytes; (*common)->bbhold = inSettings->mBounceBackHold; (*common)->bbcount = inSettings->mBounceBackBurst; + (*common)->Omit = inSettings->mOmit; #if HAVE_DECL_TCP_WINDOW_CLAMP (*common)->ClampSize = inSettings->mClampSize; #endif @@ -169,6 +180,8 @@ static void free_common_copy (struct ReportCommon *common) { free(common->SSMMulticastStr); if (common->Congestion) free(common->Congestion); + if (common->LoadCCA) + free(common->LoadCCA); if (common->transferIDStr) free(common->transferIDStr); if (common->PermitKey) @@ -180,7 +193,7 @@ static void free_common_copy (struct ReportCommon *common) { // on the setting object. If the current id is zero // this will get the next one. Otherwise it will use // the value. -void setTransferID (struct thread_Settings *inSettings, int role_reversal) { +void setTransferID (struct thread_Settings *inSettings, enum TansferIDType traffic_direction) { if (!inSettings->mTransferIDStr) { if (!inSettings->mTransferID) { Mutex_Lock(&transferid_mutex); @@ -188,7 +201,7 @@ void setTransferID (struct thread_Settings *inSettings, int role_reversal) { Mutex_Unlock(&transferid_mutex); } int len = 0; - if (role_reversal) { + if (traffic_direction == REVERSED) { #ifdef HAVE_ROLE_REVERSAL_ID if (isPermitKey(inSettings) && (inSettings->mPermitKey[0] != '\0')) { len = snprintf(NULL, 0, "[%s(*%d)] ", \ @@ -220,6 +233,17 @@ void setTransferID (struct thread_Settings *inSettings, int role_reversal) { } } +void updateTransferIDPeer (struct thread_Settings *inSettings) { + if (inSettings->mPeerTransferID && (inSettings->mPeerTransferID != inSettings->mTransferID)) { + if (inSettings->mTransferIDStr) + FREE_ARRAY(inSettings->mTransferIDStr); + int len = snprintf(NULL, 0, "[%3d] ", inSettings->mPeerTransferID); + inSettings->mTransferIDStr = (char *) calloc(len+1, sizeof(char)); + if (inSettings->mTransferIDStr) { + len = sprintf(inSettings->mTransferIDStr, "[%3d] ", inSettings->mPeerTransferID); + } + } +} void SetFullDuplexHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport) { if (isUDP(inSettings)) { sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_udp; @@ -252,7 +276,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumre } else if (isFullDuplex(inSettings)) { sumreport->info.output_handler = udp_output_fullduplex_sum; } else { - sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_read_enhanced : udp_output_sum_read); + sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sumcnt_read_enhanced : udp_output_sum_read); } } } else { @@ -270,7 +294,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumre } else if (isFullDuplex(inSettings)) { sumreport->info.output_handler = tcp_output_sum_read; } else { - sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_read_enhanced : tcp_output_sum_read); + sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sumcnt_read_enhanced : tcp_output_sum_read); } } } @@ -290,7 +314,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumre } else if (isFullDuplex(inSettings)) { sumreport->info.output_handler = udp_output_fullduplex_sum; } else { - sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_write_enhanced : udp_output_sum_write); + sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sumcnt_write_enhanced : udp_output_sum_write); } } } else { @@ -306,7 +330,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumre } else if (isFullDuplex(inSettings)) { sumreport->info.output_handler = tcp_output_fullduplex_sum; } else { - sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_write_enhanced : tcp_output_sum_write); + sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sumcnt_write_enhanced : tcp_output_sum_write); } } } @@ -316,7 +340,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumre } } -struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex_report) { +struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, bool fullduplex_report) { struct SumReport *sumreport = (struct SumReport *) calloc(1, sizeof(struct SumReport)); if (sumreport == NULL) { FAIL(1, "Out of Memory!!\n", inSettings); @@ -324,11 +348,17 @@ struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, in sumreport->reference.count = 0; sumreport->reference.maxcount = 0; Mutex_Initialize(&sumreport->reference.lock); - sumreport->threads = 0; common_copy(&sumreport->info.common, inSettings); // sumreport->info.common->transferID = inID; // this is now set in the active code - sumreport->info.threadcnt = 0; sumreport->info.isMaskOutput = false; + sumreport->info.sumreport = sumreport; + sumreport->info.type = SUM_REPORT; + sumreport->info.uplevel = LOW; + sumreport->info.downlevel = LOW; + sumreport->info.slot_thread_upcount = 0; + sumreport->info.slot_thread_downcount = 0; + sumreport->final_thread_upcount = 0; + if (inSettings->mReportMode == kReport_CSV) { format_ips_port_string(&sumreport->info, 1); } @@ -348,18 +378,18 @@ struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, in char name[] = "SUMT8"; sumreport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\ pow(10,inSettings->mHistUnits), \ - inSettings->mHistci_lower, inSettings->mHistci_upper, sumreport->info.common->transferID, name); + inSettings->mHistci_lower, inSettings->mHistci_upper, sumreport->info.common->transferID, name, false); } else { char name[] = "SUMF8"; sumreport->info.framelatency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ pow(10,inSettings->mHistUnits), inSettings->mHistci_lower, \ - inSettings->mHistci_upper, sumreport->info.common->transferID, name); + inSettings->mHistci_upper, sumreport->info.common->transferID, name, false); } } if (isJitterHistogram(inSettings) && isUDP(inSettings)) { char name[] = "SUMJ8"; sumreport->info.jitter_histogram = histogram_init(JITTER_BINCNT,inSettings->jitter_binwidth,0,JITTER_UNITS, \ - JITTER_LCI, JITTER_UCI, sumreport->info.common->transferID, name); + JITTER_LCI, JITTER_UCI, sumreport->info.common->transferID, name, false); } } if (fullduplex_report) { @@ -417,6 +447,12 @@ void FreeSumReport (struct SumReport *sumreport) { if (sumreport->info.bbrtt_histogram) { histogram_delete(sumreport->info.bbrtt_histogram); } + if (sumreport->info.bbowdto_histogram) { + histogram_delete(sumreport->info.bbowdto_histogram); + } + if (sumreport->info.bbowdfro_histogram) { + histogram_delete(sumreport->info.bbowdfro_histogram); + } if (sumreport->info.jitter_histogram) { histogram_delete(sumreport->info.jitter_histogram); } @@ -453,6 +489,12 @@ static void Free_iReport (struct ReporterData *ireport) { if (ireport->info.bbrtt_histogram) { histogram_delete(ireport->info.bbrtt_histogram); } + if (ireport->info.bbowdto_histogram) { + histogram_delete(ireport->info.bbowdto_histogram); + } + if (ireport->info.bbowdfro_histogram) { + histogram_delete(ireport->info.bbowdfro_histogram); + } free_common_copy(ireport->info.common); free(ireport); } @@ -568,13 +610,18 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { } // Copy common settings into the transfer report section common_copy(&ireport->info.common, inSettings); + ireport->info.sumreport = inSettings->mSumReport; ireport->info.final = false; ireport->info.burstid_transition = false; ireport->info.isEnableTcpInfo = false; + ireport->info.type = DATA_REPORT; // Create a new packet ring which is used to communicate // packet stats from the traffic thread to the reporter // thread. The reporter thread does all packet accounting + // ring events causes the packet ring to return a NULL on + // dequeue across a boundary, e.g. an interval report timestamp. + // This is needed so summing works properly ireport->packetring = packetring_init((inSettings->numreportstructs ? inSettings->numreportstructs : (isSingleUDP(inSettings) ? 40 : NUM_REPORT_STRUCTS)), \ &ReportCond, (isSingleUDP(inSettings) ? NULL : &inSettings->awake_me)); #ifdef HAVE_THREAD_DEBUG @@ -693,21 +740,28 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { ireport->transfer_protocol_handler = reporter_transfer_protocol_client_tcp; if (isSumOnly(inSettings)) { ireport->info.output_handler = NULL; + } else if (isBounceBack(inSettings)) { + ireport->packet_handler_post_report = reporter_handle_packet_bb_client; + ireport->transfer_protocol_handler = reporter_transfer_protocol_client_bb_tcp; + if (inSettings->mReportMode == kReport_CSV) + ireport->info.output_handler = tcp_output_write_bb_csv; + else + ireport->info.output_handler = tcp_output_write_bb; } else if ((inSettings->mReportMode == kReport_CSV) && !isSumOnly(inSettings)) { if (isEnhanced(inSettings)) ireport->info.output_handler = tcp_output_write_enhanced_csv; else ireport->info.output_handler = tcp_output_basic_csv; - } else if (isBounceBack(inSettings)) { - ireport->packet_handler_post_report = reporter_handle_packet_bb_client; - ireport->transfer_protocol_handler = reporter_transfer_protocol_client_bb_tcp; - ireport->info.output_handler = tcp_output_write_bb; } else if (isIsochronous(inSettings)) { ireport->info.output_handler = tcp_output_write_enhanced_isoch; } else if (isTcpWriteTimes(inSettings)) { ireport->info.output_handler = tcp_output_write_enhanced_write; } else if (isEnhanced(inSettings)) { - ireport->info.output_handler = tcp_output_write_enhanced; + if (isFQPacing(inSettings)) + ireport->info.output_handler = tcp_output_write_enhanced_fq; + else { + ireport->info.output_handler = tcp_output_write_enhanced; + } } else if (isFullDuplex(inSettings)) { ireport->info.output_handler = tcp_output_write; } else { @@ -729,13 +783,13 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { if (isJitterHistogram(inSettings)) { char name[] = "J8"; ireport->info.jitter_histogram = histogram_init(JITTER_BINCNT,inSettings->jitter_binwidth,0,JITTER_UNITS, \ - JITTER_LCI, JITTER_UCI, ireport->info.common->transferID, name); + JITTER_LCI, JITTER_UCI, ireport->info.common->transferID, name, false); } if (isTripTime(inSettings) && isHistogram(inSettings)) { char name[] = "T8"; ireport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\ pow(10,inSettings->mHistUnits), \ - inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name, false); } } if (isHistogram(inSettings) && (isIsochronous(inSettings) || (!isUDP(inSettings) && isTripTime(inSettings)))) { @@ -743,7 +797,7 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { // make sure frame bin size min is 100 microsecond ireport->info.framelatency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ pow(10,inSettings->mHistUnits), inSettings->mHistci_lower, \ - inSettings->mHistci_upper, ireport->info.common->transferID, name); + inSettings->mHistci_upper, ireport->info.common->transferID, name, false); } } if ((inSettings->mThreadMode == kMode_Client) && !isUDP(inSettings) && isHistogram(inSettings)) { @@ -751,16 +805,16 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { char name[] = "W8"; ireport->info.write_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\ pow(10,inSettings->mHistUnits), \ - inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name, false); } else if (isWritePrefetch(inSettings)) { char name[] = "S8"; ireport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\ pow(10,inSettings->mHistUnits), \ - inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name, false); } } if ((inSettings->mThreadMode == kMode_Client) && isBounceBack(inSettings)) { - char name[] = "BB8"; + char name[] = " BB8"; if (!isHistogram(inSettings)) { inSettings->mHistBins = 100000; // 10 seconds wide inSettings->mHistBinsize = 100; // 100 usec bins @@ -769,8 +823,16 @@ struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) { inSettings->mHistci_upper = 95; } ireport->info.bbrtt_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ - pow(10,inSettings->mHistUnits), \ - inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); + pow(10,inSettings->mHistUnits), \ + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name, false); + if (isTripTime(inSettings)) { + ireport->info.bbowdto_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ + pow(10,inSettings->mHistUnits), \ + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, " OWD-TX", false); + ireport->info.bbowdfro_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ + pow(10,inSettings->mHistUnits), \ + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, " OWD-RX", false); + } } return reporthdr; } @@ -838,8 +900,14 @@ struct ReportHeader* InitConnectionReport (struct thread_Settings *inSettings) { creport->common->winsize_requested = inSettings->mTCPWin; creport->txholdbacktime = inSettings->txholdback_timer; if (isPeriodicBurst(inSettings)) { - creport->common->FPS = inSettings->mFPS; + creport->common->FPS = (inSettings->mFPS > 0) ? inSettings->mFPS : 0; + } + if (isLoadCCA(inSettings) && (isWorkingLoadUp(inSettings) || isWorkingLoadDown(inSettings))) { + strncpy(creport->connected_cca, inSettings->mLoadCCA, TCP_CCA_NAME_MAX); + } else if (isCongestionControl(inSettings)) { + strncpy(creport->connected_cca, inSettings->mCongestion, TCP_CCA_NAME_MAX); } + creport->connected_cca[TCP_CCA_NAME_MAX - 1] = '\0'; #ifdef HAVE_THREAD_DEBUG char rs[REPORTTXTMAX]; reporttype_text(reporthdr, &rs[0]); @@ -972,6 +1040,19 @@ struct ReportHeader* InitServerRelayUDPReport(struct thread_Settings *inSettings return reporthdr; } +struct ReportHeader* InitStringReport (char *textoutput) { + struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader)); + if (reporthdr == NULL) { + WARN_errno(1, "Out of Memory!!\n"); + } + reporthdr->type = STRING_REPORT; + + reporthdr->this_report = (void *) calloc((strlen(textoutput) + 1), sizeof(char)); + char *dst = (char *)(reporthdr->this_report); + strcpy(dst, textoutput); + return reporthdr; +} + /* ------------------------------------------------------------------- * Send an AckFIN (a datagram acknowledging a FIN) on the socket, * then select on the socket for some time to check for silence. @@ -985,7 +1066,7 @@ void write_UDP_AckFIN (struct TransferInfo *stats, int len) { int ackpacket_length = (int) (sizeof(struct UDP_datagram) + sizeof(struct server_hdr)); int readlen = ((ackpacket_length * 2) > len * 2) ? (ackpacket_length * 2) : (len * 2); char *ackPacket = (char *) calloc(1, readlen); - int success = 0; + bool success = false; assert(ackPacket); fd_set readSet; int rc = 1; @@ -1068,7 +1149,7 @@ void write_UDP_AckFIN (struct TransferInfo *stats, int len) { #ifdef HAVE_THREAD_DEBUG thread_debug("UDP server detected silence - server stats assumed received by client"); #endif - success = 1; + success = true; break; } rc = read(stats->common->socket, ackPacket, readlen); diff --git a/src/Server.cpp b/src/Server.cpp index c271cd7..76eba40 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -70,7 +70,6 @@ #include "checksums.h" #endif - /* ------------------------------------------------------------------- * Stores connected socket and socket info. * ------------------------------------------------------------------- */ @@ -94,6 +93,9 @@ Server::Server (thread_Settings *inSettings) { } } #endif +#if HAVE_DECL_MSG_CTRUNC + ctrunc_warn_enable = true; +#endif // Enable kernel level timestamping if available InitKernelTimeStamping(); int sorcvtimer = 0; @@ -101,15 +103,24 @@ Server::Server (thread_Settings *inSettings) { // minterval double, units seconds // mAmount integer, units 10 milliseconds // divide by two so timeout is 1/2 the interval - if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { + if ((mSettings->mInterval > 0) && (mSettings->mIntervalMode == kInterval_Time)) { sorcvtimer = static_cast<int>(round(mSettings->mInterval / 2.0)); } else if (isServerModeTime(mSettings)) { sorcvtimer = static_cast<int>(round(mSettings->mAmount * 10000) / 2); } - isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings) && !isUDP(mSettings))); + isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings)&& !isUDP(mSettings))); if (isburst && (mSettings->mFPS > 0.0)) { sorcvtimer = static_cast<int>(round(2000000.0 / mSettings->mFPS)); } + if ((mSettings->mInterval > 0) && (mSettings->mIntervalMode == kInterval_Time)) { + int interval_quarter = static_cast<int>(round(mSettings->mAmount * 10000) / 4); + if (sorcvtimer > interval_quarter) { + sorcvtimer = interval_quarter; + } + if (sorcvtimer < 1000) { + sorcvtimer = 1000; // lower bound of 1 ms + } + } if (sorcvtimer > 0) { SetSocketOptionsReceiveTimeout(mSettings, sorcvtimer); } @@ -162,7 +173,7 @@ void Server::RunTCP () { reportstruct->packetTime.tv_usec = now.getUsecs(); while (InProgress()) { // printf("***** bid expect = %u\n", burstid_expect); - reportstruct->emptyreport=1; + reportstruct->emptyreport = true; currLen = 0; // perform read if (isBWSet(mSettings)) { @@ -170,13 +181,13 @@ void Server::RunTCP () { tokens += time2.subSec(time1) * (mSettings->mAppRate / 8.0); time1 = time2; } - reportstruct->transit_ready = 0; + reportstruct->transit_ready = false; if (tokens >= 0.0) { int n = 0; int readLen = mSettings->mBufLen; if (burst_nleft > 0) readLen = (mSettings->mBufLen < burst_nleft) ? mSettings->mBufLen : burst_nleft; - reportstruct->emptyreport=1; + reportstruct->emptyreport = true; #if HAVE_DECL_TCP_QUICKACK if (isTcpQuickAck(mSettings)) { int opt = 1; @@ -215,7 +226,7 @@ void Server::RunTCP () { burst_nleft = burst_info.burst_size - n; if (burst_nleft == 0) { reportstruct->prevSentTime = myReport->info.ts.prevsendTime; - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; reportstruct->burstperiod = burst_info.burst_period_us; } currLen += n; @@ -237,7 +248,7 @@ void Server::RunTCP () { if (!reportstruct->transit_ready) { n = recv(mSettings->mSock, mSettings->mBuf, readLen, 0); if (n > 0) { - reportstruct->emptyreport = 0; + reportstruct->emptyreport = false; if (isburst) { burst_nleft -= n; if (burst_nleft == 0) { @@ -247,7 +258,7 @@ void Server::RunTCP () { reportstruct->isochStartTime.tv_usec = burst_info.send_tt.write_tv_usec; reportstruct->burstperiod = burst_info.burst_period_us; } - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; } } } else if (n == 0) { @@ -256,9 +267,14 @@ void Server::RunTCP () { thread_debug("Server thread detected EOF on socket %d", mSettings->mSock); #endif } else if ((n < 0) && (FATALTCPREADERR(errno))) { - WARN_errno(1, "recv"); peerclose = true; n = 0; + now.setnow(); + char warnbuf[WARNBUFSIZE]; + snprintf(warnbuf, sizeof(warnbuf), "%stcp recv (%ld.%ld)",\ + mSettings->mTransferIDStr, now.getSecs(), now.getUsecs()); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN_errno(1, warnbuf); } currLen += n; } @@ -305,44 +321,103 @@ void Server::PostNullEvent () { // push a nonevent into the packet ring // this will cause the reporter to process // up to this event - memset(reportstruct, 0, sizeof(struct ReportStruct)); + struct ReportStruct report_nopacket; + memset(&report_nopacket, 0, sizeof(struct ReportStruct)); now.setnow(); - reportstruct->packetTime.tv_sec = now.getSecs(); - reportstruct->packetTime.tv_usec = now.getUsecs(); - reportstruct->emptyreport=1; - ReportPacket(myReport, reportstruct); + report_nopacket.packetTime.tv_sec = now.getSecs(); + report_nopacket.packetTime.tv_usec = now.getUsecs(); + report_nopacket.emptyreport = true; + report_nopacket.err_readwrite = WriteNoAccount; + reportstruct->packetTime = report_nopacket.packetTime; // needed for the InProgress loop test + ReportPacket(myReport, &report_nopacket); } inline bool Server::ReadBBWithRXTimestamp () { bool rc = false; int n; - while (1) { - if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) { - struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); - uint16_t bbflags = ntohs(bbhdr->bbflags); - if (!(bbflags & HEADER_BBSTOP)) { + while (InProgress()) { + int read_offset = 0; + RETRY_READ : + n = recvn(mySocket, (mSettings->mBuf + read_offset), (mSettings->mBounceBackBytes - read_offset), 0); + if (n > 0) { + read_offset += n; + if (read_offset == mSettings->mBounceBackBytes) { + struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); + uint16_t bbflags = ntohs(bbhdr->bbflags); now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); - reportstruct->emptyreport=0; + reportstruct->emptyreport = false; reportstruct->packetLen = mSettings->mBounceBackBytes; // write the rx timestamp back into the payload bbhdr->bbserverRx_ts.sec = htonl(reportstruct->packetTime.tv_sec); bbhdr->bbserverRx_ts.usec = htonl(reportstruct->packetTime.tv_usec); ReportPacket(myReport, reportstruct); - rc = true; + if (!(bbflags & HEADER_BBSTOP)) { + rc = true; + } else { + // last BB write received from client, false return code stops this side + } + break; + } + } else if (n == 0) { + peerclose = true; + } else if (n == IPERF_SOCKET_ERROR_NONFATAL) { + PostNullEvent(); + if (InProgress()) + goto RETRY_READ; + } else { + if (FATALTCPREADERR(errno)) { + WARN_errno(1, "fatal bounceback read"); + peerclose = true; + break; } else { + WARN(1, "timeout: bounceback read"); + PostNullEvent(); + if (InProgress()) + goto RETRY_READ; + } + } + } + return rc; +} + +inline bool Server::WriteBB () { + int n; + bool rc = false; + struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); + now.setnow(); + bbhdr->bbserverTx_ts.sec = htonl(now.getSecs()); + bbhdr->bbserverTx_ts.usec = htonl(now.getUsecs()); + if (mSettings->mTOS) { + bbhdr->tos = htons((uint16_t)(mSettings->mTOS & 0xFF)); + } + int write_offset = 0; + reportstruct->writecnt = 0; + int writelen = mSettings->mBounceBackReplyBytes; + while (InProgress()) { + n = writen(mySocket, (mSettings->mBuf + write_offset), (writelen - write_offset), &reportstruct->writecnt); + if (n < 0) { + if (FATALTCPWRITERR(errno)) { + reportstruct->err_readwrite=WriteErrFatal; + FAIL_errno(1, "tcp bounceback writen", mSettings); peerclose = true; + break; + } else { + PostNullEvent(); + continue; } - break; - } else if (n==0) { - peerclose = true; - break; - } else if (n == -2){ + } + write_offset += n; + if (write_offset < writelen) { + WARN_errno(1, "tcp bounceback writen incomplete"); PostNullEvent(); - } else { - break; + continue; } + reportstruct->emptyreport = false; + reportstruct->err_readwrite=WriteSuccess; + reportstruct->packetLen = writelen; + return true; } return rc; } @@ -374,40 +449,21 @@ void Server::RunBounceBackTCP () { reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->packetLen = mSettings->mBounceBackBytes; - reportstruct->emptyreport=0; + reportstruct->emptyreport = false; ReportPacket(myReport, reportstruct); - while (InProgress()) { - int n; - reportstruct->emptyreport=1; - do { - struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf); - if (mSettings->mBounceBackHold) { -#if HAVE_DECL_TCP_QUICKACK - if (isTcpQuickAck(mSettings)) { - int opt = 1; - Socklen_t len = sizeof(opt); - int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK, - reinterpret_cast<char*>(&opt), len); - WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); - } -#endif - delay_loop(mSettings->mBounceBackHold); - } - now.setnow(); - bbhdr->bbserverTx_ts.sec = htonl(now.getSecs()); - bbhdr->bbserverTx_ts.usec = htonl(now.getUsecs()); - if (mSettings->mTOS) { - bbhdr->tos = htons((uint16_t)(mSettings->mTOS & 0xFF)); - } - if ((n = writen(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, &reportstruct->writecnt)) == mSettings->mBounceBackBytes) { - reportstruct->emptyreport=0; - reportstruct->packetLen = n; - ReportPacket(myReport, reportstruct); - } else { + int rc; + while (InProgress() && (rc = WriteBB())) { + if (rc) { + ReportPacket(myReport, reportstruct); + if (ReadBBWithRXTimestamp()) + continue; + else { break; } - } while (ReadBBWithRXTimestamp()); + } else { + break; + } } disarm_itimer(); // stop timing @@ -471,10 +527,17 @@ inline void Server::SetReportStartTime () { // Servers that aren't full duplex use the accept timestamp for start myReport->info.ts.startTime.tv_sec = mSettings->sent_time.tv_sec; myReport->info.ts.startTime.tv_usec = mSettings->sent_time.tv_usec; - } else if (!TimeZero(mSettings->accept_time) && !isTxStartTime(mSettings)) { + } else if (!TimeZero(mSettings->accept_time)) { // Servers that aren't full duplex use the accept timestamp for start myReport->info.ts.startTime.tv_sec = mSettings->accept_time.tv_sec; myReport->info.ts.startTime.tv_usec = mSettings->accept_time.tv_usec; + // The client may have had a barrier between the connect and start of traffic, check and adjust + if (mSettings->barrier_time) { + now.setnow(); + if (now.subUsec(mSettings->accept_time) >= mSettings->barrier_time) { + TimeAddIntUsec(myReport->info.ts.startTime, mSettings->barrier_time); + } + } } else { now.setnow(); myReport->info.ts.startTime.tv_sec = now.getSecs(); @@ -579,6 +642,20 @@ void Server::ClientReverseFirstRead (void) { bool Server::InitTrafficLoop (void) { bool UDPReady = true; + if (isSyncTransferID(mSettings)) { + if (mSettings->mPeerTransferID != mSettings->mTransferID) { + int len = snprintf(NULL, 0, "%sTransfer ID %d remapped to %d\n", \ + mSettings->mTransferIDStr, mSettings->mTransferID, mSettings->mPeerTransferID); + char *text = (char *) calloc(len+1, sizeof(char)); + if (text) { + snprintf(text, len, "%sTransfer ID %d remapped to %d\n", \ + mSettings->mTransferIDStr, mSettings->mTransferID, mSettings->mPeerTransferID); + PostReport(InitStringReport(text)); + FREE_ARRAY(text); + } + updateTransferIDPeer(mSettings); + } + } myJob = InitIndividualReport(mSettings); myReport = static_cast<struct ReporterData *>(myJob->this_report); assert(myJob != NULL); @@ -598,21 +675,42 @@ bool Server::InitTrafficLoop (void) { reportstruct->l2errors = 0x0; int setfullduplexflag = 0; + Timestamp now; + + if ((mSettings->txstart_epoch.tv_sec > 0) && (mSettings->txstart_epoch.tv_sec - now.getSecs()) > 1) { + // Have the server thread wait on the client's epoch start + // unblocking one second ahead + struct timeval wait_until = mSettings->txstart_epoch; + wait_until.tv_sec -= 1; + clock_usleep_abstime(&wait_until); + } if (isFullDuplex(mSettings) && !isServerReverse(mSettings)) { assert(mSettings->mFullDuplexReport != NULL); if ((setfullduplexflag = fullduplex_start_barrier(&mSettings->mFullDuplexReport->fullduplex_barrier)) < 0) exit(-1); } - Timestamp now; if (isReverse(mSettings)) { mSettings->accept_time.tv_sec = now.getSecs(); mSettings->accept_time.tv_usec = now.getUsecs(); ClientReverseFirstRead(); } if (isTripTime(mSettings)) { - if ((abs(now.getSecs() - mSettings->sent_time.tv_sec)) > MAXDIFFTIMESTAMPSECS) { + int diff_tolerance; + if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { + diff_tolerance = ceil(mSettings->mInterval / 1000000); + } else { + diff_tolerance = MAXDIFFTIMESTAMPSECS; + } + if (diff_tolerance < 2) { + diff_tolerance = 2; // min is 2 seconds + } + if (mSettings->txstart_epoch.tv_sec > 0) { + mSettings->accept_time.tv_sec = mSettings->txstart_epoch.tv_sec; + mSettings->accept_time.tv_usec = mSettings->txstart_epoch.tv_usec; + mSettings->sent_time = mSettings->accept_time; // the first sent time w/epoch starts uses now() + } else if ((abs(now.getSecs() - mSettings->sent_time.tv_sec)) > diff_tolerance) { unsetTripTime(mSettings); - fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", diff_tolerance); mSettings->accept_time.tv_sec = now.getSecs(); mSettings->accept_time.tv_usec = now.getUsecs(); } @@ -643,8 +741,8 @@ bool Server::InitTrafficLoop (void) { reportstruct->packetLen = mSettings->firstreadbytes; if (isUDP(mSettings)) { int offset = 0; - UDPReady = !ReadPacketID(offset); reportstruct->packetTime = mSettings->accept_time; + UDPReady = !ReadPacketID(offset); } else { reportstruct->sentTime.tv_sec = myReport->info.ts.startTime.tv_sec; reportstruct->sentTime.tv_usec = myReport->info.ts.startTime.tv_usec; @@ -656,21 +754,36 @@ bool Server::InitTrafficLoop (void) { } inline int Server::ReadWithRxTimestamp () { - long currLen; - int tsdone = 0; + int currLen; + int tsdone = false; -#if HAVE_DECL_SO_TIMESTAMP + reportstruct->err_readwrite = ReadSuccess; + +#if (HAVE_DECL_SO_TIMESTAMP) && (HAVE_DECL_MSG_CTRUNC) cmsg = reinterpret_cast<struct cmsghdr *>(&ctrl); currLen = recvmsg(mSettings->mSock, &message, mSettings->recvflags); if (currLen > 0) { - for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; - cmsg = CMSG_NXTHDR(&message, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) { - memcpy(&(reportstruct->packetTime), CMSG_DATA(cmsg), sizeof(struct timeval)); - tsdone = 1; +#if HAVE_DECL_MSG_TRUNC + if (message.msg_flags & MSG_TRUNC) { + reportstruct->err_readwrite = ReadErrLen; + } +#endif + if (!(message.msg_flags & MSG_CTRUNC)) { + for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; + cmsg = CMSG_NXTHDR(&message, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) { + memcpy(&(reportstruct->packetTime), CMSG_DATA(cmsg), sizeof(struct timeval)); + if (TimeZero(myReport->info.ts.prevpacketTime)) { + myReport->info.ts.prevpacketTime = reportstruct->packetTime; + } + tsdone = true; + } } + } else if (ctrunc_warn_enable && mSettings->mTransferIDStr) { + fprintf(stderr, "%sWARN: recvmsg MSG_CTRUNC occured\n", mSettings->mTransferIDStr); + ctrunc_warn_enable = false; } } #else @@ -678,13 +791,15 @@ inline int Server::ReadWithRxTimestamp () { #endif if (currLen <=0) { // Socket read timeout or read error - reportstruct->emptyreport=1; + reportstruct->emptyreport = true; if (currLen == 0) { peerclose = true; } else if (FATALUDPREADERR(errno)) { WARN_errno(1, "recvmsg"); currLen = 0; peerclose = true; + } else { + reportstruct->err_readwrite = ReadTimeo; } } else if (TimeZero(myReport->info.ts.prevpacketTime)) { myReport->info.ts.prevpacketTime = reportstruct->packetTime; @@ -704,29 +819,45 @@ inline bool Server::ReadPacketID (int offset_adjust) { // terminate when datagram begins with negative index // the datagram ID should be correct, just negated + // read the sent timestamp from the rx packet + reportstruct->sentTime.tv_sec = ntohl(mBuf_UDP->tv_sec); + reportstruct->sentTime.tv_usec = ntohl(mBuf_UDP->tv_usec); if (isSeqNo64b(mSettings)) { - // New client - Signed PacketID packed into unsigned id2,id - reportstruct->packetID = (static_cast<uint32_t>(ntohl(mBuf_UDP->id))) | (static_cast<uintmax_t>(ntohl(mBuf_UDP->id2)) << 32); + // New client - Signed PacketID packed into unsigned id2,id + reportstruct->packetID = (static_cast<uint32_t>(ntohl(mBuf_UDP->id))) | (static_cast<uintmax_t>(ntohl(mBuf_UDP->id2)) << 32); #ifdef HAVE_PACKET_DEBUG - printf("id 0x%x, 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n", - ntohl(mBuf_UDP->id), ntohl(mBuf_UDP->id2), reportstruct->packetID, reportstruct->packetID); + if (isTripTime(mSettings)) { + int len = snprintf(NULL,0,"%sPacket id 0x%x, 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ") Sent: %ld.%ld6 Received: %ld.%ld6 Delay: %f\n", \ + mSettings->mTransferIDStr,ntohl(mBuf_UDP->id), ntohl(mBuf_UDP->id2), reportstruct->packetID, reportstruct->packetID, \ + reportstruct->sentTime.tv_sec, reportstruct->sentTime.tv_usec, \ + reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec, TimeDifference(reportstruct->packetTime, reportstruct->sentTime)); + char *text = (char *) calloc(len+1, sizeof(char)); + if (text) { + snprintf(text, len,"%sPacket ID id 0x%x, 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ") Sent: %ld.%ld Received: %ld.%ld Delay: %f\n", \ + mSettings->mTransferIDStr,ntohl(mBuf_UDP->id), ntohl(mBuf_UDP->id2), reportstruct->packetID, reportstruct->packetID, \ + reportstruct->sentTime.tv_sec, reportstruct->sentTime.tv_usec, \ + reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec, TimeDifference(reportstruct->packetTime, reportstruct->sentTime)); + PostReport(InitStringReport(text)); + FREE_ARRAY(text); + } + } else { + printf("id 0x%x, 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n", + ntohl(mBuf_UDP->id), ntohl(mBuf_UDP->id2), reportstruct->packetID, reportstruct->packetID); + } #endif } else { - // Old client - Signed PacketID in Signed id - reportstruct->packetID = static_cast<int32_t>(ntohl(mBuf_UDP->id)); + // Old client - Signed PacketID in Signed id + reportstruct->packetID = static_cast<int32_t>(ntohl(mBuf_UDP->id)); #ifdef HAVE_PACKET_DEBUG - printf("id 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n", - ntohl(mBuf_UDP->id), reportstruct->packetID, reportstruct->packetID); + printf("id 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n", + ntohl(mBuf_UDP->id), reportstruct->packetID, reportstruct->packetID); #endif } if (reportstruct->packetID < 0) { - reportstruct->packetID = - reportstruct->packetID; - terminate = true; + reportstruct->packetID = -reportstruct->packetID; + terminate = true; } - // read the sent timestamp from the rx packet - reportstruct->sentTime.tv_sec = ntohl(mBuf_UDP->tv_sec); - reportstruct->sentTime.tv_usec = ntohl(mBuf_UDP->tv_usec); return terminate; } @@ -749,7 +880,7 @@ void Server::L2_processing () { if (L2_quintuple_filter() != 0) { reportstruct->l2errors |= L2UNKNOWN; reportstruct->l2errors |= L2CSUMERR; - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; } } if (!(reportstruct->l2errors & L2UNKNOWN)) { @@ -759,7 +890,7 @@ void Server::L2_processing () { if (rc) { reportstruct->l2errors |= L2CSUMERR; if ((!(reportstruct->l2errors & L2LENERR)) && (L2_quintuple_filter() != 0)) { - reportstruct->emptyreport = 1; + reportstruct->emptyreport = true; reportstruct->l2errors |= L2UNKNOWN; } } @@ -848,7 +979,7 @@ int Server::L2_quintuple_filter () { } inline void Server::udp_isoch_processing (int rxlen) { - reportstruct->transit_ready = 0; + reportstruct->transit_ready = false; // Ignore runt sized isoch packets if (rxlen < static_cast<int>(sizeof(struct UDP_datagram) + sizeof(struct client_hdr_v1) + sizeof(struct client_hdrext) + sizeof(struct isoch_payload))) { reportstruct->burstsize = 0; @@ -865,7 +996,7 @@ inline void Server::udp_isoch_processing (int rxlen) { reportstruct->burstperiod = ntohl(udp_pkt->isoch.burstperiod); reportstruct->remaining = ntohl(udp_pkt->isoch.remaining); if ((reportstruct->remaining == (uint32_t) rxlen) && ((reportstruct->frameID - reportstruct->prevframeID) == 1)) { - reportstruct->transit_ready = 1; + reportstruct->transit_ready = true; } } } @@ -891,13 +1022,13 @@ void Server::RunUDP () { // bandwidth accounting, basically it's indicating // that the reportstruct itself couldn't be // completely filled out. - reportstruct->emptyreport=1; + reportstruct->emptyreport = true; reportstruct->packetLen=0; // read the next packet with timestamp // will also set empty report or not rxlen=ReadWithRxTimestamp(); if (!peerclose && (rxlen > 0)) { - reportstruct->emptyreport = 0; + reportstruct->emptyreport = false; reportstruct->packetLen = rxlen; if (isL2LengthCheck(mSettings)) { reportstruct->l2len = rxlen; @@ -924,7 +1055,7 @@ void Server::RunUDP () { } } disarm_itimer(); - int do_close = EndJob(myJob, reportstruct); + bool do_close = EndJob(myJob, reportstruct); if (!isMulticast(mSettings) && !isNoUDPfin(mSettings)) { // send a UDP acknowledgement back except when: // 1) we're NOT receiving multicast diff --git a/src/Settings.cpp b/src/Settings.cpp index 39229f5..246eddd 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -90,10 +90,13 @@ static int txstarttime = 0; static int noconnectsync = 0; static int txholdback = 0; static int fqrate = 0; +static int fqratestep = 0; +static int fqratestepinterval = 0; static int triptime = 0; static int infinitetime = 0; static int connectonly = 0; -static int connectretry = 0; +static int connectretrytime = 0; +static int connectretryinterval = 0; static int burstipg = 0; static int burstsize = 0; static int burstperiodic = 0; @@ -113,7 +116,10 @@ static int hideips = 0; static int bounceback = 0; static int bouncebackhold = 0; static int bouncebackperiod = 0; +static int bouncebackrequest = 0; +static int bouncebackreply = 0; static int overridetos = 0; +static int dscp = 0; static int notcpbbquickack = 0; static int tcpquickack = 0; static int notcpbbquickack_cliset = 0; @@ -121,6 +127,12 @@ static int workingload = 0; static int utctimes = 0; static int bouncebackdelaystart = 0; static int tcpwritetimes = 0; +static int primarycca = 0; +static int loadcca = 0; +static int tcptxdelay = 0; +static int testxchangetimeout = 0; +static int synctransferid = 0; +static int ignoreshutdown = 0; void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings); // apply compound settings after the command line has been fully parsed @@ -173,8 +185,11 @@ const struct option long_options[] = {"bounceback-hold", required_argument, &bouncebackhold, 1}, {"bounceback-no-quickack", no_argument, ¬cpbbquickack, 1}, {"bounceback-period", required_argument, &bouncebackperiod, 1}, +{"bounceback-request", required_argument, &bouncebackrequest, 1}, +{"bounceback-reply", required_argument, &bouncebackreply, 1}, {"compatibility", no_argument, NULL, 'C'}, {"daemon", no_argument, NULL, 'D'}, +{"dscp", required_argument, &dscp, 1}, {"file_input", required_argument, NULL, 'F'}, {"ssm-host", required_argument, NULL, 'H'}, {"stdin_input", no_argument, NULL, 'I'}, @@ -187,6 +202,7 @@ const struct option long_options[] = #else {"reverse", no_argument, NULL, 'R'}, #endif +{"sync-transfer-id", no_argument, &synctransferid, 1}, {"tos", required_argument, NULL, 'S'}, {"ttl", required_argument, NULL, 'T'}, {"single_udp", no_argument, NULL, 'U'}, @@ -199,6 +215,7 @@ const struct option long_options[] = {"jitter-histograms", optional_argument, &jitter_histogram, 1}, {"udp-histograms", optional_argument, &histogram, 1}, // keep support per 2.0.13 usage {"l2checks", no_argument, &l2checks, 1}, +{"ignore-shutdown", no_argument, &ignoreshutdown, 1}, {"incr-dstip", no_argument, &incrdstip, 1}, {"incr-srcip", no_argument, &incrsrcip, 1}, {"incr-dstport", no_argument, &incrdstport, 1}, @@ -207,10 +224,13 @@ const struct option long_options[] = {"txstart-time", required_argument, &txstarttime, 1}, {"txdelay-time", required_argument, &txholdback, 1}, {"fq-rate", required_argument, &fqrate, 1}, +{"fq-rate-step", required_argument, &fqratestep, 1}, +{"fq-rate-step-interval", required_argument, &fqratestepinterval, 1}, {"trip-times", no_argument, &triptime, 1}, {"no-udp-fin", no_argument, &noudpfin, 1}, {"connect-only", optional_argument, &connectonly, 1}, -{"connect-retries", required_argument, &connectretry, 1}, +{"connect-retry-time", required_argument, &connectretrytime, 1}, +{"connect-retry-timer", required_argument, &connectretryinterval, 1}, {"no-connect-sync", no_argument, &noconnectsync, 1}, {"full-duplex", no_argument, &fullduplextest, 1}, {"ipg", required_argument, &burstipg, 1}, @@ -225,11 +245,15 @@ const struct option long_options[] = {"tos-override", required_argument, &overridetos, 1}, {"tcp-rx-window-clamp", required_argument, &rxwinclamp, 1}, {"tcp-quickack", no_argument, &tcpquickack, 1}, +{"tcp-tx-delay", required_argument, &tcptxdelay, 1}, {"tcp-write-prefetch", required_argument, &txnotsentlowwater, 1}, // see doc/DESIGN_NOTES {"tcp-write-times", no_argument, &tcpwritetimes, 1}, +{"test-exchange-timeout", required_argument, &testxchangetimeout, 1}, {"tap-dev", optional_argument, &tapif, 1}, {"tun-dev", optional_argument, &tunif, 1}, {"working-load", optional_argument, &workingload, 1}, +{"working-load-cca", required_argument, &loadcca, 1}, +{"tcp-cca", required_argument, &primarycca, 1}, {"utc", no_argument, &utctimes, 1}, {"NUM_REPORT_STRUCTS", required_argument, &numreportstructs, 1}, #ifdef WIN32 @@ -294,8 +318,7 @@ const char short_options[] = "146b:c:def:hi:l:mn:o:p:rst:uvw:x:y:zAB:CDF:H:IL:M: const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec const int kDefault_TCPBufLen = 128 * 1024; // TCP default read/write size -const int kDefault_BBTCPBufLen = 100; // default bounce-back size in bytes - +const int kDefault_BBTCPReqLen = 100; // default bounce-back size in bytes /* ------------------------------------------------------------------- * Initialize all settings to defaults. @@ -414,6 +437,10 @@ void Settings_Copy (struct thread_Settings *from, struct thread_Settings **into, (*into)->mCongestion = new char[strlen(from->mCongestion) + 1]; strcpy((*into)->mCongestion, from->mCongestion); } + if (from->mLoadCCA != NULL) { + (*into)->mLoadCCA = new char[strlen(from->mLoadCCA) + 1]; + strcpy((*into)->mLoadCCA, from->mLoadCCA); + } } else { (*into)->mHost = NULL; (*into)->mOutputFileName = NULL; @@ -458,6 +485,15 @@ void Settings_Copy (struct thread_Settings *from, struct thread_Settings **into, unsetReport((*into)); } +void Settings_Grow_mBuf (struct thread_Settings *mSettings, int newsize) { + char *tmp = new char[newsize]; + pattern(tmp, newsize); + memcpy(tmp, mSettings->mBuf, mSettings->mBufLen); + DELETE_ARRAY(mSettings->mBuf); + mSettings->mBuf = tmp; + mSettings->mBufLen = newsize; +} + /* ------------------------------------------------------------------- * Delete memory: Does not clean up open file pointers or ptr_parents * ------------------------------------------------------------------- */ @@ -477,6 +513,7 @@ void Settings_Destroy (struct thread_Settings *mSettings) { DELETE_ARRAY(mSettings->mHistogramStr); DELETE_ARRAY(mSettings->mSSMMulticastStr); DELETE_ARRAY(mSettings->mCongestion); + DELETE_ARRAY(mSettings->mLoadCCA); FREE_ARRAY(mSettings->mIfrname); FREE_ARRAY(mSettings->mIfrnametx); FREE_ARRAY(mSettings->mTransferIDStr); @@ -729,7 +766,10 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings break; case 'v': // print version and exit - fprintf(stdout, "%s", version); + if (strlen(IPERF_BRANCH)) + fprintf(stdout, "%s", branch_version); + else + fprintf(stdout, "%s", version); exit(0); break; @@ -888,9 +928,12 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings // TODO use a function that understands base-2 // the zero base here allows the user to specify // "0x#" hex, "0#" octal, and "#" decimal numbers - if ((mExtSettings->mTOS = parse_ipqos(optarg)) == -1) { - fprintf(stderr, "Invalid --tos value of %s\n", optarg); + mExtSettings->mTOS = parse_ipqos(optarg); + if (mExtSettings->mTOS == -1) { + fprintf(stderr, "WARN: Invalid --tos value of %s ignored\n", optarg); mExtSettings->mTOS = 0; + } else { + setSetTOS(mExtSettings); } break; @@ -923,10 +966,14 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings break; case 'Z': -#ifdef TCP_CONGESTION - setCongestionControl(mExtSettings); - mExtSettings->mCongestion = new char[strlen(optarg)+1]; - strcpy(mExtSettings->mCongestion, optarg); +#if HAVE_DECL_TCP_CONGESTION + if (isCongestionControl(mExtSettings)) { + fprintf(stderr, "Option --tcp-congestion or -Z ignored because --tcp-cca set\n"); + } else { + setCongestionControl(mExtSettings); + mExtSettings->mCongestion = new char[strlen(optarg)+1]; + strcpy(mExtSettings->mCongestion, optarg); + } #else fprintf(stderr, "The -Z option is not available on this operating system\n"); #endif @@ -949,10 +996,18 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings incrsrcport = 0; setIncrSrcPort(mExtSettings); } + if (ignoreshutdown) { + ignoreshutdown = 0; + setIgnoreShutdown(mExtSettings); + } if (sumdstip) { sumdstip = 0; setSumServerDstIP(mExtSettings); } + if (synctransferid) { + synctransferid = 0; + setSyncTransferID(mExtSettings); + } if (txstarttime) { long seconds; long usecs; @@ -1013,9 +1068,36 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings mExtSettings->connectonly_count = -1; } } - if (connectretry) { - connectretry = 0; - mExtSettings->mConnectRetries = atoi(optarg); + if (connectretryinterval) { + connectretryinterval = 0; + char *end; + double period = strtof(optarg, &end); + if ((*end != '\0') || (period < 0 )) { + fprintf (stderr, "Invalid value of '%s' for --connect-retry-timer\n", optarg); + exit(1); + } + if (period > (UINT_MAX / 1e6)) { + fprintf (stderr, "Too large value of '%s' for --connect-retry-timer, max is %f\n", optarg, (UINT_MAX / 1e6)); + exit(1); + } + mExtSettings->connect_retry_timer = static_cast<unsigned int>(ceil(period * 1e6)); + if (mExtSettings->connect_retry_timer == 0) { + mExtSettings->connect_retry_timer = 10000; + } + } + if (connectretrytime) { + connectretrytime = 0; + char *end; + double timer = strtof(optarg, &end); + if (*end != '\0') { + fprintf (stderr, "Invalid value of '%s' for --connect-retry-time\n", optarg); + exit(1); + } + if (timer > (UINT_MAX / 1e6)) { + fprintf (stderr, "Too large value of '%s' for --connect-retry-time, max is %f\n", optarg, (UINT_MAX / 1e6)); + exit(1); + } + mExtSettings->connect_retry_time = timer; } if (sumonly) { sumonly = 0; @@ -1048,7 +1130,7 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings permitkey = 0; if (optarg) { strncpy(mExtSettings->mPermitKey, optarg, MAX_PERMITKEY_LEN); - mExtSettings->mPermitKey[MAX_PERMITKEY_LEN] = '\0'; + mExtSettings->mPermitKey[MAX_PERMITKEY_LEN-1] = '\0'; } else { mExtSettings->mPermitKey[0] = '\0'; } @@ -1056,7 +1138,12 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings } if (permitkeytimeout) { permitkeytimeout = 0; - if (atof(optarg) >= 0.0) + if (atof(optarg) > mExtSettings->mListenerTimeout) + mExtSettings->mListenerTimeout = static_cast<size_t>(atof(optarg)); + } + if (testxchangetimeout) { + testxchangetimeout = 0; + if (atof(optarg) > mExtSettings->mListenerTimeout) mExtSettings->mListenerTimeout = static_cast<size_t>(atof(optarg)); } if (histogram) { @@ -1096,8 +1183,13 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings setOverrideTOS(mExtSettings); } } + if (dscp) { + dscp = 0; + // dscp needs to shifted by 2 and the ECN bits masked off to map to a TOS byte + mExtSettings->mTOS = (atoi(optarg) << DSCP_SHIFT) & DSCP_BITMASK; //2 & 0xFC + } if (fqrate) { -#if defined(HAVE_DECL_SO_MAX_PACING_RATE) +#if (HAVE_DECL_SO_MAX_PACING_RATE) fqrate=0; setFQPacing(mExtSettings); mExtSettings->mFQPacingRate = static_cast<uintmax_t>(bitorbyte_atoi(optarg) / 8); @@ -1105,6 +1197,34 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings fprintf(stderr, "WARNING: The --fq-rate option is not supported\n"); #endif } +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (fqratestep) { + fqratestep=0; + setFQPacingStep(mExtSettings); + mExtSettings->mFQPacingRateStep = static_cast<uintmax_t>(bitorbyte_atoi(optarg) / 8); + setEnhanced(mExtSettings); + } + if (fqratestepinterval) { + fqratestepinterval=0; + double val; +#if HAVE_STRTOD + char *end; + errno = 0; + val = strtod(optarg, &end); + if (errno || (*end != '\0')) { + fprintf(stderr, "ERROR: --fq-rate-step-interval value of '%s' not recognized\n", optarg); + exit(1); + } +#else + val = atof(optarg); +#endif + if (val > 0.0) { + mExtSettings->mFQPacingRateStepInterval = val; + setFQPacingStepInterval(mExtSettings); + setEnhanced(mExtSettings); + } + } +#endif if (isochronous) { isochronous = 0; setEnhanced(mExtSettings); @@ -1153,14 +1273,59 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings setTcpQuickAck(mExtSettings); #endif } + if (tcptxdelay) { + tcptxdelay = 0; +#if HAVE_DECL_TCP_TX_DELAY + char *tmp= new char [strlen(optarg) + 1]; + char *results; + strcpy(tmp, optarg); + mExtSettings->mTcpTxDelayProb = 1.0; + if (((results = strtok(tmp, ",")) != NULL) && !strcmp(results,tmp)) { + mExtSettings->mTcpTxDelayMean = atof(results); + if ((results = strtok(NULL, ",")) != NULL) { + mExtSettings->mTcpTxDelayProb = atof(results); + } + } + if (mExtSettings->mTcpTxDelayMean > 0) { + setTcpTxDelay(mExtSettings); + } +#else + fprintf(stderr, "The --tcp-tx-delay option is not available on this operating system\n"); +#endif + } if (utctimes) { setUTC(mExtSettings); } + if (loadcca) { + loadcca = 0; +#if HAVE_DECL_TCP_CONGESTION + setLoadCCA(mExtSettings); + mExtSettings->mLoadCCA = new char[strlen(optarg)+1]; + strcpy(mExtSettings->mLoadCCA, optarg); +#else + fprintf(stderr, "The --working-load-cca option is not available on this operating system\n"); +#endif + } + if (primarycca) { + primarycca = 0; +#if HAVE_DECL_TCP_CONGESTION + if (isCongestionControl(mExtSettings)) { + fprintf(stderr, "Option --tcp-cca ignored because --tcp-congestion or -Z set\n"); + } else { + setCongestionControl(mExtSettings); + mExtSettings->mCongestion = new char[strlen(optarg)+1]; + strcpy(mExtSettings->mCongestion, optarg); + } +#else + fprintf(stderr, "The --tcp-cca option is not available on this operating system\n"); +#endif + } if (workingload) { workingload = 0; #ifdef HAVE_THREAD setWorkingLoadUp(mExtSettings); setWorkingLoadDown(mExtSettings); + setEnhanced(mExtSettings); if (optarg) { char *tmp= new char [strlen(optarg) + 1]; if (tmp) { @@ -1224,7 +1389,7 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings } if (burstsize) { burstsize = 0; - setPeriodicBurst(mExtSettings); + setBurstSize(mExtSettings); if (optarg) { mExtSettings->mBurstSize = byte_atoi(optarg); } @@ -1300,6 +1465,20 @@ void Settings_Interpret (char option, const char *optarg, struct thread_Settings } } } + if (bouncebackrequest) { + bouncebackrequest = 0; + if (optarg) + mExtSettings->mBounceBackBytes = byte_atoi(optarg); + else + mExtSettings->mBounceBackBytes = 0; + } + if (bouncebackreply) { + bouncebackreply = 0; + if (optarg) + mExtSettings->mBounceBackReplyBytes = byte_atoi(optarg); + else + mExtSettings->mBounceBackReplyBytes = 0; + } break; default: // ignore unknown break; @@ -1383,7 +1562,7 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { } } else { if (isBounceBack(mExtSettings)) - mExtSettings->mBufLen = kDefault_BBTCPBufLen; + mExtSettings->mBufLen = kDefault_TCPBufLen; else mExtSettings->mBufLen = kDefault_TCPBufLen; } @@ -1428,12 +1607,6 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "ERROR: compatibility mode not supported with the requested with options\n"); bail = true; } -#if !(HAVE_DECL_IP_TOS) - if (isOverrideTOS(mExtSettings) || mExtSettings->mTOS) { - unsetOverrideTOS(mExtSettings); - fprintf(stderr, "WARN: IP_TOS not supported\n"); - } -#endif if (isPermitKey(mExtSettings)) { if (isUDP(mExtSettings)) { fprintf(stderr, "ERROR: Option of --permit-key not supported with UDP\n"); @@ -1486,10 +1659,6 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "WARN: option of --jitter-histogram not supported on the client\n"); unsetJitterHistogram(mExtSettings); } - if (isIncrSrcPort(mExtSettings) && !mExtSettings->mBindPort) { - fprintf(stderr, "WARN: option of --incr-srcport requires -B bind option w/port to be set\n"); - unsetIncrSrcPort(mExtSettings); - } if (isPeriodicBurst(mExtSettings)) { setEnhanced(mExtSettings); setFrameInterval(mExtSettings); @@ -1502,6 +1671,17 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "ERROR: option of --permit-key requires a value on the client\n"); bail = true; } +#if (HAVE_DECL_SO_MAX_PACING_RATE) + if (isFQPacingStep(mExtSettings)) { + if (!isFQPacing(mExtSettings)) { + setFQPacing(mExtSettings); + mExtSettings->mFQPacingRate = mExtSettings->mFQPacingRateStep; + } + if (!isFQPacingStepInterval(mExtSettings)) { + mExtSettings->mFQPacingRateStepInterval = 1.0; + } + } +#endif if (!isUDP(mExtSettings) && isTxHoldback(mExtSettings) && isTxStartTime(mExtSettings)) { fprintf(stderr,"ERROR: options of --txstart-time and --txdelay-time are mutually exclusive\n"); bail = true; @@ -1527,11 +1707,40 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { bail = true; } } + if (isTripTime(mExtSettings) && (mExtSettings->mBufLen < static_cast<int> (sizeof(struct TCP_burst_payload)))) { + fprintf(stderr, "ERROR: payload (-l) size of %d too small for --trip-times, must be %d or greater\n",\ + mExtSettings->mBufLen, static_cast<int> (sizeof(struct TCP_burst_payload))); + bail = true; + } if (isBounceBack(mExtSettings)) { if (static_cast<int> (mExtSettings->mBurstSize) > 0) { fprintf(stderr, "WARN: options of --burst-size for bounce-back ignored, use -l sets size\n"); } - mExtSettings->mBounceBackBytes = mExtSettings->mBufLen; + if (mExtSettings->mBounceBackBytes <= 0) { + if (isBuflenSet(mExtSettings)) { + // Backward compatibility with older versions + mExtSettings->mBounceBackBytes = mExtSettings->mBufLen; + } else { + mExtSettings->mBounceBackBytes = kDefault_BBTCPReqLen; + } + } else if (mExtSettings->mBounceBackBytes > mExtSettings->mBufLen) { + if (isBuflenSet(mExtSettings)) { + mExtSettings->mBounceBackBytes = mExtSettings->mBufLen; + fprintf(stderr, "WARN: bounceback request will use -l length and not --bounceback-request value\n"); + } else { + mExtSettings->mBufLen = mExtSettings->mBounceBackBytes; + } + } + if (mExtSettings->mBounceBackReplyBytes <= 0) { + mExtSettings->mBounceBackReplyBytes = mExtSettings->mBounceBackBytes; + } else if (mExtSettings->mBounceBackReplyBytes > mExtSettings->mBufLen) { + if (isBuflenSet(mExtSettings)) { + mExtSettings->mBounceBackReplyBytes = mExtSettings->mBufLen; + fprintf(stderr, "WARN: bounceback reply will use -l length and not --bounceback-reply value\n"); + } else { + mExtSettings->mBufLen = mExtSettings->mBounceBackReplyBytes; + } + } mExtSettings->mBurstSize = mExtSettings->mBufLen; #if HAVE_DECL_TCP_QUICKACK if (notcpbbquickack_cliset && isTcpQuickAck(mExtSettings)) { @@ -1559,15 +1768,17 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { } } #if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isTripTime(mExtSettings) && !isUDP(mExtSettings)) { - if (isWritePrefetch(mExtSettings)) { - if (mExtSettings->mWritePrefetch <= 0) { - unsetWritePrefetch(mExtSettings); + if (!isUDP(mExtSettings)) { + if (isTcpWriteTimes(mExtSettings) || isTripTime(mExtSettings)) { + if (isWritePrefetch(mExtSettings)) { + if (mExtSettings->mWritePrefetch <= 0) { + unsetWritePrefetch(mExtSettings); + } + } else { + mExtSettings->mWritePrefetch = SMALL_WRITE_PREFETCH; + setWritePrefetch(mExtSettings); + setEnhanced(mExtSettings); } - } else { - mExtSettings->mWritePrefetch = SMALL_WRITE_PREFETCH; - setWritePrefetch(mExtSettings); - setEnhanced(mExtSettings); } } #endif @@ -1585,10 +1796,14 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "ERROR: option of --burst-size %d must be equal or larger to write length (-l) %d\n", mExtSettings->mBurstSize, mExtSettings->mBufLen); bail = true; } - } else if (!isBounceBack(mExtSettings) && (static_cast<int> (mExtSettings->mBurstSize) > 0)) { - setPeriodicBurst(mExtSettings); - mExtSettings->mFPS = 1.0; - fprintf(stderr, "WARN: option of --burst-size without --burst-period defaults --burst-period to 1 second\n"); + } + if ((mExtSettings->connect_retry_time > 0) && !mExtSettings->connect_retry_timer) { + fprintf(stderr, "WARN: companion option of --connect-retry-timer not set - setting to default value of one second\n"); + mExtSettings->connect_retry_timer = 1000000; // 1 sec in units usecs + } + if ((mExtSettings->connect_retry_timer > 0) && (mExtSettings->connect_retry_time <= 0)) { + fprintf(stderr, "WARN: companion option of --connect-retry-time not set - setting to default value of ten seconds\n"); + mExtSettings->connect_retry_time = 10; } if (isUDP(mExtSettings)) { if (isPeerVerDetect(mExtSettings)) { @@ -1615,7 +1830,7 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "ERROR: option --ipg must be a positive value\n"); bail = true; } - if (mExtSettings->mConnectRetries > 0) { + if (mExtSettings->connect_retry_timer > 0) { fprintf(stderr, "ERROR: option --connect-retries not supported with -u UDP\n"); bail = true; } @@ -1627,18 +1842,22 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "WARN: setting of option --tcp-quickack is not supported with -u UDP\n"); unsetWritePrefetch(mExtSettings); } + if (isTcpTxDelay(mExtSettings)) { + fprintf(stderr, "WARN: setting of option --tcp-tx-delay is not supported with -u UDP\n"); + unsetTcpTxDelay(mExtSettings); + } { double delay_target; if (isIPG(mExtSettings)) { - delay_target = mExtSettings->mBurstIPG * 1e9; // convert from seconds to nanoseconds + delay_target = ((mExtSettings->mBurstIPG > 0) ? mExtSettings->mBurstIPG * 1e9 : 0); // convert from seconds to nanoseconds } else { // compute delay target in units of nanoseconds if (mExtSettings->mAppRateUnits == kRate_BW) { // compute delay for bandwidth restriction, constrained to [0,max] seconds - delay_target = (mExtSettings->mBufLen * 8e9) / mExtSettings->mAppRate; + delay_target = ((mExtSettings->mAppRate > 0) ? ((mExtSettings->mBufLen * 8e9) / mExtSettings->mAppRate) : 0); } else { - delay_target = 1e9 / mExtSettings->mAppRate; + delay_target = ((mExtSettings->mAppRate > 0) ? (1e9 / mExtSettings->mAppRate) : 0); } } if (delay_target < 0 || @@ -1648,9 +1867,9 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { } } if (isTripTime(mExtSettings)) { - if (mExtSettings->mBufLen < MINTRIPTIMEPLAYOAD) { + if (mExtSettings->mBufLen < MINTRIPTIMEPAYLOAD) { if (isReverse(mExtSettings) || isFullDuplex(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) { - fprintf(stderr, "ERROR: payload (-l) size of %d too small for --trip-times, must be %d or greater\n", mExtSettings->mBufLen, MINTRIPTIMEPLAYOAD); + fprintf(stderr, "ERROR: payload (-l) size of %d too small for --trip-times, must be %d or greater\n", mExtSettings->mBufLen, MINTRIPTIMEPAYLOAD); bail = true; } else { setSmallTripTime(mExtSettings); @@ -1731,6 +1950,11 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { mExtSettings->mListenerTimeout = DEFAULT_PERMITKEY_LIFE; } } + if ((mExtSettings->mWorkingLoadThreads > 0) || (!isWorkingLoadUp(mExtSettings) && isWorkingLoadDown(mExtSettings)) \ + || (isWorkingLoadUp(mExtSettings) && !isWorkingLoadDown(mExtSettings))) { + fprintf(stderr, "ERROR: setting of --working-load options is not supported on the server, just use --working-load\n"); + bail = true; + } if (isBounceBack(mExtSettings)) { fprintf(stderr, "ERROR: setting of option --bounce-back is not supported on the server\n"); bail = true; @@ -1769,6 +1993,9 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { if (isIncrDstIP(mExtSettings)) { fprintf(stderr, "WARN: option of --incr-dstip is not supported on the server\n"); } + if (isIgnoreShutdown(mExtSettings)) { + fprintf(stderr, "WARN: option of --ignore-shutdown is not supported on the server\n"); + } if (isFQPacing(mExtSettings)) { fprintf(stderr, "WARN: option of --fq-rate is not supported on the server\n"); } @@ -1778,12 +2005,15 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { if (isPeerVerDetect(mExtSettings)) { fprintf(stderr, "WARN: option of -X or --peer-detect not supported on the server\n"); } - if (mExtSettings->mConnectRetries > 0) { + if (mExtSettings->connect_retry_timer > 0) { fprintf(stderr, "WARN: option --connect-retries not supported on the server\n"); } if (isNearCongest(mExtSettings)) { fprintf(stderr, "WARN: option of --near-congestion not supported on the server\n"); } + if (isSyncTransferID(mExtSettings)) { + fprintf(stderr, "WARN: option of --sync-transfer-id is not supported on the server\n"); + } if (isPeriodicBurst(mExtSettings)) { fprintf(stderr, "WARN: option of --burst-period can only be set on the client\n"); } @@ -1802,15 +2032,15 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { if (isHistogram(mExtSettings)) { if (!mExtSettings->mHistogramStr) { if (mExtSettings->mThreadMode == kMode_Server) { - // set default rx histogram settings, milliseconds bins between 0 and 1 secs - mExtSettings->mHistBins = 1000; + // set default rx histogram settings, milliseconds bins between 0 and 10 secs + mExtSettings->mHistBins = 10000; mExtSettings->mHistBinsize = 1; mExtSettings->mHistUnits = 3; mExtSettings->mHistci_lower = 5; mExtSettings->mHistci_upper = 95; } else { // set default tx histogram settings, microseconds with 100 us bins - mExtSettings->mHistBins = 10000; + mExtSettings->mHistBins = 100000; mExtSettings->mHistBinsize = 100; mExtSettings->mHistUnits = 6; mExtSettings->mHistci_lower = 5; @@ -1910,7 +2140,6 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { // Check for further mLocalhost (-B) and <dev> requests // full addresses look like 192.168.1.1:6001%eth0 or [2001:e30:1401:2:d46e:b891:3082:b939]:6001%eth0 - iperf_sockaddr tmp; // Parse -B addresses if (mExtSettings->mLocalhost) { if (((results = strtok(mExtSettings->mLocalhost, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) { @@ -1934,17 +2163,26 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { fprintf(stderr, "WARNING: port %s ignored - set receive port on server via -p or -L\n", results); } } - // Check for multicast per the -B - SockAddr_setHostname(mExtSettings->mLocalhost, &tmp, - (isIPV6(mExtSettings) ? 1 : 0)); - if ((mExtSettings->mThreadMode != kMode_Client) && SockAddr_isMulticast(&tmp)) { - setMulticast(mExtSettings); - } else if (SockAddr_isMulticast(&tmp)) { - if (mExtSettings->mIfrname) { - free(mExtSettings->mIfrname); - mExtSettings->mIfrname = NULL; + // Do multicast address checking and processing + SockAddr_setHostname(mExtSettings->mLocalhost, &mExtSettings->multicast_group, \ + &mExtSettings->size_multicast_group, (isIPV6(mExtSettings) ? 1 : 0)); + if (SockAddr_isMulticast(&mExtSettings->multicast_group)) { + if (mExtSettings->mThreadMode == kMode_Client) { + fprintf(stderr, "WARNING: Client src addr (per -B) must be ip unicast\n"); + exit(1); + } else { + setMulticast(mExtSettings); + if (isSSMMulticast(mExtSettings)) { + SockAddr_setHostname(mExtSettings->mSSMMulticastStr, &mExtSettings->multicast_group_source, \ + &mExtSettings->size_multicast_group, (isIPV6(mExtSettings) ? 1 : 0)); + if (SockAddr_isMulticast(&mExtSettings->multicast_group_source)) { + fprintf(stderr, "WARNING: SSM host src address (-H or --ssm-host) must be ip unicast\n"); + exit(1); + } + } } - fprintf(stderr, "WARNING: Client src addr (per -B) must be ip unicast\n"); + } else { + SockAddr_zeroAddress(&mExtSettings->multicast_group); // Zero out multicast sockaddr } } // Parse client (-c) addresses for multicast, link-local and bind to device, port incr @@ -1987,25 +2225,22 @@ void Settings_ModalOptions (struct thread_Settings *mExtSettings) { } if (SockAddr_isMulticast(&mExtSettings->peer)) { bail = false; - if ((mExtSettings->mThreads > 1) && !isIncrDstIP(mExtSettings)) { - fprintf(stderr, "ERROR: client option of -P greater than 1 not supported with multicast address\n"); - bail = true; - } else if (isFullDuplex(mExtSettings) || isReverse(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) { + if (isFullDuplex(mExtSettings) || isReverse(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) { fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r not supported with multicast addresses\n"); bail = true; + } else if (isSyncTransferID(mExtSettings)) { + fprintf(stderr, "ERROR: option of --sync-transfer-id incompatibile with multicast\n"); + bail = true; } if (bail) exit(1); else setMulticast(mExtSettings); } -#ifndef HAVE_DECL_SO_BINDTODEVICE - if (mExtSettings->mIfrnametx) { - fprintf(stderr, "bind to device will be ignored because not supported\n"); - free(mExtSettings->mIfrnametx); - mExtSettings->mIfrnametx=NULL; - } -#endif + } + if (isIncrSrcPort(mExtSettings) && !mExtSettings->mBindPort) { + fprintf(stderr, "WARN: option of --incr-srcport requires -B bind option w/port to be set\n"); + unsetIncrSrcPort(mExtSettings); } if ((mExtSettings->mIntervalMode == kInterval_Time) && (mExtSettings->mIntervalMode <= 0)) { mExtSettings->mIntervalMode = kInterval_None; @@ -2151,7 +2386,7 @@ void Settings_GenerateClientSettings (struct thread_Settings *server, struct thr thread_debug("header set for a version 1 test"); #endif if (isFullDuplex(server) || isServerReverse(server)) - setTransferID(server, 1); + setTransferID(server, REVERSED); if (isFullDuplex(server) || v1test) { Settings_Copy(server, client, SHALLOW_COPY); reversed_thread = *client; @@ -2291,7 +2526,16 @@ int Settings_GenerateClientHdrV1 (struct thread_Settings *client, struct client_ } else { hdr->mPort = htonl(client->mPort); } - hdr->numThreads = htonl(client->mThreads); + if (isSyncTransferID(client)) { + if (client->mTransferID & (HEADER_HASTRANSFERID | HEADER_TRANSFERIDMASK)) { + fprintf(stderr, "WARN: num threads too large for --sync-transfer-id\n"); + } else { + uint32_t tidthreads = (HEADER_HASTRANSFERID | ((client->mTransferID << HEADER_TRANSFERIDSHIFT) & HEADER_TRANSFERIDMASK) | client->mThreads); + hdr->numThreads = htonl(tidthreads); + } + } else { + hdr->numThreads = htonl(client->mThreads); + } if (isModeTime(client)) { hdr->mAmount = htonl(-(long)client->mAmount); } else { @@ -2325,10 +2569,13 @@ int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, s struct client_udpsmall_testhdr *hdr = static_cast<struct client_udpsmall_testhdr *>(testhdr); memset(hdr, 0, buflen); hdr->flags = htons(HEADER16_SMALL_TRIPTIMES); + if (isTxStartTime(client) && !TimeZero(startTime)) { + hdr->start_tv_sec = htonl(startTime.tv_sec); + } #ifdef HAVE_THREAD_DEBUG thread_debug("UDP small trip times flags = %X", ntohs(hdr->flags)); #endif - return (MINIPERFPAYLOAD); + return buflen; } // flags common to both TCP and UDP if (isReverse(client) && !isCompat(client)) { @@ -2473,7 +2720,7 @@ int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, s } len += sizeof(struct client_hdrext); len += Settings_GenerateClientHdrV1(client, &hdr->base); - if (!isCompat(client) && (client->mMode != kTest_Normal)) { + if ((!isCompat(client) && (client->mMode != kTest_Normal)) || isSyncTransferID(client)) { flags |= HEADER_VERSION1; if (client->mMode == kTest_DualTest) flags |= RUN_NOW; @@ -2481,7 +2728,7 @@ int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, s if (isPeerVerDetect(client)) { flags |= (HEADER_V2PEERDETECT | HEADER_VERSION2); } - if (isTripTime(client) || isFQPacing(client) || isIsochronous(client) || isTxStartTime(client)) { + if (isTripTime(client) || isFQPacing(client) || isIsochronous(client) || isTxStartTime(client) || isLoadCCA(client) || isCongestionControl(client)) { hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec); hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec); hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate); @@ -2503,6 +2750,10 @@ int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, s hdr->extend.TCPWritePrefetch = htonl((long)client->mWritePrefetch); } #endif + if (client->barrier_time) { + lowerflags |= HEADER_BARRIER_TIME; + hdr->extend.barrier_usecs = htonl((long)client->barrier_time); + } #if HAVE_DECL_TCP_QUICKACK if (isTcpQuickAck(client) && (!isReverse(client) || isFullDuplex(client))) { upperflags |= HEADER_TCPQUICKACK; @@ -2529,6 +2780,26 @@ int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, s } len += sizeof(struct client_hdrext_isoch_settings); } + } else if (isLoadCCA(client) || isCongestionControl(client)) { + // just jump the enclave hdr + len += sizeof(struct client_hdrext_isoch_settings); + } + if (isLoadCCA(client) && (isWorkingLoadUp(client) || isWorkingLoadDown(client))) { + uint16_t lenfield = ((client->mLoadCCA != NULL) ? (strlen(client->mLoadCCA)) : 0); + if (lenfield > 0) { + hdr->cca.cca_length = htons(lenfield); + lowerflags |= HEADER_CCA; + memcpy(hdr->cca.value, client->mLoadCCA, lenfield); + len += sizeof(uint16_t) + lenfield; + } + } else if (isCongestionControl(client)) { + uint16_t lenfield = ((client->mCongestion != NULL) ? (strlen(client->mCongestion)) : 0); + if (lenfield > 0) { + hdr->cca.cca_length = htons(lenfield); + lowerflags |= HEADER_CCA; + memcpy(hdr->cca.value, client->mCongestion, lenfield); + len += sizeof(uint16_t) + lenfield; + } } if (isReverse(client) || isFullDuplex(client)) { flags |= HEADER_VERSION2; diff --git a/src/SocketAddr.c b/src/SocketAddr.c index 4322475..b42d272 100644 --- a/src/SocketAddr.c +++ b/src/SocketAddr.c @@ -62,6 +62,16 @@ extern "C" { #endif +int SockAddr_getAFdomain (iperf_sockaddr *inSockAddr) { + return (SockAddr_isIPv6(inSockAddr) ? +#if HAVE_IPV6 + AF_INET6 +#else + AF_INET +#endif + : AF_INET); +} + /* ------------------------------------------------------------------- * Create a socket address. If inHostname is not null, resolve that * address and fill it in. Fill in the port number. Use IPv6 ADDR_ANY @@ -70,7 +80,7 @@ extern "C" { void SockAddr_remoteAddr (struct thread_Settings *inSettings) { if (SockAddr_isZeroAddress(&inSettings->peer) == 0) { if (inSettings->mHost != NULL) { - SockAddr_setHostname(inSettings->mHost, &inSettings->peer, isIPV6(inSettings)); + SockAddr_setHostname(inSettings->mHost, &inSettings->peer, &inSettings->size_peer, isIPV6(inSettings)); if (inSettings->incrdstip) SockAddr_incrAddress(&inSettings->peer, inSettings->incrdstip); } else { @@ -100,11 +110,10 @@ void SockAddr_localAddr (struct thread_Settings *inSettings) { SockAddr_zeroAddress(&inSettings->local); if (inSettings->mLocalhost != NULL) { - SockAddr_setHostname(inSettings->mLocalhost, &inSettings->local, + SockAddr_setHostname(inSettings->mLocalhost, &inSettings->local, &inSettings->size_local, isIPV6(inSettings)); if (inSettings->incrsrcip) SockAddr_incrAddress(&inSettings->local, inSettings->incrsrcip); - } else { #if HAVE_IPV6 if (isIPV6(inSettings)) { @@ -158,11 +167,6 @@ void SockAddr_localAddr (struct thread_Settings *inSettings) { /* * User specified port so use it */ -#if HAVE_DECL_SO_REUSEPORT - int boolean = 1; - Socklen_t len = sizeof(boolean); - setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEPORT, (char*) &boolean, len); -#endif SockAddr_setPort(&inSettings->local, (inSettings->mBindPort + inSettings->incrsrcport)); } else { /* @@ -179,12 +183,13 @@ void SockAddr_localAddr (struct thread_Settings *inSettings) { } } } + // end SocketAddr /* ------------------------------------------------------------------- * Resolve the hostname address and fill it in. * ------------------------------------------------------------------- */ -void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, int isIPv6) { +void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, Socklen_t *addr_size, int isIPv6) { // ..I think this works for both ipv6 & ipv4... we'll see bool found = false; int ret_ga; @@ -201,6 +206,7 @@ void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, i while (itr != NULL) { if (itr->ai_family == AF_INET) { memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen)); + *addr_size = (Socklen_t) sizeof(struct sockaddr_in); freeaddrinfo(res); found = true; break; @@ -223,6 +229,7 @@ void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, i if (itr->ai_family == AF_INET6) { memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen)); freeaddrinfo(res); + *addr_size = (Socklen_t) sizeof(struct sockaddr_in6); found = true; break; } else { @@ -271,7 +278,11 @@ void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, i } } if (!found) { - fprintf(stderr, "ERROR: failed to find an ip address for host '%s'\n", inHostname); + if (!isIPv6) { + fprintf(stderr, "ERROR: failed to find an ip address for host '%s'\n", inHostname); + } else { + fprintf(stderr, "ERROR: failed to find an ipv6 address for host '%s'\n", inHostname); + } exit(1); } } diff --git a/src/active_hosts.cpp b/src/active_hosts.cpp index a3455ab..24358c3 100644 --- a/src/active_hosts.cpp +++ b/src/active_hosts.cpp @@ -61,10 +61,18 @@ * Global table with active hosts, their sum reports and active thread counts */ static struct Iperf_Table active_table; -static bool Iperf_host_port_present (iperf_sockaddr *find); static struct Iperf_ListEntry* Iperf_host_present (iperf_sockaddr *find); +static struct Iperf_ListEntry* Iperf_flow_present (iperf_sockaddr *find); #if HAVE_THREAD_DEBUG +static void rcvfrom_peer_debug (thread_Settings *server, bool duplicate) { + char tmpaddr[200]; + size_t len=200; + unsigned short port = SockAddr_getPort(&server->peer); + SockAddr_getHostAddress(&server->peer, tmpaddr, len); + thread_debug("rcvfrom peer: %s port %d dup=%s", tmpaddr, port, (duplicate ? "true" : "false")); +} + static void active_table_show_entry(const char *action, Iperf_ListEntry *entry, int found) { assert(action != NULL); assert(entry != NULL); @@ -72,9 +80,9 @@ static void active_table_show_entry(const char *action, Iperf_ListEntry *entry, size_t len=200; unsigned short port = SockAddr_getPort(&(entry->host)); SockAddr_getHostAddress(&(entry->host), tmpaddr, len); - thread_debug("active table: %s %s port %d (flag=%d) rootp=%p entryp=%p totcnt/activecnt/hostcnt = %d/%d/%d", \ - action, tmpaddr, port, found, (void *) active_table.root, (void *) entry, active_table.total_count, \ - active_table.count, entry->thread_count); + thread_debug("active table: %s %s port %d (flag=%d) rootp=%p entryp=%p hostcnt/flowcnt/threadcnt = %d/%d/%d", \ + action, tmpaddr, port, found, (void *) active_table.sum_root, (void *) entry->sumreport, active_table.sum_count, \ + active_table.flow_count, entry->thread_count); } static void active_table_show_compare(const char *action, Iperf_ListEntry *entry, iperf_sockaddr *host, const char *type) { assert(action != NULL); @@ -92,79 +100,108 @@ static void active_table_show_compare(const char *action, Iperf_ListEntry *entry void Iperf_initialize_active_table () { Mutex_Initialize(&active_table.my_mutex); - active_table.root = NULL; + active_table.flow_root = NULL; + active_table.sum_root = NULL; active_table.groupid = 0; +#if HAVE_THREAD_DEBUG + active_table.sum_count = 0; + active_table.flow_count = 0; +#endif } /* - * Add Entry add to the list or update thread count + * Add Entry add to the list or update thread count, return 0 on UDP tuple duplicate */ -static void active_table_update (iperf_sockaddr *host, struct thread_Settings *agent) { - assert(host != NULL); - assert(agent != NULL); - Iperf_ListEntry *this_entry = Iperf_host_present(host); - active_table.total_count++; - if (this_entry == NULL) { - this_entry = new Iperf_ListEntry(); - assert(this_entry != NULL); - this_entry->host = *host; - this_entry->next = active_table.root; - this_entry->thread_count = 1; - this_entry->socket = agent->mSock; - active_table.count++; - active_table.groupid++; - active_table.root = this_entry; - this_entry->sum_report = InitSumReport(agent, active_table.total_count, 0); - IncrSumReportRefCounter(this_entry->sum_report); - agent->mSumReport = this_entry->sum_report; - this_entry->sum_report->info.common->transferID = -active_table.groupid; // sum ids are negative +static inline struct Iperf_ListEntry *hostkey_insert (iperf_sockaddr *host) { + struct Iperf_ListEntry *this_key = new Iperf_ListEntry(); + assert(this_key != NULL); + if (!this_key) { + fprintf(stderr, "Memory alloc failure in key insert\n"); + exit(1); + } + this_key->next = active_table.sum_root; + active_table.sum_root = this_key; + this_key->host = *host; + this_key->thread_count = 0; #if HAVE_THREAD_DEBUG - active_table_show_entry("new entry", this_entry, ((SockAddr_are_Equal(&this_entry->host, host) && SockAddr_Hostare_Equal(&this_entry->host, host)))); + active_table.sum_count++; + active_table_show_entry("new host entry", this_key, ((SockAddr_are_Equal(&this_key->host, host) && SockAddr_Hostare_Equal(&this_key->host, host)))); #endif - } else { - this_entry->thread_count++; - agent->mSumReport = this_entry->sum_report; - IncrSumReportRefCounter(this_entry->sum_report); + return this_key; +} + +static inline struct Iperf_ListEntry *flowkey_insert (iperf_sockaddr *host) { + struct Iperf_ListEntry *this_key = new Iperf_ListEntry(); + assert(this_key != NULL); + if (!this_key) { + fprintf(stderr, "Memory alloc failure in key insert\n"); + exit(1); + } + this_key->next = active_table.flow_root; + active_table.flow_root = this_key; + this_key->host = *host; #if HAVE_THREAD_DEBUG - active_table_show_entry("incr entry", this_entry, 1); + active_table.flow_count++; +// active_table_show_flow_entry("new flow entry", this_key, ((SockAddr_are_Equal(&this_key->host, host) && SockAddr_Hostare_Equal(&this_key->host, host)))); #endif - } + return this_key; } static inline iperf_sockaddr *active_table_get_host_key (struct thread_Settings *agent) { - iperf_sockaddr *key = (isSumServerDstIP(agent) ? &agent->local : &agent->peer); + iperf_sockaddr *key = ((isIncrDstIP(agent) || isSumServerDstIP(agent)) ? &agent->local : &agent->peer); return key; } -// Thread access to store a host -int Iperf_push_host (struct thread_Settings *agent) { - iperf_sockaddr *host = active_table_get_host_key(agent); - Mutex_Lock(&active_table.my_mutex); - active_table_update(host, agent); - int groupid = active_table.groupid; - Mutex_Unlock(&active_table.my_mutex); - return groupid; +static bool Iperf_push_flow (iperf_sockaddr *host) { + bool rc; + if (Iperf_flow_present(host)) { + rc = false; + } else { + flowkey_insert(host); + rc = true; + } + return rc; } -// Used for UDP push of a new host, returns negative value if the host/port is already present -// This is critical because UDP is connectionless and designed to be stateless -int Iperf_push_host_port_conditional (struct thread_Settings *agent) { - iperf_sockaddr *host = active_table_get_host_key(agent); - int rc = -1; +// Thread access to store a host +bool Iperf_push_host (struct thread_Settings *agent) { Mutex_Lock(&active_table.my_mutex); - if (!Iperf_host_port_present(host)) { - active_table_update(host, agent); - rc = active_table.groupid; + if (isUDP(agent) && (agent->mThreadMode == kMode_Server)) { + if (!Iperf_push_flow(&agent->peer)) { + // this is a duplicate on UDP, should just ignore + Mutex_Unlock(&active_table.my_mutex); +#if HAVE_THREAD_DEBUG + rcvfrom_peer_debug(agent, true); +#endif + return false; + } } + struct Iperf_ListEntry *this_host = Iperf_host_present(active_table_get_host_key(agent)); + if (!this_host) { + this_host = hostkey_insert(active_table_get_host_key(agent)); + active_table.groupid++; + this_host->sumreport = InitSumReport(agent, -active_table.groupid, false); + this_host->sumreport->info.common->transferID = -active_table.groupid; +#if HAVE_THREAD_DEBUG + active_table_show_entry("new sum report", this_host , 0); +#endif + } + agent->mSumReport = this_host->sumreport; + this_host->thread_count++; + IncrSumReportRefCounter(this_host->sumreport); + this_host->socket = agent->mSock; +#if HAVE_THREAD_DEBUG + active_table_show_entry("bind sum report", this_host, 0); +#endif Mutex_Unlock(&active_table.my_mutex); - return (rc); + return true; } /* * Remove a host from the table */ void Iperf_remove_host (struct thread_Settings *agent) { - iperf_sockaddr *del = active_table_get_host_key(agent); + iperf_sockaddr *del; // remove_list_entry(entry) { // indirect = &head; // while ((*indirect) != entry) { @@ -172,7 +209,25 @@ void Iperf_remove_host (struct thread_Settings *agent) { // } // *indirect = entry->next Mutex_Lock(&active_table.my_mutex); - Iperf_ListEntry **tmp = &active_table.root; + // Delete any flow entries first + if (isUDP(agent)) { + del = &agent->peer; + Iperf_ListEntry **tmp = &active_table.flow_root; + while ((*tmp) && !(SockAddr_are_Equal(&(*tmp)->host, del))) { + tmp = &(*tmp)->next; + } + if (*tmp) { + Iperf_ListEntry *remove = (*tmp); +#if HAVE_THREAD_DEBUG + active_table.flow_count--; +#endif + *tmp = remove->next; + delete remove; + } + } + + del = active_table_get_host_key(agent); + Iperf_ListEntry **tmp = &active_table.sum_root; while ((*tmp) && !(SockAddr_Hostare_Equal(&(*tmp)->host, del))) { #if HAVE_THREAD_DEBUG active_table_show_compare("miss", *tmp, del, "client ip"); @@ -182,16 +237,16 @@ void Iperf_remove_host (struct thread_Settings *agent) { if (*tmp) { if (--(*tmp)->thread_count == 0) { Iperf_ListEntry *remove = (*tmp); - active_table.count--; agent->mSumReport = NULL; #if HAVE_THREAD_DEBUG + active_table.sum_count--; active_table_show_entry("delete", remove, 1); #endif *tmp = remove->next; - FreeSumReport(remove->sum_report); + FreeSumReport(remove->sumreport); delete remove; } else { - DecrSumReportRefCounter((*tmp)->sum_report); + DecrSumReportRefCounter((*tmp)->sumreport); #if HAVE_THREAD_DEBUG active_table_show_entry("decr", (*tmp), 1); #endif @@ -204,55 +259,60 @@ void Iperf_remove_host (struct thread_Settings *agent) { * Destroy the table */ void Iperf_destroy_active_table () { - Iperf_ListEntry *itr1 = active_table.root, *itr2; + Iperf_ListEntry *itr1 = active_table.sum_root, *itr2; + while (itr1 != NULL) { + itr2 = itr1->next; + delete itr1; + itr1 = itr2; + } + itr1 = active_table.flow_root; while (itr1 != NULL) { itr2 = itr1->next; delete itr1; itr1 = itr2; } Mutex_Destroy(&active_table.my_mutex); - active_table.root = NULL; - active_table.count = 0; - active_table.total_count = 0; + active_table.sum_root = NULL; +#if HAVE_THREAD_DEBUG + active_table.sum_count = 0; +#endif } /* * Check if the host and port are present in the active table */ -bool Iperf_host_port_present (iperf_sockaddr *find) { - Iperf_ListEntry *itr = active_table.root; - bool rc = false; +struct Iperf_ListEntry* Iperf_flow_present (iperf_sockaddr *find) { + Iperf_ListEntry *itr = active_table.flow_root; while (itr != NULL) { if (SockAddr_are_Equal(&itr->host, find)) { #if HAVE_THREAD_DEBUG - active_table_show_compare("match", itr, find, "client ip/port"); + active_table_show_compare("match host/port", itr, find, "client ip/port"); #endif - rc = true; break; } else { #if HAVE_THREAD_DEBUG - active_table_show_compare("miss", itr, find, "client ip/port"); + active_table_show_compare("miss host/port", itr, find, "client ip/port"); #endif itr = itr->next; } } - return rc; + return itr; } /* * Check if the host is present in the active table */ static Iperf_ListEntry* Iperf_host_present (iperf_sockaddr *find) { - Iperf_ListEntry *itr = active_table.root; + Iperf_ListEntry *itr = active_table.sum_root; while (itr != NULL) { if (SockAddr_Hostare_Equal(&itr->host, find)) { #if HAVE_THREAD_DEBUG - active_table_show_compare("match", itr, find, "client ip"); + active_table_show_compare("match host", itr, find, "client ip"); #endif break; } else { #if HAVE_THREAD_DEBUG - active_table_show_compare("miss", itr, find, "client ip"); + active_table_show_compare("miss host", itr, find, "client ip"); #endif itr = itr->next; } @@ -23,6 +23,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 7 6 5 4 3 2 1 0 + * +--+--+--+--+--+--+--+--+ + * | DSCP | ECN | + * +--+--+--+--+--+--+--+--+ + * */ #if HAVE_CONFIG_H @@ -137,8 +143,8 @@ static const struct { { "nqb2", IPTOS_DSCP_NQB2 }, { "ac_be", IPTOS_DSCP_CS0 }, { "ac_bk", IPTOS_DSCP_CS1 }, - { "ac_vi", IPTOS_DSCP_CS4 }, - { "ac_vo", IPTOS_DSCP_EF }, + { "ac_vi", IPTOS_DSCP_CS5 }, + { "ac_vo", IPTOS_DSCP_CS6 }, { "lowdelay", IPTOS_LOWDELAY }, { "throughput", IPTOS_THROUGHPUT }, { "reliability", IPTOS_RELIABILITY }, diff --git a/src/histogram.c b/src/histogram.c index 3a9b82c..f00ffc7 100644 --- a/src/histogram.c +++ b/src/histogram.c @@ -47,17 +47,20 @@ */ #include "headers.h" #include "histogram.h" +#include "Locale.h" +#include "util.h" #ifdef HAVE_THREAD_DEBUG // needed for thread_debug #include "Thread.h" #endif struct histogram *histogram_init(unsigned int bincount, unsigned int binwidth, float offset, float units,\ - double ci_lower, double ci_upper, unsigned int id, char *name) { + double ci_lower, double ci_upper, unsigned int id, char *name, bool Omit) { struct histogram *this = (struct histogram *) malloc(sizeof(struct histogram)); if (!this) { fprintf(stderr,"Malloc failure in histogram init\n"); return(NULL); } + this->Omit = Omit; if (!bincount) bincount = 1000; this->mybins = (unsigned int *) malloc(sizeof(unsigned int) * bincount); @@ -196,7 +199,7 @@ void histogram_print(struct histogram *h, double start, double end) { histogram_clear(h->prev); } if (!h->prev) { - h->prev = histogram_init(h->bincount, h->binwidth, h->offset, h->units, h->ci_lower, h->ci_upper, h->id, h->myname); + h->prev = histogram_init(h->bincount, h->binwidth, h->offset, h->units, h->ci_lower, h->ci_upper, h->id, h->myname, h->Omit); } int n = 0, ix, delta, lowerci, upperci, outliercnt, fence_lower, fence_upper, upper3stdev; int running=0; @@ -258,12 +261,18 @@ void histogram_print(struct histogram *h, double start, double end) { fprintf(stdout, "%s (%.2f/%.2f/99.7%%=%d/%d/%d,Outliers=%d,obl/obu=%d/%d)", \ h->outbuf, h->ci_lower, h->ci_upper, lowerci, upperci, upper3stdev, outliercnt, oob_l, oob_u); if (!h->final && (h->maxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) { - fprintf(stdout, " (%0.3f ms/%ld.%ld)\n", (h->maxval * 1e3), (long) h->maxts.tv_sec, (long) h->maxts.tv_usec); + fprintf(stdout, " (%0.3f ms/%ld.%ld)", (h->maxval * 1e3), (long) h->maxts.tv_sec, (long) h->maxts.tv_usec); + if (TimeDifference(h->prev->maxts, h->maxts) > 0) { + fprintf(stdout, "(clock_err)"); + } h->maxbin = -1; h->maxval = 0; + h->prev->maxts.tv_sec = 0; + h->prev->maxts.tv_usec = 0; + h->maxts.tv_sec = 0; + h->maxts.tv_usec = 0; } else if (h->final && (h->fmaxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) { - fprintf(stdout, " (%0.3f ms/%ld.%ld)\n", (h->fmaxval * 1e3), (long) h->fmaxts.tv_sec, (long) h->fmaxts.tv_usec); - } else { - fprintf(stdout, "\n"); + fprintf(stdout, " (%0.3f ms/%ld.%ld)", (h->fmaxval * 1e3), (long) h->fmaxts.tv_sec, (long) h->fmaxts.tv_usec); } + fprintf(stdout, "%s\n", (h->Omit ? report_omitted : "")); } diff --git a/src/iperf_formattime.c b/src/iperf_formattime.c index df65650..f0e8d1d 100644 --- a/src/iperf_formattime.c +++ b/src/iperf_formattime.c @@ -50,51 +50,53 @@ #include "iperf_formattime.h" inline void iperf_formattime (char *timestr, int buflen, struct timeval timestamp, bool prec_ms, bool utc_time, enum TimeFormatType ftype) { - struct tm ts ; - ts = (utc_time ? *gmtime(×tamp.tv_sec) : *localtime(×tamp.tv_sec)); - switch (ftype) { - case YearThruSec: - strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts); - if (prec_ms) { + if (buflen > 0) { + struct tm ts ; + ts = (utc_time ? *gmtime(×tamp.tv_sec) : *localtime(×tamp.tv_sec)); + switch (ftype) { + case YearThruSec: + strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts); + if (prec_ms) { + int currlen = strlen(timestr); + if (currlen > 5) { + snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + } + } + break; + case YearThruSecTZ: + strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts); int currlen = strlen(timestr); - if (currlen > 5) { - snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + if (prec_ms) { + if (currlen > 5) { + snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + currlen = strlen(timestr); + } } - } - break; - case YearThruSecTZ: - strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts); - int currlen = strlen(timestr); - if (prec_ms) { - if (currlen > 5) { - snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); - currlen = strlen(timestr); + if ((buflen - currlen) > 5) { + strftime((timestr + currlen), (buflen - currlen), " (%Z)", &ts); } - } - if ((buflen - currlen) > 5) { - strftime((timestr + currlen), (buflen - currlen), " (%Z)", &ts); - } - break; - case CSV: - strftime(timestr, buflen, "%Y%m%d%H%M%S", &ts); - if (prec_ms) { - int currlen = strlen(timestr); - if (currlen > 5) { - snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + break; + case CSV: + strftime(timestr, buflen, "%Y%m%d%H%M%S", &ts); + if (prec_ms) { + int currlen = strlen(timestr); + if (currlen > 5) { + snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + } } - } - break; - case CSVTZ: - strftime(timestr, buflen, "%z:%Y%m%d%H%M%S", &ts); - if (prec_ms) { - int currlen = strlen(timestr); - if (currlen > 5) { - snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + break; + case CSVTZ: + strftime(timestr, buflen, "%z:%Y%m%d%H%M%S", &ts); + if (prec_ms) { + int currlen = strlen(timestr); + if (currlen > 5) { + snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000)); + } } + break; + default: + FAIL_exit(1, "iperf_formattime program error"); } - break; - default: - FAIL_exit(1, "iperf_formattime program error"); + timestr[buflen - 1] = '\0'; // make sure string is null terminated } - timestr[buflen - 1] = '\0'; // make sure string is null terminated } diff --git a/src/iperf_multicast_api.c b/src/iperf_multicast_api.c new file mode 100644 index 0000000..48c0184 --- /dev/null +++ b/src/iperf_multicast_api.c @@ -0,0 +1,386 @@ +/*--------------------------------------------------------------- + * Copyright (c) 2023 + * Broadcom Corporation + * All Rights Reserved. + *--------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and + * the following disclaimers. + * + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimers in the documentation and/or other materials + * provided with the distribution. + * + * + * Neither the name of Broadcom Coporation, + * nor the names of its contributors may be used to endorse + * or promote products derived from this Software without + * specific prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * ________________________________________________________________ + * + * iperf_multicast_api.c + * pull iperf multicast code for maitainability + * + * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com) + * + * + * Joins the multicast group or source and group (SSM S,G) + * + * taken from: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.hale001/ipv6d0141001708.htm + * + * Multicast function IPv4 IPv6 Protocol-independent + * ================== ==== ==== ==================== + * Level of specified option on setsockopt()/getsockopt() IPPROTO_IP IPPROTO_IPV6 IPPROTO_IP or IPPROTO_IPV6 + * Join a multicast group IP_ADD_MEMBERSHIP IPV6_JOIN_GROUP MCAST_JOIN_GROUP + * Leave a multicast group or leave all sources of that + * multicast group IP_DROP_MEMBERSHIP IPV6_LEAVE_GROUP MCAST_LEAVE_GROUP + * Select outbound interface for sending multicast datagrams IP_MULTICAST_IF IPV6_MULTICAST_IF NA + * Set maximum hop count IP_MULTICAST_TTL IPV6_MULTICAST_HOPS NA + * Enable multicast loopback IP_MULTICAST_LOOP IPV6_MULTICAST_LOOP NA + * Join a source multicast group IP_ADD_SOURCE_MEMBERSHIP NA MCAST_JOIN_SOURCE_GROUP + * Leave a source multicast group IP_DROP_SOURCE_MEMBERSHIP NA MCAST_LEAVE_SOURCE_GROUP + * Block data from a source to a multicast group IP_BLOCK_SOURCE NA MCAST_BLOCK_SOURCE + * Unblock a previously blocked source for a multicast group IP_UNBLOCK_SOURCE NA MCAST_UNBLOCK_SOURCE + * + * + * Reminder: The os will decide which version of IGMP or MLD to use. This may be controlled by system settings, e.g.: + * + * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep mld | grep force + * net.ipv6.conf.all.force_mld_version = 0 + * net.ipv6.conf.default.force_mld_version = 0 + * net.ipv6.conf.lo.force_mld_version = 0 + * net.ipv6.conf.eth0.force_mld_version = 0 + * + * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep igmp | grep force + * net.ipv4.conf.all.force_igmp_version = 0 + * net.ipv4.conf.default.force_igmp_version = 0 + * net.ipv4.conf.lo.force_igmp_version = 0 + * net.ipv4.conf.eth0.force_igmp_version = 0 + * + * ------------------------------------------------------------------- */ +#include "headers.h" +#include "Settings.hpp" +#include "iperf_multicast_api.h" +#include "SocketAddr.h" +#include "util.h" + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +static unsigned int mcast_iface (struct thread_Settings *inSettings) { + unsigned int iface=0; + /* Set the interface or any */ + if (inSettings->mIfrname) { +#if HAVE_NET_IF_H && !WIN32 + iface = if_nametoindex(inSettings->mIfrname); + FAIL_errno(!iface, "mcast if_nametoindex", inSettings); +#else + fprintf(stderr, "multicast bind to device not supported on this platform\n"); +#endif + } + return iface; +} + + +// IP_MULTICAST_ALL is on be default, disable it here. +// If set to 1, the socket will receive messages from all the groups that have been joined +// globally on the whole system. Otherwise, it will deliver messages only from the +// groups that have been explicitly joined (for example via the IP_ADD_MEMBERSHIP option) +// on this particular socket. +#if HAVE_MULTICAST_ALL_DISABLE +static int iperf_multicast_all_disable (struct thread_Settings *inSettings) { + int rc = 0; +#if HAVE_DECL_IP_MULTICAST_ALL + int mc_all = 0; + rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all)); + FAIL_errno(rc == SOCKET_ERROR, "ip_multicast_all", inSettings); +#endif + return rc; +} +#endif + +// This is the older mulitcast join code. Both SSM and binding the +// an interface requires the newer socket options. Using the older +// code here will maintain compatiblity with previous iperf versions +static int iperf_multicast_join_v4_legacy (struct thread_Settings *inSettings) { +#if HAVE_DECL_IP_ADD_MEMBERSHIP +#if (HAVE_STRUCT_IP_MREQ) || (HAVE_STRUCT_IP_MREQN) +#if HAVE_STRUCT_IP_MREQ + struct ip_mreq mreq; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + size_t len = sizeof(struct ip_mreq); +#elif HAVE_STRUCT_IP_MREQN + // struct ip_mreqn { + // struct in_addr imr_multiaddr; /* IP multicast address of group */ + // struct in_addr imr_interface; /* local IP address of interface */ + // int imr_ifindex; /* interface index */ + // } + struct ip_mreqn mreq; + size_t len = sizeof(struct ip_mreqn); + mreq.imr_address.s_addr = htonl(INADDR_ANY); + mreq.imr_ifindex = mcast_iface(inSettings); +#endif + memcpy(&mreq.imr_multiaddr, SockAddr_get_in_addr(&inSettings->multicast_group), sizeof(mreq.imr_multiaddr)); + int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char*)(&mreq), len); + FAIL_errno(rc == SOCKET_ERROR, "multicast join", inSettings); +#if HAVE_MULTICAST_ALL_DISABLE + iperf_multicast_all_disable(inSettings); +#endif + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#endif +#endif + return IPERF_MULTICAST_JOIN_UNSUPPORTED; +} + +static int iperf_multicast_join_v4_pi (struct thread_Settings *inSettings) { +#if HAVE_DECL_MCAST_JOIN_GROUP + int rc = -1; + struct group_req group_req; + + memset(&group_req, 0, sizeof(struct group_req)); + memcpy(&group_req.gr_group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in)); + group_req.gr_interface = mcast_iface(inSettings); + group_req.gr_group.ss_family = AF_INET; + rc = setsockopt(inSettings->mSock, IPPROTO_IP, MCAST_JOIN_GROUP, (const char *)(&group_req), + (socklen_t) sizeof(struct group_source_req)); + FAIL_errno(rc == SOCKET_ERROR, "mcast v4 join group pi", inSettings); + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#else + return IPERF_MULTICAST_JOIN_UNSUPPORTED; +#endif +} + + +static int iperf_multicast_join_v6 (struct thread_Settings *inSettings) { +#if (HAVE_DECL_IPV6_JOIN_GROUP || HAVE_DECL_IPV6_ADD_MEMBERSHIP) +#if HAVE_STRUCT_IPV6_MREQ + struct ipv6_mreq mreq; + memcpy(&mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr(&inSettings->multicast_group), sizeof(mreq.ipv6mr_multiaddr)); + mreq.ipv6mr_interface = mcast_iface(inSettings); +#if HAVE_DECL_IPV6_JOIN_GROUP + int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_JOIN_GROUP, \ + (char*)(&mreq), sizeof(mreq)); +#else + int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, \ + (char*)(&mreq), sizeof(mreq)); +#endif + FAIL_errno(rc == SOCKET_ERROR, "multicast v6 join", inSettings); + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#endif +#endif + return IPERF_MULTICAST_JOIN_UNSUPPORTED; + +} + +static int iperf_multicast_join_v6_pi (struct thread_Settings *inSettings) { +#if HAVE_DECL_MCAST_JOIN_GROUP + int rc = -1; + struct group_req group_req; + + memset(&group_req, 0, sizeof(struct group_req)); + memcpy(&group_req.gr_group, (struct sockaddr_in6 *)(&inSettings->multicast_group), sizeof(struct sockaddr_in6)); + group_req.gr_interface = mcast_iface(inSettings); + group_req.gr_group.ss_family = AF_INET6; + rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, MCAST_JOIN_GROUP, (const char *)(&group_req), + (socklen_t) sizeof(struct group_source_req)); + FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group", inSettings); + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#endif + return IPERF_MULTICAST_JOIN_UNSUPPORTED; +} + + +static int iperf_multicast_ssm_join_v4 (struct thread_Settings *inSettings) { +#if HAVE_SSM_MULTICAST + int rc; + struct sockaddr_in *group; + struct sockaddr_in *source; + + // Fill out both structures because we don't which one will succeed + // and both may need to be tried +#if HAVE_STRUCT_IP_MREQ_SOURCE + struct ip_mreq_source imr; + memset (&imr, 0, sizeof (imr)); +#endif +#if HAVE_STRUCT_GROUP_SOURCE_REQ + struct group_source_req group_source_req; + memset(&group_source_req, 0, sizeof(struct group_source_req)); + group_source_req.gsr_interface = mcast_iface(inSettings); + group=(struct sockaddr_in *)(&group_source_req.gsr_group); + source=(struct sockaddr_in *)(&group_source_req.gsr_source); +#else + struct sockaddr_in imrgroup; + struct sockaddr_in imrsource; + group = &imrgroup; + source = &imrsource; +#endif + source->sin_family = AF_INET; + group->sin_family = AF_INET; + /* Set the group and SSM source*/ + memcpy(group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in)); + memcpy(source, (struct sockaddr_in *)(&inSettings->multicast_group_source), sizeof(struct sockaddr_in)); +#if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + source->sin_len = group->sin_len; +#endif + source->sin_port = 0; /* Ignored */ + rc = -1; + +#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP + rc = setsockopt(inSettings->mSock,IPPROTO_IP,MCAST_JOIN_SOURCE_GROUP, (const char *)(&group_source_req), \ + sizeof(struct group_source_req)); + WARN(rc == SOCKET_ERROR, "mcast v4 join ssm join_src"); +#endif + +#if (HAVE_DECL_IP_ADD_SOURCE_MEMBERSHIP && HAVE_STRUCT_IP_MREQ_SOURCE) + // Some operating systems will have MCAST_JOIN_SOURCE_GROUP but still fail + // In those cases try the IP_ADD_SOURCE_MEMBERSHIP + if (rc < 0) { +#if HAVE_STRUCT_IP_MREQ_SOURCE_IMR_MULTIADDR_S_ADDR + imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr; + imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr; +#else + // Some Android versions declare mreq_source without an s_addr + imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr.s_addr; + imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr.s_addr; +#endif + rc = setsockopt (inSettings->mSock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)(&imr), sizeof (imr)); + FAIL_errno(rc == SOCKET_ERROR, "mcast v4 join ssm add_src", inSettings); + } +#endif + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#endif + return IPERF_MULTICAST_JOIN_UNSUPPORTED; +} + +static int iperf_multicast_ssm_join_v6 (struct thread_Settings *inSettings) { +#if (HAVE_IPV6_MULTICAST && HAVE_SSM_MULTICAST && HAVE_DECL_MCAST_JOIN_SOURCE_GROUP) + int rc; + + // Here it's either an SSM S,G multicast join or a *,G with an interface specifier + // Use the newer socket options when these are specified + struct group_source_req group_source_req; + struct sockaddr_in6 *group; + struct sockaddr_in6 *source; + + memset(&group_source_req, 0, sizeof(struct group_source_req)); + + group_source_req.gsr_interface = mcast_iface(inSettings); + group=(struct sockaddr_in6*)(&group_source_req.gsr_group); + source=(struct sockaddr_in6*)(&group_source_req.gsr_source); + source->sin6_family = AF_INET6; + group->sin6_family = AF_INET6; + /* Set the group and SSM source*/ + memcpy(group, (struct sockaddr_in *)(&inSettings->multicast_group), sizeof(struct sockaddr_in6)); + memcpy(source, (struct sockaddr_in *)(&inSettings->multicast_group_source), sizeof(struct sockaddr_in6)); + group->sin6_port = 0; /* Ignored */ +#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + source->sin6_len = group->sin6_len; +#endif + rc = setsockopt(inSettings->mSock,IPPROTO_IPV6,MCAST_JOIN_SOURCE_GROUP, (const char *)(&group_source_req), + sizeof(struct group_source_req)); + FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join source group", inSettings); + return ((rc == 0) ? IPERF_MULTICAST_JOIN_SUCCESS : IPERF_MULTICAST_JOIN_FAIL); +#endif + return IPERF_MULTICAST_JOIN_UNSUPPORTED; +} + +enum McastJoinResponse iperf_multicast_join (struct thread_Settings *inSettings) { + int rc = IPERF_MULTICAST_JOIN_FAIL; + if (!isSSMMulticast(inSettings)) { + // *.G join + if (!SockAddr_isIPv6(&inSettings->multicast_group)) { + if (!mcast_iface(inSettings)) { + rc = iperf_multicast_join_v4_legacy(inSettings); + } + if (rc != IPERF_MULTICAST_JOIN_SUCCESS) { + rc = iperf_multicast_join_v4_pi(inSettings); + } + } else { + rc = iperf_multicast_join_v6(inSettings); + if (rc != IPERF_MULTICAST_JOIN_SUCCESS) { + rc = iperf_multicast_join_v6_pi(inSettings); + } + } + } else { + // SSM or S,G join + if (!SockAddr_isIPv6(&inSettings->multicast_group)) { + rc = iperf_multicast_ssm_join_v4(inSettings); + } else { + rc = iperf_multicast_ssm_join_v6(inSettings); + } + } + return rc; +} + +static void iperf_multicast_sync_ifrname (struct thread_Settings *inSettings) { + if (inSettings->mIfrname && !inSettings->mIfrnametx) { + int len = strlen(inSettings->mIfrname); + inSettings->mIfrnametx = calloc((len + 1), sizeof(char)); + if (inSettings->mIfrnametx) { + strncpy(inSettings->mIfrnametx, inSettings->mIfrname, len+1); + } + } + if (!inSettings->mIfrname && inSettings->mIfrnametx) { + int len = strlen(inSettings->mIfrnametx); + inSettings->mIfrname = calloc((len + 1), sizeof(char)); + if (inSettings->mIfrname) { + strncpy(inSettings->mIfrname, inSettings->mIfrnametx, len+1); + } + } +} + +bool iperf_multicast_sendif_v4 (struct thread_Settings *inSettings) { + bool result = false; +#if HAVE_DECL_IP_MULTICAST_IF + struct in_addr interface_addr; + memcpy(&interface_addr, SockAddr_get_in_addr(&inSettings->local), sizeof(interface_addr)); + int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_IF, \ + (char*)(&interface_addr), sizeof(interface_addr)); + if ((rc != SOCKET_ERROR) && SockAddr_Ifrname(inSettings)) { + iperf_multicast_sync_ifrname(inSettings); + } + FAIL_errno(rc == SOCKET_ERROR, "v4 multicast if", inSettings); + result = ((rc == 0) ? true : false); +#endif + return result; +} + +bool iperf_multicast_sendif_v6 (struct thread_Settings *inSettings) { + int result = false; +#if HAVE_DECL_IPV6_MULTICAST_IF && HAVE_NET_IF_H && !WIN32 + if (inSettings->mIfrnametx) { + unsigned int ifindex = if_nametoindex(inSettings->mIfrnametx); + if (ifindex) { + int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + if (rc == 0) { + iperf_multicast_sync_ifrname(inSettings); + result = true; + } + } + } +#endif + return result; +} diff --git a/src/isochronous.cpp b/src/isochronous.cpp index 573f05a..62c9703 100644 --- a/src/isochronous.cpp +++ b/src/isochronous.cpp @@ -57,7 +57,7 @@ using namespace Isochronous; FrameCounter::FrameCounter (double value, const Timestamp& start) : frequency(value) { period = static_cast<unsigned int>(1000000 / frequency); startTime = start; - nextslotTime=start; + nextslotTime = start; lastcounter = 0; slot_counter = 0; slip = 0; @@ -69,6 +69,8 @@ FrameCounter::FrameCounter (double value) : frequency(value) { if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) WARN_errno(1, "SetThreadPriority"); #endif + startTime.setnow(); + nextslotTime = startTime; period = static_cast<unsigned int>(1000000 / frequency); // unit us lastcounter = 0; slot_counter = 0; @@ -119,23 +121,23 @@ int FrameCounter::mySetWaitableTimer (long delay_time) { #endif #if HAVE_CLOCK_NANOSLEEP -unsigned int FrameCounter::wait_tick (long *sched_err) { +unsigned int FrameCounter::wait_tick (long *sched_err, bool sync_strict) { Timestamp now; int rc = true; if (!slot_counter) { slot_counter = 1; - now.setnow(); nextslotTime = now; + startTime = now; } else { - while (!now.before(nextslotTime)) { - now.setnow(); + nextslotTime.add(period); + slot_counter++; + while (now.subUsec(nextslotTime) > static_cast<long>(sync_strict ? 0 : period)) { nextslotTime.add(period); -// printf("***** next slot %ld.%ld\n",nextslotTime.getSecs(), nextslotTime.getUsecs()); slot_counter++; } - if (lastcounter && ((slot_counter - lastcounter) > 1)) { + if (lastcounter && ((slot_counter - lastcounter) > 1)) { slip++; - } + } } #ifndef WIN32 timespec txtime_ts; @@ -179,42 +181,42 @@ unsigned int FrameCounter::wait_tick (long *sched_err) { return(slot_counter); } #else -unsigned int FrameCounter::wait_tick (long *sched_err) { - long remaining; - unsigned int framecounter; - - if (!lastcounter) { - reset(); - framecounter = 1; +unsigned int FrameCounter::wait_tick (long *sched_err, bool sync_strict) { + Timestamp now; + if (!slot_counter) { + slot_counter = 1; + startTime = now; + nextslotTime = now; } else { - framecounter = get(&remaining); - if ((framecounter - lastcounter) > 1) - slip++; -// delay_loop(remaining); - remaining *= 1000; - struct timespec tv0={0,0}, tv1; - tv0.tv_sec = (remaining / BILLION); - tv0.tv_nsec += (remaining % BILLION); - if (tv0.tv_nsec >= BILLION) { - tv0.tv_sec++; - tv0.tv_nsec -= BILLION; + long remaining; + nextslotTime.add(period); + slot_counter++; + while (now.subUsec(nextslotTime) > (sync_strict ? 0 : period)) { + nextslotTime.add(period); + slot_counter++; } - Timestamp slotstart = startTime; - slot_counter = get(); - // period unit is in microseconds, convert to seconds - slotstart.add(slot_counter * period * 1e-6); - int rc = nanosleep(&tv0, &tv1); - if (sched_err) { - Timestamp actual; - *sched_err = actual.subUsec(slotstart); -// printf("**** slot %ld.%ld actual %ld.%ld %ld\n", slotstart.getSecs(), slotstart.getUsecs(), actual.getSecs(), actual.getUsecs(), *sched_err); +// printf("**** sync strict %d now %ld.%ld next %ld.%ld\n", sync_strict, now.getSecs(), now.getUsecs(), nextslotTime.getSecs(), nextslotTime.getUsecs()); + if (now.before(nextslotTime)) { + struct timespec tv0={0,0}, tv1; + get(&remaining); + remaining *= 1000; // convert to nano seconds + tv0.tv_sec = (remaining / BILLION); + tv0.tv_nsec += (remaining % BILLION); + if (tv0.tv_nsec >= BILLION) { + tv0.tv_sec++; + tv0.tv_nsec -= BILLION; + } +// printf("**** wait: nanos %ld remain %ld.%ld\n", remaining, tv0.tv_sec, tv0.tv_nsec); + int rc = nanosleep(&tv0, &tv1); + if (sched_err) { + Timestamp actual; + *sched_err = actual.subUsec(nextslotTime); + // printf("**** slot %ld.%ld actual %ld.%ld %ld\n", slotstart.getSecs(), slotstart.getUsecs(), actual.getSecs(), actual.getUsecs(), *sched_err); + } + WARN_errno((rc != 0), "nanosleep wait_tick"); } - WARN_errno((rc != 0), "nanosleep wait_tick"); ; -// printf("****** rc = %d, remain %ld.%ld\n", rc, tv1.tv_sec, tv1.tv_nsec); - framecounter ++; } - lastcounter = framecounter; - return(framecounter); + return(slot_counter); } #endif inline unsigned int FrameCounter::get () const { @@ -226,7 +228,7 @@ inline unsigned int FrameCounter::get (const Timestamp& slot) const { return(slot_counter + 1); // Frame counter for packets starts at 1 } -inline unsigned int FrameCounter::get (long *ticks_remaining) { +unsigned int FrameCounter::get (long *ticks_remaining) { assert(ticks_remaining != NULL); Timestamp sampleTime; // Constructor will initialize timestamp to now long usecs = -startTime.subUsec(sampleTime); @@ -234,7 +236,7 @@ inline unsigned int FrameCounter::get (long *ticks_remaining) { // figure out how many usecs before the next frame counter tick // the caller can use this to delay until the next tick *ticks_remaining = (counter * period) - usecs; - return(counter + 1); // Frame counter for packets starts at 1 + return(counter); // Frame counter for packets starts at 1 } inline Timestamp FrameCounter::next_slot () { diff --git a/src/packet_ring.c b/src/packet_ring.c index 93e7578..97a25a5 100644 --- a/src/packet_ring.c +++ b/src/packet_ring.c @@ -55,6 +55,11 @@ static int totalpacketringcount = 0; Mutex packetringdebug_mutex; #endif +// +// Initialize a packet ring between a traffic thread and the reporter thread +// Note: enable dequeue events will have the dequeue return null on an event relevant to +// the reporter thread moving to the next ring. This is needed for proper summing +// struct PacketRing * packetring_init (int count, struct Condition *awake_consumer, struct Condition *awake_producer) { assert(awake_consumer != NULL); struct PacketRing *pr = NULL; @@ -78,6 +83,8 @@ struct PacketRing * packetring_init (int count, struct Condition *awake_consumer pr->mutex_enable=1; pr->consumerdone = 0; pr->awaitcounter = 0; + pr->uplevel = HIGH; + pr->downlevel = HIGH; #ifdef HAVE_THREAD_DEBUG Mutex_Lock(&packetringdebug_mutex); totalpacketringcount++; @@ -103,7 +110,7 @@ inline void packetring_enqueue (struct PacketRing *pr, struct ReportStruct *meta { struct timeval now; static struct timeval prev={0, 0}; - gettimeofday( &now, NULL ); + TimeGetNow(now); if (!prev.tv_sec || (TimeDifference(now, prev) > 1.0)) { prev = now; thread_debug( "Not good, traffic's packet ring %p stalled per %p", (void *)pr, (void *)&pr->awake_producer); @@ -127,16 +134,18 @@ inline void packetring_enqueue (struct PacketRing *pr, struct ReportStruct *meta inline struct ReportStruct *packetring_dequeue (struct PacketRing *pr) { struct ReportStruct *packet = NULL; - if (pr->producer == pr->consumer) + if (pr->producer == pr->consumer) { return NULL; - + } int readindex; if ((pr->consumer + 1) == pr->maxcount) readindex = 0; else readindex = (pr->consumer + 1); + packet = (pr->data + readindex); - // advance the consumer pointer last + // See if the dequeue needs to detect an event so the reporter + // can move to the next packet ring pr->consumer = readindex; if (pr->mutex_enable) { // Signal the traffic thread assigned to this ring @@ -152,6 +161,10 @@ inline struct ReportStruct *packetring_dequeue (struct PacketRing *pr) { return packet; } +inline enum edgeLevel toggleLevel(enum edgeLevel level) { + return ((level == HIGH) ? LOW : HIGH); +} + inline void enqueue_ackring (struct PacketRing *pr, struct ReportStruct *metapacket) { packetring_enqueue(pr, metapacket); // Keep the latency low by signaling the consumer thread @@ -63,13 +63,16 @@ #define TRUE 1 float box_muller(void) { float x1, x2, w, y1; +#if 0 static float y2; static int generate = FALSE; /* Each iteration produces two values, if one exists use the value from previous call */ generate = !generate; if (!generate) { y1 = y2; - } else { + } else +#endif + { int loopcontrol=100; do { x1 = 2.0 * (float)rand()/(float)(RAND_MAX) - 1.0; @@ -82,7 +85,9 @@ float box_muller(void) { } else { w = sqrt( (-2.0 * logf( w ) ) / w ); y1 = x1 * w; +#if 0 y2 = x2 * w; +#endif } } return(y1); diff --git a/src/socket_io.c b/src/socket_io.c index 0475310..0f83a6c 100644 --- a/src/socket_io.c +++ b/src/socket_io.c @@ -82,7 +82,7 @@ ssize_t readn (int inSock, void *outBuf, size_t inLen) { if (errno == EINTR) nread = 0; /* interupted, call read again */ else - return -1; /* error */ + return SOCKET_ERROR; /* error */ } else if (nread == 0) break; /* EOF */ @@ -119,7 +119,7 @@ int recvn (int inSock, char *outBuf, int inLen, int flags) { // Note: use TCP fatal error codes even for UDP if (FATALTCPREADERR(errno)) { WARN_errno(1, "recvn peek"); - nread = -1; + nread = SOCKET_ERROR; sInterupted = 1; goto DONE; } @@ -151,11 +151,11 @@ int recvn (int inSock, char *outBuf, int inLen, int flags) { // Note: use TCP fatal error codes even for UDP if (FATALTCPREADERR(errno)) { WARN_errno(1, "recvn"); - nread = -1; + nread = SOCKET_ERROR; sInterupted = 1; goto DONE; } else { - nread = -2; + nread = IPERF_SOCKET_ERROR_NONFATAL; goto DONE; } #ifdef HAVE_THREAD_DEBUG @@ -179,7 +179,7 @@ int recvn (int inSock, char *outBuf, int inLen, int flags) { } DONE: return(nread); -} /* end readn */ +} /* end recvn */ /* ------------------------------------------------------------------- * Attempts to write n bytes to a socket. @@ -202,7 +202,6 @@ int writen (int inSock, const void *inBuf, int inLen, int *count) { ptr = (char*) inBuf; nleft = inLen; nwritten = 0; - *count = 0; while ((nleft > 0) && !sInterupted) { nwritten = write(inSock, ptr, nleft); @@ -212,9 +211,8 @@ int writen (int inSock, const void *inBuf, int inLen, int *count) { // check for a fatal error vs an error that should retry if ((errno != EINTR) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { nwritten = inLen - nleft; - fprintf(stdout, "FAIL: writen errno = %d\n", errno); - WARN_errno(1, "writen fatal"); - sInterupted = 1; + fprintf(stdout, "FAIL: writen errno = %d (bytes=%d)\n", errno, nwritten); +// sInterupted = 1; goto DONE; } break; diff --git a/src/stdio.c b/src/stdio.c index 3bdf668..33e58ac 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -322,6 +322,17 @@ void byte_snprintf(char* outString, int inLen, double inNum, char inFormat) { snprintf(outString, inLen, format, inNum, suffix); } /* end byte_snprintf */ + +void make_lower (char *s) { + if (s) { + int length = strlen(s); + for (int i = 0; i < length; i++) { + s[i] = tolower(s[i]); + } + } + return; +} + /* ------------------------------------------------------------------- * redirect * |