|
|
@@ -14,6 +14,8 @@
|
|
|
#include "freertos/task.h"
|
|
|
#include "esp_check.h"
|
|
|
#include "esp_log.h"
|
|
|
+#include "esp_rom_sys.h"
|
|
|
+#include "esp_timer.h"
|
|
|
#include "iperf.h"
|
|
|
|
|
|
typedef struct {
|
|
|
@@ -147,25 +149,40 @@ static void socket_recv(int recv_socket, struct sockaddr_storage listen_addr, ui
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void socket_send(int send_socket, struct sockaddr_storage dest_addr, uint8_t type)
|
|
|
+static void socket_send(int send_socket, struct sockaddr_storage dest_addr, uint8_t type, int bw_lim)
|
|
|
{
|
|
|
- bool retry = false;
|
|
|
uint8_t *buffer;
|
|
|
- uint8_t delay = 0;
|
|
|
int actual_send = 0;
|
|
|
int want_send = 0;
|
|
|
+ int period_us = -1;
|
|
|
+ int delay_us = 0;
|
|
|
+ int64_t prev_time = 0;
|
|
|
+ int64_t send_time = 0;
|
|
|
int err = 0;
|
|
|
|
|
|
buffer = s_iperf_ctrl.buffer;
|
|
|
want_send = s_iperf_ctrl.buffer_len;
|
|
|
iperf_start_report();
|
|
|
|
|
|
+ if (bw_lim > 0) {
|
|
|
+ period_us = want_send * 8 / bw_lim;
|
|
|
+ }
|
|
|
+
|
|
|
while (!s_iperf_ctrl.finish) {
|
|
|
- if (type == IPERF_TRANS_TYPE_UDP) {
|
|
|
- if (false == retry) {
|
|
|
- delay = 1;
|
|
|
+ if (period_us > 0) {
|
|
|
+ send_time = esp_timer_get_time();
|
|
|
+ if (actual_send > 0){
|
|
|
+ // Last packet "send" was successful, check how much off the previous loop duration was to the ideal send period. Result will adjust the
|
|
|
+ // next send delay.
|
|
|
+ delay_us += period_us + (int32_t)(prev_time - send_time);
|
|
|
+ } else {
|
|
|
+ // Last packet "send" was not successful. Ideally we should try to catch up the whole previous loop duration (e.g. prev_time - send_time).
|
|
|
+ // However, that's not possible since the most probable reason why the send was unsuccessful is the HW was not able to process the packet.
|
|
|
+ // Hence, we cannot queue more packets with shorter (or no) delay to catch up since we are already at the performance edge. The best we
|
|
|
+ // can do is to reset the send delay (which is probably big negative number) and start all over again.
|
|
|
+ delay_us = 0;
|
|
|
}
|
|
|
- retry = false;
|
|
|
+ prev_time = send_time;
|
|
|
}
|
|
|
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
|
|
|
actual_send = sendto(send_socket, buffer, want_send, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
|
|
|
@@ -175,17 +192,9 @@ static void socket_send(int send_socket, struct sockaddr_storage dest_addr, uint
|
|
|
if (actual_send != want_send) {
|
|
|
if (type == IPERF_TRANS_TYPE_UDP) {
|
|
|
err = iperf_get_socket_error_code(send_socket);
|
|
|
- if (err == ENOMEM) {
|
|
|
- vTaskDelay(delay);
|
|
|
- if (delay < IPERF_MAX_DELAY) {
|
|
|
- delay <<= 1;
|
|
|
- }
|
|
|
- retry = true;
|
|
|
- continue;
|
|
|
- } else {
|
|
|
- if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
|
|
|
- ESP_LOGE(TAG, "udp client send abort: err=%d", err);
|
|
|
- }
|
|
|
+ // ENOMEM is expected under heavy load => do not print it
|
|
|
+ if (err != ENOMEM) {
|
|
|
+ iperf_show_socket_error_reason("udp client send", send_socket);
|
|
|
}
|
|
|
}
|
|
|
if (type == IPERF_TRANS_TYPE_TCP) {
|
|
|
@@ -196,6 +205,10 @@ static void socket_send(int send_socket, struct sockaddr_storage dest_addr, uint
|
|
|
} else {
|
|
|
s_iperf_ctrl.actual_len += actual_send;
|
|
|
}
|
|
|
+ // The send delay may be negative, it indicates we are trying to catch up and hence to not delay the loop at all.
|
|
|
+ if (delay_us > 0) {
|
|
|
+ esp_rom_delay_us(delay_us);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -316,7 +329,7 @@ static esp_err_t iperf_run_tcp_client(void)
|
|
|
memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
|
|
|
}
|
|
|
|
|
|
- socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_TCP);
|
|
|
+ socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_TCP, s_iperf_ctrl.cfg.bw_lim);
|
|
|
exit:
|
|
|
if (client_socket != -1) {
|
|
|
shutdown(client_socket, 0);
|
|
|
@@ -423,7 +436,7 @@ static esp_err_t iperf_run_udp_client(void)
|
|
|
memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
|
|
|
}
|
|
|
|
|
|
- socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_UDP);
|
|
|
+ socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_UDP, s_iperf_ctrl.cfg.bw_lim);
|
|
|
exit:
|
|
|
if (client_socket != -1) {
|
|
|
shutdown(client_socket, 0);
|