diff options
author | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2017-01-17 15:20:24 +0200 |
---|---|---|
committer | Jukka Rissanen <jukka.rissanen@linux.intel.com> | 2017-02-03 15:59:17 +0200 |
commit | 48b9b96d3f20b577bc9e072af0f4b3981c05a456 (patch) | |
tree | 4c6fd66a2d6ce6c313557c9a1448c3addc2211a9 /samples | |
parent | 56afb1065ac5d0756935e00e359697c11003d31b (diff) |
samples: zperf: Use native IP stack for TCP support
Enable zperf code to support TCP using the native IP stack
when testing the network throughput.
Change-Id: I3e58754cfff65525ad15e63adf57f1ea22e4559d
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Diffstat (limited to 'samples')
-rw-r--r-- | samples/net/zperf/README.rst | 38 | ||||
-rw-r--r-- | samples/net/zperf/src/zperf.h | 1 | ||||
-rw-r--r-- | samples/net/zperf/src/zperf_internal.h | 1 | ||||
-rw-r--r-- | samples/net/zperf/src/zperf_shell.c | 61 | ||||
-rw-r--r-- | samples/net/zperf/src/zperf_tcp_receiver.c | 277 | ||||
-rw-r--r-- | samples/net/zperf/src/zperf_tcp_uploader.c | 89 |
6 files changed, 331 insertions, 136 deletions
diff --git a/samples/net/zperf/README.rst b/samples/net/zperf/README.rst index 4ec060ba7..41feb5493 100644 --- a/samples/net/zperf/README.rst +++ b/samples/net/zperf/README.rst @@ -45,6 +45,12 @@ For example, the following command line must be used for UDP testing: $ iperf -s -l 1K -u -V -B 2001:db8::2 +For TCP testing, the command line would look like this: + +.. code-block:: console + + $ iperf -s -l 1K -V -B 2001:db8::2 + In the Zephyr console, zperf can be executed as follows: @@ -53,6 +59,13 @@ In the Zephyr console, zperf can be executed as follows: zperf> udp.upload 2001:db8::2 5001 10 1K 1M +For TCP the zperf command would look like this: + +.. code-block:: console + + zperf> tcp.upload 2001:db8::2 5001 10 1K 1M + + If the IP addresses of Zephyr and the host machine are specified in the config file, zperf can be started as follows: @@ -61,20 +74,41 @@ config file, zperf can be started as follows: zperf> udp.upload2 v6 10 1K 1M -If Zephyr is acting as a server, set the download mode as follows: +or like this if you want to test TCP: + +.. code-block:: console + + zperf> tcp.upload2 v6 10 1K 1M + + +If Zephyr is acting as a server, set the download mode as follows for UDP: .. code-block:: console zperf> udp.download 5001 +or like this for TCP: + +.. code-block:: console + + zperf> tcp.download 5001 + + and in the host side, iPerf must be executed with the following -command line: +command line if you are testing UDP: .. code-block:: console $ iperf -l 1K -u -V -c 2001:db8::1 -p 5001 +and this if you are testing TCP: + +.. code-block:: console + + $ iperf -l 1K -V -c 2001:db8::1 -p 5001 + + iPerf output can be limited by using the -b option if Zephyr is not able to receive all the packets in orderly manner. diff --git a/samples/net/zperf/src/zperf.h b/samples/net/zperf/src/zperf.h index 1a7ab2c63..5630d92c6 100644 --- a/samples/net/zperf/src/zperf.h +++ b/samples/net/zperf/src/zperf.h @@ -16,6 +16,7 @@ #define CMD_STR_UDP_UPLOAD2 "udp.upload2" #define CMD_STR_UDP_DOWNLOAD "udp.download" #define CMD_STR_TCP_UPLOAD "tcp.upload" +#define CMD_STR_TCP_UPLOAD2 "tcp.upload2" #define CMD_STR_TCP_DOWNLOAD "tcp.download" struct zperf_results { diff --git a/samples/net/zperf/src/zperf_internal.h b/samples/net/zperf/src/zperf_internal.h index 13422c966..4eca4230f 100644 --- a/samples/net/zperf/src/zperf_internal.h +++ b/samples/net/zperf/src/zperf_internal.h @@ -99,6 +99,7 @@ extern void zperf_receiver_init(int port); #if defined(CONFIG_NET_TCP) extern void zperf_tcp_receiver_init(int port); +extern void zperf_tcp_uploader_init(struct k_fifo *tx_queue); extern void zperf_tcp_upload(struct net_context *net_context, unsigned int duration_in_ms, unsigned int packet_size, diff --git a/samples/net/zperf/src/zperf_shell.c b/samples/net/zperf/src/zperf_shell.c index adf16dbf4..d3c302dec 100644 --- a/samples/net/zperf/src/zperf_shell.c +++ b/samples/net/zperf/src/zperf_shell.c @@ -59,6 +59,7 @@ static const char *CONFIG = #define MY_SRC_PORT 50000 #define DEF_PORT 5001 +#define WAIT_CONNECT (2 * 1000) /* in ms */ #if defined(CONFIG_NET_IPV6) static struct in6_addr ipv6; @@ -328,7 +329,7 @@ static void shell_udp_upload2_usage(void) printk("\t<baud rate>:\tBaudrate in kilobyte or megabyte\n"); printk("\nExample %s v6 1 1K 1M\n", CMD_STR_UDP_UPLOAD2); -#if defined(CONFIG_NET_IPV6) && defined(MY_IPV6ADDR) +#if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR) printk("\nDefault IPv6 address is %s, destination [%s]:%d\n", MY_IP6ADDR, DST_IP6ADDR, DEF_PORT); #endif @@ -354,6 +355,29 @@ static void shell_tcp_upload_usage(void) printk("\nExample %s 10.237.164.178 1111 1 1K 1M\n", CMD_STR_TCP_UPLOAD); } + +static void shell_tcp_upload2_usage(void) +{ + /* Print usage */ + printk("\n%s:\n", CMD_STR_TCP_UPLOAD2); + printk("Usage:\t%s v6|v4 <duration> <packet " + "size>[K] <baud rate>[K|M]\n", CMD_STR_TCP_UPLOAD2); + printk("\t<v6|v4>:\tUse either IPv6 or IPv4\n"); + printk("\t<duration>:\tDuration of the test in seconds\n"); + printk("\t<packet size>:\tSize of the packet in byte or kilobyte " + "(with suffix K)\n"); + printk("\t<baud rate>:\tBaudrate in kilobyte or megabyte\n"); + printk("\nExample %s v6 1 1K 1M\n", + CMD_STR_TCP_UPLOAD2); +#if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR) + printk("\nDefault IPv6 address is %s, destination [%s]:%d\n", + MY_IP6ADDR, DST_IP6ADDR, DEF_PORT); +#endif +#if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR) + printk("\nDefault IPv4 address is %s, destination %s:%d\n", + MY_IP4ADDR, DST_IP4ADDR, DEF_PORT); +#endif +} #endif #if defined(CONFIG_NET_UDP) @@ -435,11 +459,11 @@ static void shell_tcp_upload_print_stats(struct zperf_results *results) printk("[%s] duration:\t", CMD_STR_TCP_UPLOAD); print_number(results->client_time_in_us, TIME_US, TIME_US_UNIT); printk("\n"); - printk("[%s] nb packets:\t%u\n", CMD_STR_UDP_UPLOAD, + printk("[%s] nb packets:\t%u\n", CMD_STR_TCP_UPLOAD, results->nb_packets_sent); printk("[%s] nb sending errors (retry or fail):\t%u\n", - CMD_STR_UDP_UPLOAD, results->nb_packets_errors); - printk("[%s] rate:\t", CMD_STR_UDP_UPLOAD); + CMD_STR_TCP_UPLOAD, results->nb_packets_errors); + printk("[%s] rate:\t", CMD_STR_TCP_UPLOAD); print_number(client_rate_in_kbps, KBPS, KBPS_UNIT); printk("\n"); } @@ -596,38 +620,53 @@ static int execute_upload(struct net_context *context6, #endif } else { #if defined(CONFIG_NET_TCP) - if (context6) { + if (family == AF_INET6 && context6) { ret = net_context_connect(context6, (struct sockaddr *)ipv6, sizeof(*ipv6), NULL, - K_NO_WAIT, + WAIT_CONNECT, NULL); if (ret < 0) { - printk("[%s] IPv6 connect failed\n", argv0); + printk("[%s] IPv6 connect failed (%d)\n", + argv0, ret); goto out; } + /* We either upload using IPv4 or IPv6, not both at + * the same time. + */ + net_context_put(context4); + zperf_tcp_upload(context6, duration_in_ms, packet_size, &results); + shell_tcp_upload_print_stats(&results); + + return 0; } - if (context4) { + if (family == AF_INET && context4) { ret = net_context_connect(context4, (struct sockaddr *)ipv4, sizeof(*ipv4), NULL, - K_NO_WAIT, + WAIT_CONNECT, NULL); if (ret < 0) { - printk("[%s] IPv4 connect failed\n", argv0); + printk("[%s] IPv4 connect failed (%d)\n", + argv0, ret); goto out; } + net_context_put(context6); + zperf_tcp_upload(context4, duration_in_ms, packet_size, &results); + shell_tcp_upload_print_stats(&results); + + return 0; } #else printk("[%s] TCP not supported\n", argv0); @@ -983,6 +1022,8 @@ struct shell_cmd commands[] = { { CMD_STR_UDP_DOWNLOAD, shell_cmd_udp_download }, #endif #if defined(CONFIG_NET_TCP) + { CMD_STR_TCP_UPLOAD, shell_cmd_upload }, + { CMD_STR_TCP_UPLOAD2, shell_cmd_upload2 }, { CMD_STR_TCP_DOWNLOAD, shell_cmd_tcp_download }, #endif #if defined(PROFILER) diff --git a/samples/net/zperf/src/zperf_tcp_receiver.c b/samples/net/zperf/src/zperf_tcp_receiver.c index 18910c56f..1a860e49e 100644 --- a/samples/net/zperf/src/zperf_tcp_receiver.c +++ b/samples/net/zperf/src/zperf_tcp_receiver.c @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#error "FIXME - TCP not supported yet" - #include <zephyr.h> #include <sections.h> @@ -22,110 +20,219 @@ #include "shell_utils.h" #include "zperf_session.h" +/* To get net_sprint_ipv{4|6}_addr() */ +#define NET_LOG_ENABLED 1 +#include "net_private.h" + #define TAG CMD_STR_TCP_DOWNLOAD" " + #define TCP_RX_FIBER_STACK_SIZE 1024 -/* Static data */ -static char __noinit __stack zperf_tcp_rx_fiber_stack[TCP_RX_FIBER_STACK_SIZE]; +static char __noinit __stack zperf_tcp_rx_stack[TCP_RX_FIBER_STACK_SIZE]; -static struct net_addr in_addr_any = { -#if defined(CONFIG_NETWORKING_WITH_IPV6) - .family = AF_INET6, - .in6_addr = IN6ADDR_ANY_INIT -#else - .family = AF_INET, .in_addr = { { { 0 } } } -#endif -}; - -static struct net_addr in_addr_my = { -#if defined(CONFIG_NETWORKING_WITH_IPV6) - .family = AF_INET6, - .in6_addr = IN6ADDR_ANY_INIT -#else - .family = AF_INET, .in_addr = { { { 0 } } } -#endif -}; +static struct sockaddr_in6 *in6_addr_my; +static struct sockaddr_in *in4_addr_my; -/* TCP RX fiber entry point */ -static void zperf_tcp_rx_fiber(int port) +static void tcp_received(struct net_context *context, + struct net_buf *buf, + int status, + void *user_data) { - struct net_context *net_context = net_context_get(IPPROTO_TCP, &in_addr_any, - 0, &in_addr_my, port); + struct session *session; + uint32_t time; - if (!net_context) { - printk(TAG "ERROR! Cannot get network context.\n"); + if (!buf) { return; } - while (1) { - struct net_buf *buf = net_receive(net_context, K_FOREVER); - struct session *session = NULL; - uint32_t time = k_cycle_get_32(); + time = k_cycle_get_32(); - if (buf == NULL) { - printk(TAG "buf is null\n"); - continue; - } + session = get_session(buf, SESSION_TCP); + if (!session) { + printk(TAG "ERROR! cannot get a session!\n"); + return; + } - session = get_session(buf, SESSION_UDP); - if (session == NULL) { - printk(TAG "ERROR! cannot get a session!\n"); - /* free buffer */ - ip_buf_unref(buf); - continue; + switch (session->state) { + case STATE_NULL: + case STATE_COMPLETED: + printk(TAG "New session started\n"); + zperf_reset_session_stats(session); + session->start_time = sys_cycle_get_32(); + session->state = STATE_ONGOING; + /* fall through */ + case STATE_ONGOING: + session->counter++; + + if (buf) { + session->length += net_nbuf_appdatalen(buf); } - switch (session->state) { - case STATE_NULL: - case STATE_COMPLETED: - printk(TAG "New session started\n"); - zperf_reset_session_stats(session); - session->start_time = k_cycle_get_32(); - session->state = STATE_ONGOING; - case STATE_ONGOING: - session->counter++; - session->length += ip_buf_appdatalen(buf); - - if (uip_closed(buf)) { - session->state = STATE_COMPLETED; - uint32_t rate_in_kbps; - uint32_t duration = HW_CYCLES_TO_USEC( - time_delta(session->start_time, time)); - - /* Compute baud rate */ - if (duration != 0) { - rate_in_kbps = (uint32_t) (( - (uint64_t) session->length - * (uint64_t) 8 - * (uint64_t) USEC_PER_SEC) - / ((uint64_t) duration * 1024)); - } else { - rate_in_kbps = 0; - } - - printk(TAG "TCP session ended\n"); - printk(TAG " duration:\t\t"); - print_number(duration, TIME_US, TIME_US_UNIT); - printk("\n"); - printk(TAG " rate:\t\t\t"); - print_number(rate_in_kbps, KBPS, KBPS_UNIT); - printk("\n"); + if (!buf && status == 0) { /* EOF */ + uint32_t rate_in_kbps; + uint32_t duration = HW_CYCLES_TO_USEC( + time_delta(session->start_time, time)); + + session->state = STATE_COMPLETED; + + /* Compute baud rate */ + if (duration != 0) { + rate_in_kbps = (uint32_t) + (((uint64_t)session->length * + (uint64_t)8 * + (uint64_t)USEC_PER_SEC) / + ((uint64_t)duration * 1024)); + } else { + rate_in_kbps = 0; } - break; - case STATE_LAST_PACKET_RECEIVED: - break; - default: - printk(TAG "Error! Unsupported case\n"); + printk(TAG "TCP session ended\n"); + + printk(TAG " duration:\t\t"); + print_number(duration, TIME_US, TIME_US_UNIT); + printk("\n"); + + printk(TAG " rate:\t\t\t"); + print_number(rate_in_kbps, KBPS, KBPS_UNIT); + printk("\n"); } + break; + case STATE_LAST_PACKET_RECEIVED: + break; + default: + printk(TAG "Error! Unsupported case\n"); + } + + net_nbuf_unref(buf); +} + +static void tcp_accepted(struct net_context *context, + struct sockaddr *addr, + socklen_t addrlen, + int error, + void *user_data) +{ + int ret; - /* free buffer */ - ip_buf_unref(buf); + ret = net_context_recv(context, tcp_received, K_NO_WAIT, user_data); + if (ret < 0) { + printk(TAG "Cannot receive TCP packet (family %d)", + net_context_get_family(context)); } } +static void zperf_tcp_rx_thread(int port) +{ + struct net_context *context4 = NULL, *context6 = NULL; + int ret, fail = 0; + +#if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR) + ret = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, &context4); + if (ret < 0) { + printk(TAG "ERROR! Cannot get IPv4 TCP network context.\n"); + return; + } + + ret = zperf_get_ipv4_addr(MY_IP4ADDR, &in4_addr_my->sin_addr, TAG); + if (ret < 0) { + printk(TAG "ERROR! Unable to set IPv4\n"); + return; + } + + printk(TAG "Binding to %s\n", + net_sprint_ipv4_addr(&in4_addr_my->sin_addr)); + + in4_addr_my->sin_port = htons(port); +#endif + +#if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR) + ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &context6); + if (ret < 0) { + printk(TAG "ERROR! Cannot get IPv6 TCP network context.\n"); + return; + } + + ret = zperf_get_ipv6_addr(MY_IP6ADDR, MY_PREFIX_LEN_STR, + &in6_addr_my->sin6_addr, TAG); + if (ret < 0) { + printk(TAG "ERROR! Unable to set IPv6\n"); + return; + } + + printk(TAG "Binding to %s\n", + net_sprint_ipv6_addr(&in6_addr_my->sin6_addr)); + + in6_addr_my->sin6_port = htons(port); +#endif + +#if defined(CONFIG_NET_IPV6) + if (context6) { + ret = net_context_bind(context6, + (struct sockaddr *)in6_addr_my, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + printk(TAG "Cannot bind IPv6 TCP port %d (%d)\n", + ntohs(in6_addr_my->sin6_port), ret); + fail++; + } + + ret = net_context_listen(context6, 0); + if (ret < 0) { + printk(TAG "Cannot listen IPv6 TCP (%d)", ret); + return; + } + + ret = net_context_accept(context6, tcp_accepted, 0, NULL); + if (ret < 0) { + printk(TAG "Cannot receive IPv6 TCP packets (%d)", ret); + return; + } + } +#endif + +#if defined(CONFIG_NET_IPV4) + if (context4) { + ret = net_context_bind(context4, + (struct sockaddr *)in4_addr_my, + sizeof(struct sockaddr_in)); + if (ret < 0) { + printk(TAG "Cannot bind IPv4 TCP port %d (%d)\n", + ntohs(in4_addr_my->sin_port), ret); + fail++; + } + + ret = net_context_listen(context4, 0); + if (ret < 0) { + printk(TAG "Cannot listen IPv4 TCP (%d)", ret); + return; + } + + ret = net_context_accept(context4, tcp_accepted, 0, NULL); + if (ret < 0) { + printk(TAG "Cannot receive IPv4 TCP packets (%d)", ret); + return; + } + } +#endif + + if (fail > 1) { + return; + } + + k_sleep(K_FOREVER); +} + void zperf_tcp_receiver_init(int port) { - fiber_start(zperf_tcp_rx_fiber_stack, sizeof(zperf_tcp_rx_fiber_stack), - (nano_fiber_entry_t) zperf_tcp_rx_fiber, port, 0, 7, 0); +#if defined(CONFIG_NET_IPV6) + in6_addr_my = zperf_get_sin6(); +#endif +#if defined(CONFIG_NET_IPV4) + in4_addr_my = zperf_get_sin(); +#endif + + k_thread_spawn(zperf_tcp_rx_stack, sizeof(zperf_tcp_rx_stack), + (k_thread_entry_t)zperf_tcp_rx_thread, + INT_TO_POINTER(port), 0, 0, + K_PRIO_COOP(7), 0, K_NO_WAIT); } diff --git a/samples/net/zperf/src/zperf_tcp_uploader.c b/samples/net/zperf/src/zperf_tcp_uploader.c index 2a79fe49b..7d1735203 100644 --- a/samples/net/zperf/src/zperf_tcp_uploader.c +++ b/samples/net/zperf/src/zperf_tcp_uploader.c @@ -4,23 +4,26 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include <zephyr.h> + #include <errno.h> #include <misc/printk.h> -#include <net/ip_buf.h> + +#include <net/nbuf.h> #include <net/net_ip.h> #include <net/net_core.h> -#include <net/net_socket.h> -#include <zephyr.h> #include "zperf.h" #include "zperf_internal.h" #define TAG CMD_STR_TCP_UPLOAD" " -void zperf_tcp_upload(struct net_context *net_context, - unsigned int duration_in_ms, - unsigned int packet_size, - zperf_results *results) +static char sample_packet[PACKET_SIZE_MAX]; + +void zperf_tcp_upload(struct net_context *ctx, + unsigned int duration_in_ms, + unsigned int packet_size, + struct zperf_results *results) { uint32_t duration = MSEC_TO_HW_CYCLES(duration_in_ms); uint32_t nb_packets = 0, nb_errors = 0; @@ -29,7 +32,7 @@ void zperf_tcp_upload(struct net_context *net_context, if (packet_size > PACKET_SIZE_MAX) { printk(TAG "WARNING! packet size too large! max size: %u\n", - PACKET_SIZE_MAX); + PACKET_SIZE_MAX); packet_size = PACKET_SIZE_MAX; } @@ -38,69 +41,77 @@ void zperf_tcp_upload(struct net_context *net_context, last_print_time = start_time; last_loop_time = start_time; printk(TAG "New session started\n"); + + memset(sample_packet, 'z', sizeof(sample_packet)); + do { - uint32_t loop_time; - uint8_t *ptr = NULL; int ret = 0; + struct net_buf *buf, *frag; + uint32_t loop_time; + bool st; /* Timestamps */ loop_time = k_cycle_get_32(); last_loop_time = loop_time; - /* Get a new TX buffer */ - struct net_buf *buf = ip_buf_get_tx(net_context); - + buf = net_nbuf_get_tx(ctx); if (!buf) { printk(TAG "ERROR! Failed to retrieve a buffer\n"); continue; } + frag = net_nbuf_get_data(ctx); + if (!frag) { + printk(TAG "ERROR! Failed to retrieve a fragment\n"); + continue; + } + + net_buf_frag_add(buf, frag); + /* Fill in the TCP payload */ - ptr = net_buf_add(buf, packet_size); - memset(ptr, 'z', packet_size); - - /* If test time is elapsed, send a last packet with specific flag - * to request uIP to close the TCP connection - */ - if (time_elapsed) { - uip_flags(buf) |= UIP_CLOSE; + st = net_nbuf_append(buf, sizeof(sample_packet), + sample_packet); + if (!st) { + printk(TAG "ERROR! Failed to fill packet\n"); + + net_nbuf_unref(buf); + nb_errors++; + break; } /* Send the packet */ -again: - ret = net_send(buf); + ret = net_context_send(buf, NULL, K_NO_WAIT, NULL, NULL); if (ret < 0) { - if (ret == -EINPROGRESS || ret == -EAGAIN) { - nb_errors++; - fiber_sleep(100); - goto again; - } else { - printk("ERROR! Failed to send the buffer\n"); - nb_errors++; - } + printk(TAG "ERROR! Failed to send the buffer (%d)\n", + ret); + + net_nbuf_unref(buf); + nb_errors++; + break; } else { nb_packets++; - /* if test time is elapsed and are here, that means TCP connection - * has been closed by uIP as requested. So exit the loop. - */ + if (time_elapsed) { finished = 1; } } - if (!time_elapsed && time_delta(start_time, last_loop_time) > duration) + if (!time_elapsed && time_delta(start_time, + last_loop_time) > duration) { time_elapsed = 1; + } - ip_buf_unref(buf); - fiber_yield(); + k_yield(); } while (!finished); end_time = k_cycle_get_32(); /* Add result coming from the client */ results->nb_packets_sent = nb_packets; - results->client_time_in_us = HW_CYCLES_TO_USEC( - time_delta(start_time, end_time)); + results->client_time_in_us = + HW_CYCLES_TO_USEC(time_delta(start_time, end_time)); results->packet_size = packet_size; results->nb_packets_errors = nb_errors; + + net_context_put(ctx); } |