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;
+}
--
To view, visit
https://gerrit.osmocom.org/c/upf-benchmark/+/38329?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: upf-benchmark
Gerrit-Branch: master
Gerrit-Change-Id: I389c75054f769b7ca93a56f7085b1694e999aa04
Gerrit-Change-Number: 38329
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>