diff --git a/configure.ac b/configure.ac index a23c3bcf4..073d07dcc 100644 --- a/configure.ac +++ b/configure.ac @@ -342,5 +342,14 @@ AC_SEARCH_LIBS(clock_gettime, [rt posix4]) # Check for clock_gettime support AC_CHECK_FUNCS([clock_gettime]) +# Check if we need -lrt for nanosleep +AC_SEARCH_LIBS(nanosleep, [rt posix4]) +# Check for nanosleep support +AC_CHECK_FUNCS([nanosleep]) +# Check if we need -lrt for clock_nanosleep +AC_SEARCH_LIBS(clock_nanosleep, [rt posix4]) +# Check for clock_nanosleep support +AC_CHECK_FUNCS([clock_nanosleep]) + AC_CONFIG_FILES([Makefile src/Makefile src/version.h examples/Makefile iperf3.spec]) AC_OUTPUT diff --git a/src/iperf_api.c b/src/iperf_api.c index dafbadf65..565e0b0aa 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1881,17 +1881,73 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) struct iperf_time temp_time; double seconds; uint64_t bits_per_second; + int64_t missing_rate; + uint64_t bits_sent; + +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + struct timespec nanosleep_time; + int64_t time_to_green_light, delta_bits; + int ret; +#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP) */ +#if defined(HAVE_CLOCK_NANOSLEEP) + int64_t ns; +#endif /* HAVE_CLOCK_NANOSLEEP */ if (sp->test->done || sp->test->settings->rate == 0) return; iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time); seconds = iperf_time_in_secs(&temp_time); - bits_per_second = sp->result->bytes_sent * 8 / seconds; - if (bits_per_second < sp->test->settings->rate) { + bits_sent = sp->result->bytes_sent * 8; + bits_per_second = bits_sent / seconds; + missing_rate = sp->test->settings->rate - bits_per_second; + + if (missing_rate > 0) { sp->green_light = 1; } else { sp->green_light = 0; } + +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + // If estimated time to next send is large enough, sleep instead of just CPU looping until green light is set + if (missing_rate < 0) { + delta_bits = bits_sent - (seconds * sp->test->settings->rate); + // Calclate time until next data send is required + time_to_green_light = (SEC_TO_NS * delta_bits / sp->test->settings->rate); + // Whether shouuld wait before next send + if (time_to_green_light >= 0) { +#if defined(HAVE_CLOCK_NANOSLEEP) + if (clock_gettime(CLOCK_MONOTONIC, &nanosleep_time) == 0) { + // Calculate absolute end of sleep time + ns = nanosleep_time.tv_nsec + time_to_green_light; + if (ns < SEC_TO_NS) { + nanosleep_time.tv_nsec = ns; + } else { + nanosleep_time.tv_sec += ns / SEC_TO_NS; + nanosleep_time.tv_nsec = ns % SEC_TO_NS; + } + // Sleep until average baud rate reaches the target value + while((ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &nanosleep_time, NULL)) == EINTR); + if (ret == 0) { + sp->green_light = 1; + } + } + +#else /* HAVE_NANOSLEEP */ + nanosleep_time.tv_sec = 0; + // Sleep until average baud rate reaches the target value or intrupt / error + do { + // nansleep() time should be less than 1 sec + nanosleep_time.tv_nsec = (time_to_green_light >= SEC_TO_NS) ? SEC_TO_NS - 1 : time_to_green_light; + time_to_green_light -= nanosleep_time.tv_nsec; + ret = nanosleep(&nanosleep_time, NULL); + } while (ret == 0 && time_to_green_light > 0); + if (ret == 0) { + sp->green_light = 1; + } +#endif /* HAVE_CLOCK_NANOSLEEP else HAVE_NANOSLEEP */ + } + } +#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */ } /* Verify that average traffic is not greater than the specified limit */ @@ -1982,7 +2038,11 @@ iperf_send_mt(struct iperf_stream *sp) if (!streams_active) break; } +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + if (!sp->green_light) { /* Should check if green ligh can be set, as pacing timer is not supported in this case */ +#else /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ if (!no_throttle_check) { /* Throttle check if was not checked for each send */ +#endif /* HAVE_CLOCK_NANOSLEEP, HAVE_NANOSLEEP */ iperf_time_now(&now); if (sp->sender) iperf_check_throttle(sp, &now); @@ -2032,6 +2092,7 @@ iperf_init_test(struct iperf_test *test) return 0; } +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) static void send_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { @@ -2043,20 +2104,25 @@ send_timer_proc(TimerClientData client_data, struct iperf_time *nowP) */ iperf_check_throttle(sp, nowP); } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ int iperf_create_send_timers(struct iperf_test * test) { - struct iperf_time now; struct iperf_stream *sp; +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) TimerClientData cd; + struct iperf_time now; if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ + SLIST_FOREACH(sp, &test->streams, streams) { sp->green_light = 1; +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) if (test->settings->rate != 0 && sp->sender) { cd.p = sp; sp->send_timer = tmr_create(NULL, send_timer_proc, cd, test->settings->pacing_timer, 1); @@ -2065,6 +2131,7 @@ iperf_create_send_timers(struct iperf_test * test) return -1; } } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ } return 0; } diff --git a/src/iperf_locale.c b/src/iperf_locale.c index cfdba5826..5c6e66dfd 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -163,7 +163,12 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n" " (default %d Mbit/sec for UDP, unlimited for TCP)\n" " (optional slash and packet count for burst mode)\n" +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) " --pacing-timer #[KMG] set the timing for pacing, in microseconds (default %d)\n" +#else + " --pacing-timer #[KMG] set the Server timing for pacing, in microseconds (default %d)\n" + " (used by the server only if this option is in its help message)\n" +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ #if defined(HAVE_SO_MAX_PACING_RATE) " --fq-rate #[KMG] enable fair-queuing based socket pacing in\n" " bits/sec (Linux only)\n"