diff --git a/components/esp_eth/src/esp_eth_mac_esp.c b/components/esp_eth/src/esp_eth_mac_esp.c index 4b13c9f120..31e697fed7 100644 --- a/components/esp_eth/src/esp_eth_mac_esp.c +++ b/components/esp_eth/src/esp_eth_mac_esp.c @@ -226,7 +226,7 @@ static esp_err_t emac_esp32_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t esp_err_t ret = ESP_OK; emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); uint32_t sent_len = emac_hal_transmit_frame(&emac->hal, buf, length); - ESP_GOTO_ON_FALSE(sent_len == length, ESP_ERR_INVALID_SIZE, err, TAG, "insufficient TX buffer size"); + ESP_GOTO_ON_FALSE(sent_len == length, ESP_ERR_NO_MEM, err, TAG, "insufficient TX buffer size"); return ESP_OK; err: return ret; diff --git a/components/lwip/port/esp32/netif/ethernetif.c b/components/lwip/port/esp32/netif/ethernetif.c index 7e1b7dbd78..3b51af804d 100644 --- a/components/lwip/port/esp32/netif/ethernetif.c +++ b/components/lwip/port/esp32/netif/ethernetif.c @@ -134,11 +134,12 @@ static err_t ethernet_low_level_output(struct netif *netif, struct pbuf *p) pbuf_free(q); } /* Check error */ - if (unlikely(ret != ESP_OK)) { - return ERR_ABRT; - } else { + if (likely(ret == ESP_OK)) { return ERR_OK; + } else if (ret == ESP_ERR_NO_MEM) { + return ERR_MEM; } + return ERR_IF; } /** diff --git a/examples/common_components/iperf/include/iperf.h b/examples/common_components/iperf/include/iperf.h index e7692d8197..72d2c62b3c 100644 --- a/examples/common_components/iperf/include/iperf.h +++ b/examples/common_components/iperf/include/iperf.h @@ -33,6 +33,7 @@ extern "C" { #define IPERF_DEFAULT_PORT 5001 #define IPERF_DEFAULT_INTERVAL 3 #define IPERF_DEFAULT_TIME 30 +#define IPERF_DEFAULT_NO_BW_LIMIT -1 #define IPERF_TRAFFIC_TASK_NAME "iperf_traffic" #define IPERF_TRAFFIC_TASK_PRIORITY 4 @@ -67,6 +68,7 @@ typedef struct { uint32_t interval; uint32_t time; uint16_t len_send_buf; + int32_t bw_lim; } iperf_cfg_t; esp_err_t iperf_start(iperf_cfg_t *cfg); diff --git a/examples/common_components/iperf/iperf.c b/examples/common_components/iperf/iperf.c index 3aaca0c9d7..09309f1417 100644 --- a/examples/common_components/iperf/iperf.c +++ b/examples/common_components/iperf/iperf.c @@ -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); diff --git a/examples/ethernet/iperf/README.md b/examples/ethernet/iperf/README.md index bdb29819cf..b48c860afc 100644 --- a/examples/ethernet/iperf/README.md +++ b/examples/ethernet/iperf/README.md @@ -149,7 +149,7 @@ I (2534456) iperf: want recv=16384 ## Suggestions of getting higher bandwidth 1. Higher MCU working frequency will get higher bandwidth. -2. Put frequency invoked function into IRAM via macro `IRAM_ATTR` in code. +2. Put frequently invoked functions into IRAM via macro `IRAM_ATTR` in code. Note that the lwIP IRAM optimization is already enabled by default. 3. Priority of iperf task may also have effect. ## Troubleshooting diff --git a/examples/ethernet/iperf/iperf_test.py b/examples/ethernet/iperf/iperf_test.py index 88a4f86657..3bc69271c9 100644 --- a/examples/ethernet/iperf/iperf_test.py +++ b/examples/ethernet/iperf/iperf_test.py @@ -24,6 +24,8 @@ except ImportError: # Only used for type annotations pass +NO_BANDWIDTH_LIMIT = -1 # iperf send bandwith is not limited + class IperfTestUtilityEth(IperfUtility.IperfTestUtility): """ iperf test implementation """ @@ -46,7 +48,7 @@ class IperfTestUtilityEth(IperfUtility.IperfTestUtility): self.dut.write('ethernet start') time.sleep(10) self.dut.write('ethernet info') - dut_ip = self.dut.expect(re.compile(r'ETHIP: ([\d.]+)'))[0] + dut_ip = self.dut.expect(re.compile(r'ETHIP: (\d+[.]\d+[.]\d+[.]\d+)'))[0] rssi = 0 return dut_ip, rssi @@ -78,7 +80,10 @@ def test_ethernet_throughput_basic(env, _): # type: (Any, Any) -> None # 3. run test for TCP Tx, Rx and UDP Tx, Rx - test_utility.run_all_cases(0) + test_utility.run_test('tcp', 'tx', 0, NO_BANDWIDTH_LIMIT) + test_utility.run_test('tcp', 'rx', 0, NO_BANDWIDTH_LIMIT) + test_utility.run_test('udp', 'tx', 0, 80) + test_utility.run_test('udp', 'rx', 0, NO_BANDWIDTH_LIMIT) # 4. log performance and compare with pass standard performance_items = [] diff --git a/examples/ethernet/iperf/main/cmd_ethernet.c b/examples/ethernet/iperf/main/cmd_ethernet.c index fbe89bc03f..4a42aeb1de 100644 --- a/examples/ethernet/iperf/main/cmd_ethernet.c +++ b/examples/ethernet/iperf/main/cmd_ethernet.c @@ -70,6 +70,7 @@ static struct { struct arg_int *length; struct arg_int *interval; struct arg_int *time; + struct arg_int *bw_limit; struct arg_lit *abort; struct arg_end *end; } iperf_args; @@ -167,6 +168,16 @@ static int eth_cmd_iperf(int argc, char **argv) } } + /* iperf -b */ + if (iperf_args.bw_limit->count == 0) { + cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT; + } else { + cfg.bw_lim = iperf_args.bw_limit->ival[0]; + if (cfg.bw_lim <= 0) { + cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT; + } + } + printf("mode=%s-%s sip=%d.%d.%d.%d:%d, dip=%d.%d.%d.%d:%d, interval=%d, time=%d\r\n", cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp", cfg.flag & IPERF_FLAG_SERVER ? "server" : "client", @@ -345,6 +356,7 @@ void register_ethernet(void) iperf_args.interval = arg_int0("i", "interval", "", "seconds between periodic bandwidth reports"); iperf_args.time = arg_int0("t", "time", "