samples: net: zperf: Rewrite upload part to use sockets

Rewrite TCP/UDP upload part of the zperf sample to use socket API
instead of net_context. This has a negligible impact on the upload
throughputs (< 1 Mbps).

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2022-06-01 15:10:26 +02:00 committed by Carles Cufí
parent ef6770f22b
commit 6c30c9ac47
5 changed files with 134 additions and 193 deletions

View file

@ -11,11 +11,17 @@ CONFIG_NET_PKT_RX_COUNT=14
CONFIG_NET_PKT_TX_COUNT=14
CONFIG_NET_BUF_RX_COUNT=28
CONFIG_NET_BUF_TX_COUNT=28
CONFIG_NET_BUF_DATA_SIZE=1500
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=4
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5
CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=1
CONFIG_NET_MAX_CONTEXTS=5
CONFIG_NET_CONTEXT_SYNC_RECV=y
CONFIG_NET_CONTEXT_RCVTIMEO=y
CONFIG_NET_TC_TX_COUNT=1
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y
CONFIG_NET_SOCKETS_POLL_MAX=4
CONFIG_POSIX_MAX_FDS=8
CONFIG_INIT_STACKS=y
CONFIG_TEST_RANDOM_GENERATOR=y

View file

@ -79,7 +79,7 @@ int zperf_get_ipv4_addr(const struct shell *shell, char *host,
struct sockaddr_in *zperf_get_sin(void);
extern void zperf_udp_upload(const struct shell *shell,
struct net_context *context,
int sock,
int port,
unsigned int duration_in_ms,
unsigned int packet_size,
@ -91,7 +91,7 @@ extern void zperf_udp_receiver_init(const struct shell *shell, int port);
extern void zperf_tcp_receiver_init(const struct shell *shell, int port);
extern void zperf_tcp_uploader_init(struct k_fifo *tx_queue);
extern void zperf_tcp_upload(const struct shell *shell,
struct net_context *net_context,
int sock,
unsigned int duration_in_ms,
unsigned int packet_size,
struct zperf_results *results);

View file

@ -17,6 +17,7 @@ LOG_MODULE_REGISTER(net_zperf_sample, LOG_LEVEL_DBG);
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/socket.h>
#include "zperf.h"
#include "zperf_internal.h"
@ -52,7 +53,6 @@ static const char *CONFIG =
#define MY_SRC_PORT 50000
#define DEF_PORT 5001
#define DEF_PORT_STR STRINGIFY(DEF_PORT)
#define WAIT_CONNECT K_SECONDS(2) /* in ms */
static struct in6_addr ipv6;
@ -451,9 +451,9 @@ static void shell_tcp_upload_print_stats(const struct shell *shell,
}
}
static int setup_contexts(const struct shell *shell,
struct net_context **context6,
struct net_context **context4,
static int setup_upload_sockets(const struct shell *shell,
int *sock6,
int *sock4,
sa_family_t family,
struct sockaddr_in6 *ipv6,
struct sockaddr_in *ipv4,
@ -461,17 +461,14 @@ static int setup_contexts(const struct shell *shell,
bool is_udp,
char *argv0)
{
int ret;
if (IS_ENABLED(CONFIG_NET_IPV6)) {
ret = net_context_get(AF_INET6,
*sock6 = socket(AF_INET6,
is_udp ? SOCK_DGRAM : SOCK_STREAM,
is_udp ? IPPROTO_UDP : IPPROTO_TCP,
context6);
if (ret < 0) {
is_udp ? IPPROTO_UDP : IPPROTO_TCP);
if (*sock6 < 0) {
shell_fprintf(shell, SHELL_WARNING,
"Cannot get IPv6 network context (%d)\n",
ret);
"Cannot create IPv6 network socket (%d)\n",
errno);
return -ENOEXEC;
}
@ -480,14 +477,13 @@ static int setup_contexts(const struct shell *shell,
}
if (IS_ENABLED(CONFIG_NET_IPV4)) {
ret = net_context_get(AF_INET,
*sock4 = socket(AF_INET,
is_udp ? SOCK_DGRAM : SOCK_STREAM,
is_udp ? IPPROTO_UDP : IPPROTO_TCP,
context4);
if (ret < 0) {
is_udp ? IPPROTO_UDP : IPPROTO_TCP);
if (*sock4 < 0) {
shell_fprintf(shell, SHELL_WARNING,
"Cannot get IPv4 network context (%d)\n",
ret);
"Cannot create IPv4 network socket (%d)\n",
errno);
return -ENOEXEC;
}
@ -495,33 +491,9 @@ static int setup_contexts(const struct shell *shell,
ipv4->sin_family = AF_INET;
}
if (family == AF_INET6 && *context6) {
ret = net_context_bind(*context6,
(struct sockaddr *)ipv6,
sizeof(struct sockaddr_in6));
if (ret < 0) {
shell_fprintf(shell, SHELL_NORMAL,
"Cannot bind IPv6 port %d (%d)",
ntohs(ipv6->sin6_port), ret);
return -ENOEXEC;
}
}
if (family == AF_INET && *context4) {
ret = net_context_bind(*context4,
(struct sockaddr *)ipv4,
sizeof(struct sockaddr_in));
if (ret < 0) {
if ((*sock6 < 0) && (*sock4 < 0)) {
shell_fprintf(shell, SHELL_WARNING,
"Cannot bind IPv4 port %d (%d)",
ntohs(ipv4->sin_port), ret);
return -ENOEXEC;
}
}
if (!(*context6) && !(*context4)) {
shell_fprintf(shell, SHELL_WARNING,
"Fail to retrieve network context(s)\n");
"Fail to create network socket(s)\n");
return -ENOEXEC;
}
@ -529,8 +501,8 @@ static int setup_contexts(const struct shell *shell,
}
static int execute_upload(const struct shell *shell,
struct net_context *context6,
struct net_context *context4,
int sock6,
int sock4,
sa_family_t family,
struct sockaddr_in6 *ipv6,
struct sockaddr_in *ipv4,
@ -554,7 +526,7 @@ static int execute_upload(const struct shell *shell,
rate_in_kbps);
shell_fprintf(shell, SHELL_NORMAL, "Starting...\n");
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6 && context6) {
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6 && sock6 >= 0) {
/* For IPv6, we should make sure that neighbor discovery
* has been done for the peer. So send ping here, wait
* some time and start the test after that.
@ -570,40 +542,34 @@ static int execute_upload(const struct shell *shell,
print_number(shell, rate_in_kbps, KBPS, KBPS_UNIT);
shell_fprintf(shell, SHELL_NORMAL, "\n");
if (family == AF_INET6 && context6) {
ret = net_context_connect(context6,
if (family == AF_INET6 && sock6 >= 0) {
ret = connect(sock6,
(struct sockaddr *)ipv6,
sizeof(*ipv6),
NULL,
K_NO_WAIT,
NULL);
sizeof(*ipv6));
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"IPv6 connect failed (%d)\n",
ret);
errno);
goto out;
}
zperf_udp_upload(shell, context6, port, duration_in_ms,
zperf_udp_upload(shell, sock6, port, duration_in_ms,
packet_size, rate_in_kbps, &results);
shell_udp_upload_print_stats(shell, &results);
}
if (family == AF_INET && context4) {
ret = net_context_connect(context4,
if (family == AF_INET && sock4 >= 0) {
ret = connect(sock4,
(struct sockaddr *)ipv4,
sizeof(*ipv4),
NULL,
K_NO_WAIT,
NULL);
sizeof(*ipv4));
if (ret < 0) {
shell_fprintf(shell, SHELL_NORMAL,
"IPv4 connect failed (%d)\n",
ret);
errno);
goto out;
}
zperf_udp_upload(shell, context4, port, duration_in_ms,
zperf_udp_upload(shell, sock4, port, duration_in_ms,
packet_size, rate_in_kbps, &results);
shell_udp_upload_print_stats(shell, &results);
}
@ -615,59 +581,51 @@ static int execute_upload(const struct shell *shell,
}
if (!is_udp && IS_ENABLED(CONFIG_NET_TCP)) {
if (family == AF_INET6 && context6) {
ret = net_context_connect(context6,
if (family == AF_INET6 && sock6 >= 0) {
ret = connect(sock6,
(struct sockaddr *)ipv6,
sizeof(*ipv6),
NULL,
WAIT_CONNECT,
NULL);
sizeof(*ipv6));
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"IPv6 connect failed (%d)\n",
ret);
errno);
goto out;
}
/* We either upload using IPv4 or IPv6, not both at
* the same time.
*/
if (IS_ENABLED(CONFIG_NET_IPV4)) {
net_context_put(context4);
if (IS_ENABLED(CONFIG_NET_IPV4) && sock4 >= 0) {
(void)close(sock4);
sock4 = -1;
}
zperf_tcp_upload(shell, context6, duration_in_ms,
zperf_tcp_upload(shell, sock6, duration_in_ms,
packet_size, &results);
shell_tcp_upload_print_stats(shell, &results);
return 0;
}
if (family == AF_INET && context4) {
ret = net_context_connect(context4,
if (family == AF_INET && sock4 >= 0) {
ret = connect(sock4,
(struct sockaddr *)ipv4,
sizeof(*ipv4),
NULL,
WAIT_CONNECT,
NULL);
sizeof(*ipv4));
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"IPv4 connect failed (%d)\n",
ret);
errno);
goto out;
}
if (IS_ENABLED(CONFIG_NET_IPV6)) {
net_context_put(context6);
if (IS_ENABLED(CONFIG_NET_IPV6) && sock6 >= 0) {
(void)close(sock6);
sock6 = -1;
}
zperf_tcp_upload(shell, context4, duration_in_ms,
zperf_tcp_upload(shell, sock4, duration_in_ms,
packet_size, &results);
shell_tcp_upload_print_stats(shell, &results);
return 0;
}
} else {
if (!IS_ENABLED(CONFIG_NET_TCP)) {
@ -677,11 +635,14 @@ static int execute_upload(const struct shell *shell,
}
out:
if (IS_ENABLED(CONFIG_NET_IPV6)) {
net_context_put(context6);
if (IS_ENABLED(CONFIG_NET_IPV6) && sock6 >= 0) {
(void)close(sock6);
sock6 = -1;
}
if (IS_ENABLED(CONFIG_NET_IPV4)) {
net_context_put(context4);
if (IS_ENABLED(CONFIG_NET_IPV4) && sock4 >= 0) {
(void)close(sock4);
sock4 = -1;
}
return 0;
@ -692,7 +653,7 @@ static int shell_cmd_upload(const struct shell *shell, size_t argc,
{
struct sockaddr_in6 ipv6 = { .sin6_family = AF_INET6 };
struct sockaddr_in ipv4 = { .sin_family = AF_INET };
struct net_context *context6 = NULL, *context4 = NULL;
int sock6 = -1, sock4 = -1;
sa_family_t family = AF_UNSPEC;
unsigned int duration_in_ms, packet_size, rate_in_kbps;
char *port_str;
@ -786,7 +747,7 @@ static int shell_cmd_upload(const struct shell *shell, size_t argc,
}
}
if (setup_contexts(shell, &context6, &context4, family, &in6_addr_my,
if (setup_upload_sockets(shell, &sock6, &sock4, family, &in6_addr_my,
&in4_addr_my, port, is_udp, argv[start]) < 0) {
return -ENOEXEC;
}
@ -812,7 +773,7 @@ static int shell_cmd_upload(const struct shell *shell, size_t argc,
rate_in_kbps = 10U;
}
return execute_upload(shell, context6, context4, family, &ipv6, &ipv4,
return execute_upload(shell, sock6, sock4, family, &ipv6, &ipv4,
is_udp, port, argv[start], duration_in_ms,
packet_size, rate_in_kbps);
}
@ -834,7 +795,7 @@ static int cmd_udp_upload(const struct shell *shell, size_t argc, char *argv[])
static int shell_cmd_upload2(const struct shell *shell, size_t argc,
char *argv[], enum net_ip_protocol proto)
{
struct net_context *context6 = NULL, *context4 = NULL;
int sock6 = -1, sock4 = -1;
uint16_t port = DEF_PORT;
unsigned int duration_in_ms, packet_size, rate_in_kbps;
sa_family_t family;
@ -906,7 +867,7 @@ static int shell_cmd_upload2(const struct shell *shell, size_t argc,
net_sprint_ipv4_addr(&in4_addr_dst.sin_addr));
}
if (setup_contexts(shell, &context6, &context4, family, &in6_addr_my,
if (setup_upload_sockets(shell, &sock6, &sock4, family, &in6_addr_my,
&in4_addr_my, port, is_udp, argv[start]) < 0) {
return -ENOEXEC;
}
@ -932,7 +893,7 @@ static int shell_cmd_upload2(const struct shell *shell, size_t argc,
rate_in_kbps = 10U;
}
return execute_upload(shell, context6, context4, family, &in6_addr_dst,
return execute_upload(shell, sock6, sock4, family, &in6_addr_dst,
&in4_addr_dst, is_udp, port, argv[start],
duration_in_ms, packet_size, rate_in_kbps);
}

View file

@ -12,9 +12,7 @@ LOG_MODULE_DECLARE(net_zperf_sample, LOG_LEVEL_DBG);
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/socket.h>
#include "zperf.h"
#include "zperf_internal.h"
@ -22,7 +20,7 @@ LOG_MODULE_DECLARE(net_zperf_sample, LOG_LEVEL_DBG);
static char sample_packet[PACKET_SIZE_MAX];
void zperf_tcp_upload(const struct shell *shell,
struct net_context *ctx,
int sock,
unsigned int duration_in_ms,
unsigned int packet_size,
struct zperf_results *results)
@ -58,19 +56,17 @@ void zperf_tcp_upload(const struct shell *shell,
int ret = 0;
/* Send the packet */
ret = net_context_send(ctx, sample_packet,
packet_size, NULL,
K_NO_WAIT, NULL);
ret = send(sock, sample_packet, packet_size, 0);
if (ret < 0) {
if (nb_errors == 0 && ret != -ENOMEM) {
shell_fprintf(shell, SHELL_WARNING,
"Failed to send the packet (%d)\n",
ret);
errno);
}
nb_errors++;
if (ret == -ENOMEM) {
if (errno == -ENOMEM) {
/* Ignore memory errors as we just run out of
* buffers which is kind of expected if the
* buffer count is not optimized for the test
@ -111,6 +107,4 @@ void zperf_tcp_upload(const struct shell *shell,
"options.\n",
alloc_errors);
}
net_context_put(ctx);
}

View file

@ -11,9 +11,7 @@ LOG_MODULE_DECLARE(net_zperf_sample, LOG_LEVEL_DBG);
#include <zephyr/sys/printk.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/socket.h>
#include "zperf.h"
#include "zperf_internal.h"
@ -23,31 +21,20 @@ static uint8_t sample_packet[sizeof(struct zperf_udp_datagram) +
PACKET_SIZE_MAX];
static inline void zperf_upload_decode_stat(const struct shell *shell,
struct net_pkt *pkt,
const uint8_t *data,
size_t datalen,
struct zperf_results *results)
{
NET_PKT_DATA_ACCESS_DEFINE(zperf_udp, struct zperf_udp_datagram);
NET_PKT_DATA_ACCESS_DEFINE(zperf_stat, struct zperf_server_hdr);
struct zperf_udp_datagram *hdr;
struct zperf_server_hdr *stat;
hdr = (struct zperf_udp_datagram *)
net_pkt_get_data(pkt, &zperf_udp);
if (!hdr) {
if (datalen < sizeof(struct zperf_udp_datagram) +
sizeof(struct zperf_server_hdr)) {
shell_fprintf(shell, SHELL_WARNING,
"Network packet too short\n");
return;
}
net_pkt_acknowledge_data(pkt, &zperf_udp);
stat = (struct zperf_server_hdr *)
net_pkt_get_data(pkt, &zperf_stat);
if (!stat) {
shell_fprintf(shell, SHELL_WARNING,
"Network packet too short\n");
return;
}
(data + sizeof(struct zperf_udp_datagram));
results->nb_packets_rcvd = ntohl(UNALIGNED_GET(&stat->datagrams));
results->nb_packets_lost = ntohl(UNALIGNED_GET(&stat->error_cnt));
@ -60,34 +47,27 @@ static inline void zperf_upload_decode_stat(const struct shell *shell,
ntohl(UNALIGNED_GET(&stat->jitter1)) * USEC_PER_SEC;
}
static void stat_received(struct net_context *context,
struct net_pkt *pkt,
union net_ip_header *ip_hdr,
union net_proto_header *proto_hdr,
int status,
void *user_data)
{
struct net_pkt **stat = user_data;
*stat = pkt;
}
static inline void zperf_upload_fin(const struct shell *shell,
struct net_context *context,
int sock,
uint32_t nb_packets,
uint64_t end_time,
uint32_t packet_size,
struct zperf_results *results)
{
struct net_pkt *stat = NULL;
uint8_t stats[sizeof(struct zperf_udp_datagram) +
sizeof(struct zperf_server_hdr)] = { 0 };
struct zperf_udp_datagram *datagram;
struct zperf_client_hdr_v1 *hdr;
uint32_t secs = k_ticks_to_ms_ceil32(end_time) / 1000U;
uint32_t usecs = k_ticks_to_us_ceil32(end_time) - secs * USEC_PER_SEC;
int loop = 2;
int ret;
int ret = 0;
struct timeval rcvtimeo = {
.tv_sec = 2,
.tv_usec = 0,
};
while (!stat && loop-- > 0) {
while (ret <= 0 && loop-- > 0) {
datagram = (struct zperf_udp_datagram *)sample_packet;
/* Fill the packet header */
@ -112,60 +92,61 @@ static inline void zperf_upload_fin(const struct shell *shell,
hdr->num_of_bytes = htonl(packet_size);
/* Send the packet */
ret = net_context_send(context, sample_packet, packet_size,
NULL, K_NO_WAIT, NULL);
ret = send(sock, sample_packet, packet_size, 0);
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"Failed to send the packet (%d)\n",
ret);
errno);
continue;
}
/* Receive statistics */
stat = NULL;
ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo,
sizeof(rcvtimeo));
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"setsockopt error (%d)\n",
errno);
continue;
}
ret = net_context_recv(context, stat_received,
K_SECONDS(2), &stat);
if (ret == -ETIMEDOUT) {
break;
ret = recv(sock, stats, sizeof(stats), 0);
if (ret == -EAGAIN) {
shell_fprintf(shell, SHELL_WARNING,
"Stats receive timeout\n");
} else if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"Failed to receive packet (%d)\n",
errno);
}
}
/* Decode statistics */
if (stat) {
zperf_upload_decode_stat(shell, stat, results);
net_pkt_unref(stat);
if (ret > 0) {
zperf_upload_decode_stat(shell, stats, ret, results);
}
/* Drain RX */
while (stat) {
stat = NULL;
ret = net_context_recv(context, stat_received,
K_NO_WAIT, &stat);
if (ret == -ETIMEDOUT) {
while (true) {
ret = recv(sock, stats, sizeof(stats), MSG_DONTWAIT);
if (ret < 0) {
break;
}
if (stat) {
shell_fprintf(shell, SHELL_WARNING,
"Drain one spurious stat packet!\n");
net_pkt_unref(stat);
}
}
}
void zperf_udp_upload(const struct shell *shell,
struct net_context *context,
int sock,
int port,
unsigned int duration_in_ms,
unsigned int packet_size,
unsigned int rate_in_kbps,
struct zperf_results *results)
{
uint32_t packet_duration = ((uint32_t)packet_size * 8U * USEC_PER_SEC) /
uint32_t packet_duration = ((uint64_t)packet_size * 8U * USEC_PER_SEC) /
(rate_in_kbps * 1024U);
uint64_t duration = sys_clock_timeout_end_calc(K_MSEC(duration_in_ms));
int64_t print_interval = sys_clock_timeout_end_calc(K_SECONDS(1));
@ -257,12 +238,11 @@ void zperf_udp_upload(const struct shell *shell,
hdr->num_of_bytes = htonl(packet_size);
/* Send the packet */
ret = net_context_send(context, sample_packet, packet_size,
NULL, K_NO_WAIT, NULL);
ret = send(sock, sample_packet, packet_size, 0);
if (ret < 0) {
shell_fprintf(shell, SHELL_WARNING,
"Failed to send the packet (%d)\n",
ret);
errno);
break;
} else {
nb_packets++;
@ -296,7 +276,7 @@ void zperf_udp_upload(const struct shell *shell,
end_time = k_uptime_ticks();
zperf_upload_fin(shell, context, nb_packets, end_time, packet_size,
zperf_upload_fin(shell, sock, nb_packets, end_time, packet_size,
results);
/* Add result coming from the client */