200 lines
4.5 KiB
C
200 lines
4.5 KiB
C
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
|
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/net/socket.h>
|
|
#include <zephyr/net/zperf.h>
|
|
|
|
#include "zperf_internal.h"
|
|
|
|
static char sample_packet[PACKET_SIZE_MAX];
|
|
|
|
static struct zperf_async_upload_context tcp_async_upload_ctx;
|
|
|
|
static ssize_t sendall(int sock, const void *buf, size_t len)
|
|
{
|
|
while (len) {
|
|
ssize_t out_len = zsock_send(sock, buf, len, 0);
|
|
|
|
if (out_len < 0) {
|
|
return out_len;
|
|
}
|
|
|
|
buf = (const char *)buf + out_len;
|
|
len -= out_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tcp_upload(int sock,
|
|
unsigned int duration_in_ms,
|
|
unsigned int packet_size,
|
|
struct zperf_results *results)
|
|
{
|
|
k_timepoint_t end = sys_timepoint_calc(K_MSEC(duration_in_ms));
|
|
int64_t start_time, end_time;
|
|
uint32_t nb_packets = 0U, nb_errors = 0U;
|
|
uint32_t alloc_errors = 0U;
|
|
int ret = 0;
|
|
|
|
if (packet_size > PACKET_SIZE_MAX) {
|
|
NET_WARN("Packet size too large! max size: %u\n",
|
|
PACKET_SIZE_MAX);
|
|
packet_size = PACKET_SIZE_MAX;
|
|
}
|
|
|
|
/* Start the loop */
|
|
start_time = k_uptime_ticks();
|
|
|
|
(void)memset(sample_packet, 'z', sizeof(sample_packet));
|
|
|
|
/* Set the "flags" field in start of the packet to be 0.
|
|
* As the protocol is not properly described anywhere, it is
|
|
* not certain if this is a proper thing to do.
|
|
*/
|
|
(void)memset(sample_packet, 0, sizeof(uint32_t));
|
|
|
|
do {
|
|
/* Send the packet */
|
|
ret = sendall(sock, sample_packet, packet_size);
|
|
if (ret < 0) {
|
|
if (nb_errors == 0 && ret != -ENOMEM) {
|
|
NET_ERR("Failed to send the packet (%d)", errno);
|
|
}
|
|
|
|
nb_errors++;
|
|
|
|
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
|
|
* and device.
|
|
*/
|
|
alloc_errors++;
|
|
} else {
|
|
ret = -errno;
|
|
break;
|
|
}
|
|
} else {
|
|
nb_packets++;
|
|
}
|
|
|
|
#if defined(CONFIG_ARCH_POSIX)
|
|
k_busy_wait(100 * USEC_PER_MSEC);
|
|
#else
|
|
k_yield();
|
|
#endif
|
|
|
|
} while (!sys_timepoint_expired(end));
|
|
|
|
end_time = k_uptime_ticks();
|
|
|
|
/* Add result coming from the client */
|
|
results->nb_packets_sent = nb_packets;
|
|
results->client_time_in_us =
|
|
k_ticks_to_us_ceil32(end_time - start_time);
|
|
results->packet_size = packet_size;
|
|
results->nb_packets_errors = nb_errors;
|
|
|
|
if (alloc_errors > 0) {
|
|
NET_WARN("There was %u network buffer allocation "
|
|
"errors during send.\nConsider increasing the "
|
|
"value of CONFIG_NET_BUF_TX_COUNT and\n"
|
|
"optionally CONFIG_NET_PKT_TX_COUNT Kconfig "
|
|
"options.",
|
|
alloc_errors);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zperf_tcp_upload(const struct zperf_upload_params *param,
|
|
struct zperf_results *result)
|
|
{
|
|
int sock;
|
|
int ret;
|
|
|
|
if (param == NULL || result == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
sock = zperf_prepare_upload_sock(¶m->peer_addr, param->options.tos,
|
|
param->options.priority, IPPROTO_TCP);
|
|
if (sock < 0) {
|
|
return sock;
|
|
}
|
|
|
|
if (param->options.tcp_nodelay &&
|
|
zsock_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
|
¶m->options.tcp_nodelay,
|
|
sizeof(param->options.tcp_nodelay)) != 0) {
|
|
NET_WARN("Failed to set IPPROTO_TCP - TCP_NODELAY socket option.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = tcp_upload(sock, param->duration_ms, param->packet_size, result);
|
|
|
|
zsock_close(sock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void tcp_upload_async_work(struct k_work *work)
|
|
{
|
|
struct zperf_async_upload_context *upload_ctx =
|
|
CONTAINER_OF(work, struct zperf_async_upload_context, work);
|
|
struct zperf_results result;
|
|
int ret;
|
|
|
|
upload_ctx->callback(ZPERF_SESSION_STARTED, NULL,
|
|
upload_ctx->user_data);
|
|
|
|
ret = zperf_tcp_upload(&upload_ctx->param, &result);
|
|
if (ret < 0) {
|
|
upload_ctx->callback(ZPERF_SESSION_ERROR, NULL,
|
|
upload_ctx->user_data);
|
|
} else {
|
|
upload_ctx->callback(ZPERF_SESSION_FINISHED, &result,
|
|
upload_ctx->user_data);
|
|
}
|
|
}
|
|
|
|
int zperf_tcp_upload_async(const struct zperf_upload_params *param,
|
|
zperf_callback callback, void *user_data)
|
|
{
|
|
if (param == NULL || callback == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (k_work_is_pending(&tcp_async_upload_ctx.work)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
memcpy(&tcp_async_upload_ctx.param, param, sizeof(*param));
|
|
tcp_async_upload_ctx.callback = callback;
|
|
tcp_async_upload_ctx.user_data = user_data;
|
|
|
|
zperf_async_work_submit(&tcp_async_upload_ctx.work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void zperf_tcp_uploader_init(void)
|
|
{
|
|
k_work_init(&tcp_async_upload_ctx.work, tcp_upload_async_work);
|
|
}
|