pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/upf-benchmark/+/38329?usp=email )
Change subject: Introduce osmo-udp-simpleflood ......................................................................
Introduce osmo-udp-simpleflood
Related: SYS#7096 Change-Id: I389c75054f769b7ca93a56f7085b1694e999aa04 --- M configure.ac M src/Makefile.am A src/osmo-udp-simpleflood/Makefile.am A src/osmo-udp-simpleflood/README.md A src/osmo-udp-simpleflood/osmo_udp_simpleflood_rx.c A src/osmo-udp-simpleflood/osmo_udp_simpleflood_tx.c 6 files changed, 381 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/upf-benchmark refs/changes/29/38329/1
diff --git a/configure.ac b/configure.ac index 4bbc5f6..68f9a41 100644 --- a/configure.ac +++ b/configure.ac @@ -167,6 +167,7 @@ src/Makefile src/osmo-pfcp-tool/Makefile src/osmo-udp-responder/Makefile + src/osmo-udp-simpleflood/Makefile doc/Makefile doc/manuals/Makefile Makefile) diff --git a/src/Makefile.am b/src/Makefile.am index 731fc5e..0005c41 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ osmo-pfcp-tool \ osmo-udp-responder \ + osmo-udp-simpleflood \ $(NULL) diff --git a/src/osmo-udp-simpleflood/Makefile.am b/src/osmo-udp-simpleflood/Makefile.am new file mode 100644 index 0000000..540b047 --- /dev/null +++ b/src/osmo-udp-simpleflood/Makefile.am @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_builddir) \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBURING_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBURING_LIBS) \ + $(NULL) + +bin_PROGRAMS = \ + osmo-udp-simpleflood-rx \ + osmo-udp-simpleflood-tx \ + $(NULL) + +osmo_udp_simpleflood_rx_SOURCES = \ + osmo_udp_simpleflood_rx.c \ + $(NULL) + +osmo_udp_simpleflood_tx_SOURCES = \ + osmo_udp_simpleflood_tx.c \ + $(NULL) diff --git a/src/osmo-udp-simpleflood/README.md b/src/osmo-udp-simpleflood/README.md new file mode 100644 index 0000000..81c60aa --- /dev/null +++ b/src/osmo-udp-simpleflood/README.md @@ -0,0 +1,5 @@ +Simple pair of programs to transmit and receive UDP packets over a single UDP +stream using a single CPU. + +These programs are kept simple on purpose, to be able to test simplest scenario +and to be used as a skeleton for new tools. \ No newline at end of file diff --git a/src/osmo-udp-simpleflood/osmo_udp_simpleflood_rx.c b/src/osmo-udp-simpleflood/osmo_udp_simpleflood_rx.c new file mode 100644 index 0000000..6f7be44 --- /dev/null +++ b/src/osmo-udp-simpleflood/osmo_udp_simpleflood_rx.c @@ -0,0 +1,184 @@ +#include <liburing.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/timer_compat.h> + +#define RECV_SLOTS 32000 +#define BUF_SIZE 1460 + +static struct io_uring ring = {}; +static unsigned long long num_packets_received; +static unsigned long long last_num_packets_received; +static unsigned long long num_bytes_received; +static bool started; +static struct timespec ts_start; + +static int rx_fd; + +struct recv_slot { + struct iovec iov; + uint8_t buf[BUF_SIZE]; + struct msghdr msgh; +}; + +static struct recv_slot recv_slots[RECV_SLOTS]; + +static void prepare_rx(struct recv_slot *s) +{ + s->iov.iov_base = s->buf; + s->iov.iov_len = sizeof(s->buf); + + s->msgh = (struct msghdr){ + .msg_iov = &s->iov, + .msg_iovlen = 1, + }; +} + +static void submit_rx(struct recv_slot *s) +{ + struct io_uring_sqe *sqe; + + sqe = io_uring_get_sqe(&ring); + OSMO_ASSERT(sqe); + io_uring_prep_recvmsg(sqe, rx_fd, &s->msgh, 0); + io_uring_sqe_set_data(sqe, s); +} + +void handle_completion(struct io_uring_cqe *cqe) +{ + struct recv_slot *s; + s = io_uring_cqe_get_data(cqe); + + if (OSMO_UNLIKELY(!started)) { + started = true; + OSMO_ASSERT(clock_gettime(CLOCK_MONOTONIC, &ts_start) == 0); + } + + if (cqe->res <= 0) { + printf("rc = %d\n", cqe->res); + return; + } + + num_bytes_received += cqe->res; + num_packets_received++; + io_uring_cqe_seen(&ring, cqe); + + /* submit more */ + submit_rx(s); +} + +static void log_rx_force(struct timespec *ts_now) +{ + struct timespec ts_elapsed; + timespecsub(ts_now, &ts_start, &ts_elapsed); + unsigned long long elapsed_usec = (ts_elapsed.tv_sec * 1000 * 1000) + (ts_elapsed.tv_nsec / 1000); + if (elapsed_usec == 0) + elapsed_usec = 1; + unsigned long long kpkts_per_sec = num_packets_received * 1000 / elapsed_usec; + unsigned long long mbps = num_bytes_received * 8 / elapsed_usec; + printf("%16llu RX: %8llu packets %16llu bytes %16llu kPPS %16llu Mbps\n", + elapsed_usec, num_packets_received, num_bytes_received, kpkts_per_sec, mbps); + fflush(stdout); +} + +static void log_rx(void) +{ + static struct timespec last_info_log = {.tv_sec = 0, .tv_nsec = 0}; + struct timespec ts_now; + clock_gettime(CLOCK_MONOTONIC, &ts_now); + if (OSMO_UNLIKELY(ts_now.tv_sec != last_info_log.tv_sec)) { + last_info_log = ts_now; + log_rx_force(&ts_now); + } +} + +int main(int argc, const char **argv) +{ + int i; + int rc; + const char *local_addr_str = "0.0.0.0"; + uint16_t local_port = 23000; + + struct osmo_sockaddr_str local_addr = {}; + struct osmo_sockaddr local_osa = {}; + + struct __kernel_timespec ts_zero = {}; + struct __kernel_timespec ts_1s = { .tv_sec = 1 }; + + if (argc >= 2) + local_addr_str = argv[1]; + if (argc >= 3) + local_port = atoi(argv[2]); + + if (osmo_sockaddr_str_from_str(&local_addr, local_addr_str, local_port) + || osmo_sockaddr_str_to_osa(&local_addr, &local_osa)) { + printf("ERROR: invalid address or port number: %s:%d\n", local_addr_str, local_port); + return -1; + } + + /* create and bind socket */ + rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &local_osa, NULL, OSMO_SOCK_F_BIND); + if (rc < 0) + return -1; + rx_fd = rc; + printf("bound UDP %s fd=%d\n", osmo_sock_get_name2(rx_fd), rx_fd); + + rc = io_uring_queue_init(ARRAY_SIZE(recv_slots), &ring, 0); + + /* Prepare */ + for (i = 0; i < ARRAY_SIZE(recv_slots); i++) + prepare_rx(&recv_slots[i]); + + /* fill up tx queue */ + for (i = 0; i < ARRAY_SIZE(recv_slots); i++) + submit_rx(&recv_slots[i]); + + + while (1) { + uint32_t new_submissions; + uint32_t new_completions = 0; + struct io_uring_cqe *cqe; + + /* submit any requests from previous loop */ + new_submissions = io_uring_submit(&ring); + + /* process all pending completions */ + while (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_zero) == 0) { + handle_completion(cqe); + new_completions++; + } + + /* Nothing happened in this loop iteration, so wait a bit longer */ + if (!new_submissions && !new_completions) { + if (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_1s) == 0) { + handle_completion(cqe); + new_completions++; + } + } + + + if (OSMO_UNLIKELY(!started)) + continue; + + if (OSMO_UNLIKELY(last_num_packets_received == num_packets_received)) { +#if 0 + printf("FINISHED!\n"); + struct timespec ts_now; + clock_gettime(CLOCK_MONOTONIC, &ts_now); + log_rx_force(&ts_now); + started = false; + last_num_packets_received = 0; +#endif + continue; + } + + last_num_packets_received = num_packets_received; + log_rx(); + } + + printf("done\n"); + return 0; +} diff --git a/src/osmo-udp-simpleflood/osmo_udp_simpleflood_tx.c b/src/osmo-udp-simpleflood/osmo_udp_simpleflood_tx.c new file mode 100644 index 0000000..be1c9f6 --- /dev/null +++ b/src/osmo-udp-simpleflood/osmo_udp_simpleflood_tx.c @@ -0,0 +1,160 @@ +#include <stdio.h> +#include <limits.h> +#include <liburing.h> +#include <netinet/in.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> + +#define SEND_SLOTS 32000 +#define BUF_SIZE 1452 + +static struct io_uring ring = {}; +static unsigned long long num_packets = ULLONG_MAX; +static unsigned long long num_packets_completed = 0; + +static int src_fd; +static struct osmo_sockaddr remote_osa; + +struct send_slot { + struct iovec iov; + uint8_t buf[BUF_SIZE]; + struct msghdr msgh; +}; + +struct send_slot send_slots[SEND_SLOTS]; + +static void prepare_tx(struct send_slot *s) +{ + s->iov.iov_base = s->buf; + s->iov.iov_len = sizeof(s->buf); + + s->msgh = (struct msghdr){ + .msg_name = &remote_osa, + .msg_namelen = sizeof(remote_osa), + .msg_iov = &s->iov, + .msg_iovlen = 1, + }; +} + +static void submit_tx(struct send_slot *s) +{ + struct io_uring_sqe *sqe; + + sqe = io_uring_get_sqe(&ring); + OSMO_ASSERT(sqe); + io_uring_prep_sendmsg(sqe, src_fd, &s->msgh, 0); + io_uring_sqe_set_data(sqe, s); +} + +static void handle_completion(struct io_uring_cqe *cqe) +{ + struct send_slot *s; + s = io_uring_cqe_get_data(cqe); + + if (cqe->res < 0) { + printf("rc = %d\n", cqe->res); + return; + } + + io_uring_cqe_seen(&ring, cqe); + num_packets_completed++; + + /* submit more */ + submit_tx(s); +} + +int main(int argc, const char **argv) +{ + int i; + int rc; + const char *local_addr_str = "0.0.0.0"; + uint16_t local_port = 42000; + const char *remote_addr_str = "127.0.0.2"; + uint16_t remote_port = 23000; + + struct osmo_sockaddr_str local_addr = {}; + struct osmo_sockaddr local_osa = {}; + + struct osmo_sockaddr_str remote_addr = {}; + + struct __kernel_timespec ts_zero = {}; + struct __kernel_timespec ts_1s = { .tv_sec = 1 }; + + if (argc >= 2) + remote_addr_str = argv[1]; + if (argc >= 3) + remote_port = atoi(argv[2]); + if (argc >= 4) + local_port = atoi(argv[3]); + if (argc >= 5) + num_packets = atoi(argv[4]); + + if (osmo_sockaddr_str_from_str(&local_addr, local_addr_str, local_port) + || osmo_sockaddr_str_to_osa(&local_addr, &local_osa)) { + printf("ERROR: invalid address or port number: %s:%d\n", local_addr_str, local_port); + return -1; + } + + if (osmo_sockaddr_str_from_str(&remote_addr, remote_addr_str, remote_port) + || osmo_sockaddr_str_to_osa(&remote_addr, &remote_osa)) { + printf("ERROR: invalid address or port number: %s:%d\n", remote_addr_str, remote_port); + return -1; + } + + /* create and bind socket */ + rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &local_osa, NULL, OSMO_SOCK_F_BIND); + if (rc < 0) + return -1; + src_fd = rc; + printf("bound UDP %s fd=%d\n", osmo_sock_get_name2(src_fd), src_fd); + + /* Set Don't Fragment (DF) bit on IP packets transmitted by socket: */ + int val = IP_PMTUDISC_DO; + rc = setsockopt(src_fd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); + if (rc == -1) { + fprintf(stderr, "ERROR: setsockopt(IPPROTO_IP, IP_DONTFRAG) failed errno=%d\n", errno); + return -1; + } + + printf("sending %llu UDP packets to %s\n", num_packets, osmo_sockaddr_to_str(&remote_osa)); + + rc = io_uring_queue_init(ARRAY_SIZE(send_slots), &ring, 0); + if (rc < 0) { + fprintf(stderr, "ERROR: io_uring_queue_init errno=%d\n", -rc); + return -1; + } + + /* Prepare */ + for (i = 0; i < ARRAY_SIZE(send_slots); i++) + prepare_tx(&send_slots[i]); + /* fill up tx queue */ + for (i = 0; i < ARRAY_SIZE(send_slots); i++) + submit_tx(&send_slots[i]); + + while (num_packets_completed < num_packets) { + uint32_t new_submissions; + uint32_t new_completions = 0; + struct io_uring_cqe *cqe; + + /* submit any requests from previous loop */ + new_submissions = io_uring_submit(&ring); + + /* process all pending completions */ + while (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_zero) == 0) { + handle_completion(cqe); + new_completions++; + + } + + /* Nothing happened in this loop iteration, so wait a bit longer */ + if (!new_submissions && !new_completions) { + if (io_uring_wait_cqe_timeout(&ring, &cqe, &ts_1s) == 0) + handle_completion(cqe); + } + } + + printf("done\n"); + return 0; +}