mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
86fdbd7054
The macros CONCAT(), MIN(), and MAX() are defined over and over again in RIOT's code base. This de-duplicates the code by moving the macros to a common place.
213 lines
5.3 KiB
C
213 lines
5.3 KiB
C
/*
|
|
* Copyright (C) 2021 ML!PA Consulting GmbH
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
* Public License v2.1. See the file LICENSE in the top level directory for
|
|
* more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup test_utils_benchmark_udp
|
|
* @{
|
|
*
|
|
* @file
|
|
*
|
|
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "macros/utils.h"
|
|
#include "net/sock/udp.h"
|
|
#include "net/utils.h"
|
|
#include "sema_inv.h"
|
|
#include "test_utils/benchmark_udp.h"
|
|
#include "xtimer.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
static sock_udp_t sock;
|
|
static uint32_t delay_us = US_PER_SEC;
|
|
static uint16_t payload_size = 32;
|
|
|
|
static char send_thread_stack[THREAD_STACKSIZE_DEFAULT];
|
|
static char listen_thread_stack[THREAD_STACKSIZE_DEFAULT];
|
|
|
|
static uint8_t buf_tx[BENCH_PAYLOAD_SIZE_MAX + sizeof(benchmark_msg_ping_t)];
|
|
static benchmark_msg_ping_t *ping = (void *)buf_tx;
|
|
|
|
static bool running;
|
|
static sema_inv_t thread_sync;
|
|
|
|
struct {
|
|
uint32_t seq_no;
|
|
uint32_t time_tx_us;
|
|
} record_tx[4];
|
|
|
|
static uint32_t _get_rtt(uint32_t seq_num, uint32_t prev)
|
|
{
|
|
for (unsigned i = 0; i < ARRAY_SIZE(record_tx); ++i) {
|
|
if (record_tx[i].seq_no == seq_num) {
|
|
return xtimer_now_usec() - record_tx[i].time_tx_us;
|
|
}
|
|
}
|
|
|
|
return prev;
|
|
}
|
|
|
|
static void _put_rtt(uint32_t seq_num) {
|
|
uint8_t oldest = 0;
|
|
uint32_t oldest_diff = 0;
|
|
uint32_t now = xtimer_now_usec();
|
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(record_tx); ++i) {
|
|
uint32_t diff = now - record_tx[i].time_tx_us;
|
|
if (diff > oldest_diff) {
|
|
oldest_diff = diff;
|
|
oldest = i;
|
|
}
|
|
}
|
|
|
|
record_tx[oldest].seq_no = seq_num;
|
|
record_tx[oldest].time_tx_us = now;
|
|
}
|
|
|
|
static void *_listen_thread(void *ctx)
|
|
{
|
|
(void)ctx;
|
|
|
|
static uint8_t buf[BENCH_PAYLOAD_SIZE_MAX + sizeof(benchmark_msg_ping_t)];
|
|
benchmark_msg_cmd_t *cmd = (void *)buf;
|
|
|
|
DEBUG_PUTS("bench_udp: listen thread start");
|
|
|
|
while (running) {
|
|
ssize_t res;
|
|
|
|
res = sock_udp_recv(&sock, buf, sizeof(buf), 2 * delay_us, NULL);
|
|
if (res < 0) {
|
|
if (res != -ETIMEDOUT) {
|
|
printf("Error receiving message: %zd\n", res);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
unsigned state = irq_disable();
|
|
if (cmd->flags & BENCH_FLAG_CMD_PKT) {
|
|
ping->seq_no = 0;
|
|
ping->replies = 0;
|
|
ping->flags = cmd->flags & BENCH_MASK_COOKIE;
|
|
delay_us = cmd->delay_us;
|
|
payload_size = MIN(cmd->payload_len, BENCH_PAYLOAD_SIZE_MAX);
|
|
} else {
|
|
benchmark_msg_ping_t *pong = (void *)buf;
|
|
|
|
ping->replies++;
|
|
ping->rtt_last = _get_rtt(pong->seq_no, ping->rtt_last);
|
|
}
|
|
irq_restore(state);
|
|
}
|
|
|
|
DEBUG_PUTS("bench_udp: listen thread terminates");
|
|
sema_inv_post(&thread_sync);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void *_send_thread(void *ctx)
|
|
{
|
|
sock_udp_ep_t remote = *(sock_udp_ep_t*)ctx;
|
|
|
|
DEBUG_PUTS("sending thread start");
|
|
|
|
while (running) {
|
|
_put_rtt(ping->seq_no);
|
|
|
|
if (sock_udp_send(&sock, ping, sizeof(*ping) + payload_size, &remote) < 0) {
|
|
puts("Error sending message");
|
|
continue;
|
|
} else {
|
|
unsigned state = irq_disable();
|
|
ping->seq_no++;
|
|
irq_restore(state);
|
|
}
|
|
|
|
xtimer_usleep(delay_us);
|
|
}
|
|
|
|
DEBUG_PUTS("bench_udp: sending thread terminates");
|
|
sema_inv_post(&thread_sync);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int benchmark_udp_start(const char *server, uint16_t port)
|
|
{
|
|
netif_t *netif;
|
|
sock_udp_ep_t local = { .family = AF_INET6,
|
|
.netif = SOCK_ADDR_ANY_NETIF,
|
|
.port = port };
|
|
sock_udp_ep_t remote = { .family = AF_INET6,
|
|
.port = port };
|
|
|
|
/* stop threads first */
|
|
benchmark_udp_stop();
|
|
|
|
if (sock_udp_create(&sock, &local, NULL, 0) < 0) {
|
|
puts("Error creating UDP sock");
|
|
return 1;
|
|
}
|
|
|
|
if (netutils_get_ipv6((ipv6_addr_t *)&remote.addr.ipv6, &netif, server) < 0) {
|
|
puts("can't resolve remote address");
|
|
return 1;
|
|
}
|
|
if (netif) {
|
|
remote.netif = netif_get_id(netif);
|
|
} else {
|
|
remote.netif = SOCK_ADDR_ANY_NETIF;
|
|
}
|
|
|
|
running = true;
|
|
thread_create(listen_thread_stack, sizeof(listen_thread_stack),
|
|
THREAD_PRIORITY_MAIN - 2, THREAD_CREATE_STACKTEST,
|
|
_listen_thread, NULL, "UDP receiver");
|
|
thread_create(send_thread_stack, sizeof(send_thread_stack),
|
|
THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
|
|
_send_thread, &remote, "UDP sender");
|
|
return 0;
|
|
}
|
|
|
|
bool benchmark_udp_stop(void)
|
|
{
|
|
if (!running) {
|
|
return false;
|
|
}
|
|
|
|
/* signal threads to stop */
|
|
sema_inv_init(&thread_sync, 2);
|
|
running = false;
|
|
|
|
DEBUG_PUTS("bench_udp: waiting for threads to terminate");
|
|
|
|
/* wait for threads to terminate */
|
|
sema_inv_wait(&thread_sync);
|
|
sock_udp_close(&sock);
|
|
|
|
DEBUG_PUTS("bench_udp: threads terminated");
|
|
|
|
/* clear cookie & stack */
|
|
ping->flags = 0;
|
|
memset(send_thread_stack, 0, sizeof(send_thread_stack));
|
|
memset(listen_thread_stack, 0, sizeof(listen_thread_stack));
|
|
|
|
return true;
|
|
}
|
|
|
|
void benchmark_udp_auto_init(void)
|
|
{
|
|
benchmark_udp_start(BENCH_SERVER_DEFAULT, BENCH_PORT_DEFAULT);
|
|
}
|
|
/** @} */
|