Commit 5b22c6a9 authored by Gilles Chanteperdrix's avatar Gilles Chanteperdrix

testsuite/smokey: add RTnet testsuite

Starting with UDP and raw sockets.
Measuring round trip time and checking for lost packets.
parent 3cf57440
......@@ -891,6 +891,9 @@ AC_CONFIG_FILES([ \
testsuite/smokey/timerfd/Makefile \
testsuite/smokey/tsc/Makefile \
testsuite/smokey/leaks/Makefile \
testsuite/smokey/net_udp/Makefile \
testsuite/smokey/net_packet_dgram/Makefile \
testsuite/smokey/net_common/Makefile \
testsuite/clocktest/Makefile \
testsuite/xeno-test/Makefile \
utils/Makefile \
......
......@@ -10,6 +10,9 @@ COBALT_SUBDIRS = \
bufp \
iddp \
leaks \
net_packet_dgram\
net_udp \
net_common \
posix-clock \
posix-cond \
posix-fork \
......
bin_PROGRAMS = smokey_net_server
noinst_LIBRARIES = libnet_common.a
noinst_HEADERS = smokey_net.h
AM_CPPFLAGS = \
@XENO_USER_CFLAGS@ \
-I$(top_srcdir)/include \
-I$(top_srcdir)/kernel/drivers/net/stack/include
libnet_common_a_SOURCES = \
client.c \
server.c \
setup.c
smokey_net_server_SOURCES = \
smokey_net_server.c
smokey_net_server_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@
smokey_net_server_LDADD = \
libnet_common.a \
$(top_builddir)/lib/@XENO_CORE_LIB@ \
@XENO_USER_LDADD@ \
-lpthread -lrt
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <sys/socket.h>
#include <sys/select.h>
#include "smokey_net.h"
static int duration = 10;
static int rate = 1000;
static const char *driver = "rt_loopback";
static const char *intf;
static pthread_t tid;
static unsigned long long glost, glate;
static int rcv_packet(struct smokey_net_client *client, int sock, unsigned seq,
struct timespec *next_shot, bool last)
{
static unsigned long long gmin = ~0ULL, gmax = 0, gsum = 0, gcount = 0;
static unsigned long long min = ~0ULL, max = 0, sum = 0, count = 0,
lost = 0, late = 0;
static struct timespec last_print;
struct smokey_net_payload payload;
struct timeval timeout;
struct timespec now;
char packet[256];
long long diff;
fd_set set;
int err;
FD_ZERO(&set);
FD_SET(sock, &set);
err = smokey_check_errno(
__RT(clock_gettime(CLOCK_MONOTONIC, &now)));
if (err < 0)
return err;
diff = next_shot->tv_sec * 1000000000ULL + next_shot->tv_nsec
- (now.tv_sec * 1000000000ULL + now.tv_nsec);
if (diff < 0)
diff = 0;
timeout.tv_sec = diff / 1000000000;
timeout.tv_usec = (diff % 1000000000 + 500) / 1000;
err = smokey_check_errno(
__RT(select(sock + 1, &set, NULL, NULL, &timeout)));
if (err < 0)
return err;
if (err == 0) {
if (seq)
++lost;
err = -ETIMEDOUT;
goto print_stats;
}
err = smokey_check_errno(
__RT(recv(sock, packet, sizeof(packet), 0)));
if (err < 0)
return err;
err = client->extract(client, &payload, packet, err);
if (err < 0)
return err;
err = smokey_check_errno(
__RT(clock_gettime(CLOCK_MONOTONIC, &now)));
if (err < 0)
return err;
diff = now.tv_sec * 1000000000ULL + now.tv_nsec
- (payload.ts.tv_sec * 1000000000ULL
+ payload.ts.tv_nsec);
if (diff < min)
min = diff;
if (diff > max)
max = diff;
sum += diff;
++count;
err = 0;
if (payload.seq != seq) {
++late;
err = -EAGAIN;
}
print_stats:
if (seq == 1 && !last_print.tv_sec) {
last_print = now;
if (last_print.tv_nsec < 1000000000 / rate) {
last_print.tv_nsec += 1000000000;
last_print.tv_sec--;
}
last_print.tv_nsec -= 1000000000 / rate;
}
diff = now.tv_sec * 1000000000ULL + now.tv_nsec
- (last_print.tv_sec * 1000000000ULL
+ last_print.tv_nsec);
if (diff < 1000000000LL && (!last || (!count && !lost)))
return err;
if (min < gmin)
gmin = min;
if (max > gmax)
gmax = max;
gsum += sum;
gcount += count;
glost += lost - late;
glate += late;
smokey_trace("%g pps\t%Lu\t%Lu\t%.03gus\t%.03gus\t%.03gus\t"
"| %Lu\t%Lu\t%.03gus\t%.03gus\t%.03gus",
count / (diff / 1000000000.0),
lost - late,
late,
count ? min / 1000.0 : 0,
count ? (sum / (double)count) / 1000 : 0,
count ? max / 1000.0 : 0,
glost,
glate,
gcount ? gmin / 1000.0 : 0,
gcount ? (gsum / (double)gcount) / 1000 : 0,
gcount ? gmax / 1000.0 : 0);
min = ~0ULL;
max = 0;
sum = 0;
count = 0;
lost = 0;
late = 0;
last_print = now;
return err;
}
static int smokey_net_client_loop(struct smokey_net_client *client)
{
struct smokey_net_payload payload;
struct timespec next_shot;
struct sched_param prio;
char packet[256];
long long limit;
int sock, err;
sock = client->create_socket(client);
if (sock < 0)
return sock;
prio.sched_priority = 20;
err = smokey_check_status(
pthread_setschedparam(pthread_self(), SCHED_FIFO, &prio));
if (err < 0)
return err;
err = smokey_check_errno(
__RT(clock_gettime(CLOCK_MONOTONIC, &next_shot)));
if (err < 0)
goto err;
limit = (long long)rate * duration;
for (payload.seq = 1;
limit <= 0 || payload.seq < limit + 1; payload.seq++) {
unsigned seq = payload.seq;
next_shot.tv_nsec += 1000000000 / rate;
if (next_shot.tv_nsec > 1000000000) {
next_shot.tv_nsec -= 1000000000;
next_shot.tv_sec++;
}
err = smokey_check_errno(
__RT(clock_gettime(CLOCK_MONOTONIC, &payload.ts)));
if (err < 0)
goto err;
err = client->prepare(client, packet, sizeof(packet), &payload);
if (err < 0)
goto err;
err = smokey_check_errno(
__RT(sendto(sock, packet, err, 0,
&client->peer, client->peer_len)));
if (err < 0)
goto err;
do {
err = rcv_packet(client, sock, seq, &next_shot,
payload.seq == limit);
if (!err)
seq = 0;
} while (err != -ETIMEDOUT);
}
if (glost || glate)
fprintf(stderr, "RTnet %s test failed", client->name);
if (glost) {
if (glost == limit)
fprintf(stderr, ", all packets lost"
" (is smokey_net_server running ?)");
else
fprintf(stderr, ", %Lu packets lost (%g %%)",
glost, 100.0 * glost / limit);
}
if (glate)
fprintf(stderr, ", %Lu overruns", glate);
if (glost || glate)
fputc('\n', stderr);
err = glost || glate ? -EPROTO : 0;
err:
sock = smokey_check_errno(__RT(close(sock)));
if (err == 0)
err = sock;
return err;
}
static void *trampoline(void *cookie)
{
int err = smokey_net_client_loop(cookie);
pthread_exit((void *)(long)err);
}
int smokey_net_client_run(struct smokey_test *t,
struct smokey_net_client *client,
int argc, char *const argv[])
{
int err, err_teardown;
void *status;
smokey_parse_args(t, argc, argv);
if (SMOKEY_ARG_ISSET(*t, rtnet_driver))
driver = SMOKEY_ARG_STRING(*t, rtnet_driver);
if (SMOKEY_ARG_ISSET(*t, rtnet_interface))
intf = SMOKEY_ARG_STRING(*t, rtnet_interface);
if (SMOKEY_ARG_ISSET(*t, rtnet_duration))
duration = SMOKEY_ARG_INT(*t, rtnet_duration);
if (SMOKEY_ARG_ISSET(*t, rtnet_rate)) {
rate = SMOKEY_ARG_INT(*t, rtnet_rate);
if (rate == 0) {
smokey_warning("rate can not be null");
return -EINVAL;
}
}
if (!intf)
intf = strcmp(driver, "rt_loopback") ? "rteth0" : "rtlo";
smokey_trace("Configuring interface %s (driver %s) for RTnet %s test",
intf, driver, client->name);
err = smokey_net_setup(driver, intf, client->option, &client->peer);
if (err < 0)
return err;
smokey_trace("Running RTnet %s test on interface %s",
client->name, intf);
err = smokey_check_status(
__RT(pthread_create(&tid, NULL, trampoline, client)));
if (err < 0)
return err;
err = smokey_check_status(pthread_join(tid, &status));
if (err < 0)
return err;
err = (int)(long)status;
err_teardown = smokey_net_teardown(driver, intf, client->option);
if (err == 0)
err = err_teardown;
return err;
}
/*
* RTnet test server loop
*
* Copyright (C) 2015 Gilles Chanteperdrix <gch@xenomai.org>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sched.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <sys/cobalt.h>
#include <smokey/smokey.h>
#include "smokey_net.h"
#include "smokey_net_server.h"
struct smokey_server {
sem_t sync;
int fds[0];
};
struct proto {
int config_flag;
int (*create_socket)(void);
void (*serve)(int fd);
};
static int udp_create_socket(void);
static void udp_serve(int fd);
static int packet_dgram_socket(void);
static void packet_dgram_serve(int fd);
static const struct proto protos[] = {
{
.config_flag = _CC_COBALT_NET_UDP,
.create_socket = &udp_create_socket,
.serve = &udp_serve,
},
{
.config_flag = _CC_COBALT_NET_AF_PACKET,
.create_socket = &packet_dgram_socket,
.serve = &packet_dgram_serve,
},
};
static int udp_create_socket(void)
{
struct sockaddr_in name;
int fd;
fd = check_unix(__RT(socket(PF_INET, SOCK_DGRAM, 0)));
name.sin_family = AF_INET;
name.sin_port = htons(7); /* UDP echo service */
name.sin_addr.s_addr = htonl(INADDR_ANY);
check_unix(__RT(bind(fd, (struct sockaddr *)&name, sizeof(name))));
return fd;
}
static void udp_serve(int fd)
{
struct smokey_net_payload pkt;
struct sockaddr_in peer;
socklen_t peer_len;
int err;
peer_len = sizeof(peer);
err = check_unix(
__RT(recvfrom(fd, &pkt, sizeof(pkt), 0,
(struct sockaddr *)&peer, &peer_len)));
check_unix(
__RT(sendto(fd, &pkt, err, 0,
(struct sockaddr *)&peer, peer_len)));
}
static int packet_dgram_socket(void)
{
return check_unix(
__RT(socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_802_EX1))));
}
static void packet_dgram_serve(int fd)
{
struct smokey_net_payload pkt;
struct sockaddr_ll peer;
socklen_t peer_len;
int err;
peer_len = sizeof(peer);
err = check_unix(
__RT(recvfrom(fd, &pkt, sizeof(pkt), 0,
(struct sockaddr *)&peer, &peer_len)));
peer.sll_protocol = htons(ETH_P_802_EX1 + 1);
check_unix(
__RT(sendto(fd, &pkt, err, 0,
(struct sockaddr *)&peer, peer_len)));
}
static void server_loop_cleanup(void *cookie)
{
int *fds = cookie;
int i;
for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++)
__RT(close(fds[i]));
free(fds);
}
void smokey_net_server_loop(int net_config)
{
struct sched_param prio;
const struct proto *p;
int i, maxfd, *fds;
fd_set rfds;
fds = malloc(sizeof(*fds) * sizeof(protos)/sizeof(protos[0]));
if (fds == NULL)
pthread_exit((void *)(long)-ENOMEM);
pthread_cleanup_push(server_loop_cleanup, fds);
FD_ZERO(&rfds);
maxfd = 0;
for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) {
p = &protos[i];
if ((net_config & p->config_flag) == 0) {
fds[i] = -1;
continue;
}
fds[i] = p->create_socket();
FD_SET(fds[i], &rfds);
if (fds[i] > maxfd)
maxfd = fds[i];
}
prio.sched_priority = 20;
check_pthread(
__RT(pthread_setschedparam(pthread_self(), SCHED_FIFO, &prio)));
for (;;) {
fd_set tfds;
tfds = rfds;
check_unix(__RT(select(maxfd + 1, &tfds, NULL, NULL, NULL)));
for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) {
p = &protos[i];
if (fds[i] < 0 || !FD_ISSET(fds[i], &tfds))
continue;
p->serve(fds[i]);
}
}
pthread_cleanup_pop(1);
}
This diff is collapsed.
/*
* Copyright (C) 2015 Gilles Chanteperdrix <gch@xenomai.org>
*
* SPDX-License-Identifier: MIT
*/
#ifndef SMOKEY_NET_H
#define SMOKEY_NET_H
#include <time.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <netinet/in.h>
#include <smokey/smokey.h>
struct smokey_net_payload {
struct timespec ts;
unsigned seq;
};
struct smokey_net_client {
const char *name;
int option;
union {
struct sockaddr peer;
struct sockaddr_ll ll_peer;
struct sockaddr_in in_peer;
};
socklen_t peer_len;
int (*create_socket)(struct smokey_net_client *client);
int (*prepare)(struct smokey_net_client *client,
void *buf, size_t len,
const struct smokey_net_payload *payload);
int (*extract)(struct smokey_net_client *client,
struct smokey_net_payload *payload,
const void *buf, size_t len);
};
int smokey_net_setup(const char *driver, const char *intf, int tested_config,
void *vpeer);
int smokey_net_teardown(const char *driver,
const char *intf, int tested_config);
int smokey_net_client_run(struct smokey_test *t,
struct smokey_net_client *client,
int argc, char *const argv[]);
#endif /* SMOKEY_NET_H */
/*
* RTnet test server
*
* Copyright (C) 2015 Gilles Chanteperdrix <gch@xenomai.org>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rtcfg_chrdev.h>
#include <sys/cobalt.h>
#include <smokey/smokey.h>
#include <xenomai/init.h>
#include "smokey_net.h"
#include "smokey_net_server.h"
static const char *intf = "rteth0";
int smokey_net_server_check_inner(const char *file, int line,
const char *msg, int status)
{
if (status >= 0)
return status;
fprintf(stderr, "FAILED %s: returned error %d - %s\n",
msg, -status, strerror(-status));
exit(EXIT_FAILURE);
}
static int rtnet_rtcfg_setup_server(void)
{
struct rtcfg_cmd cmd;
int fd;
memset(&cmd, 0, sizeof(cmd));
cmd.args.server.period = 1000;
cmd.args.server.burstrate = 4;
cmd.args.server.heartbeat = 1000;
cmd.args.server.threshold = 2;
cmd.args.server.flags = 0;
check_unix(snprintf(cmd.head.if_name, sizeof(cmd.head.if_name),
intf, sizeof(cmd.head.if_name)));
fd = check_unix(open("/dev/rtnet", O_RDWR));
check_unix(ioctl(fd, RTCFG_IOC_SERVER, &cmd));
return fd;
}
static void
rtnet_rtcfg_add_client(int fd, const char *hwaddr, const char *ipaddr)
{
struct rtcfg_cmd cmd;
struct ether_addr mac;