This is implemented as a server/client tool that only uses io_uring commands for setup. Signed-off-by: Gabriel Krisman Bertazi <krisman@xxxxxxx> --- test/Makefile | 1 + test/bind-listen.c | 231 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 test/bind-listen.c diff --git a/test/Makefile b/test/Makefile index fcf6554..a47ca6f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -52,6 +52,7 @@ test_srcs := \ across-fork.c \ b19062a56726.c \ b5837bd5311d.c \ + bind-listen.c \ buf-ring.c \ buf-ring-nommap.c \ buf-ring-put.c \ diff --git a/test/bind-listen.c b/test/bind-listen.c new file mode 100644 index 0000000..cd1e386 --- /dev/null +++ b/test/bind-listen.c @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: MIT */ +/* + * echo server using solely io_uring operations + */ +#include <stdio.h> +#include <string.h> +#include <liburing.h> +#include <err.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <unistd.h> +#include <stdlib.h> +#include <netinet/ip.h> +#include <pthread.h> + +#include "liburing.h" +#include "helpers.h" + +static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec) +{ + ts->tv_sec = msec / 1000; + ts->tv_nsec = (msec % 1000) * 1000000; +} + +struct srv_data { + pthread_mutex_t mutex; +}; +struct sockaddr_in server_addr; + +const char *magic = "Hello World!"; + +static void *do_server(void *data) +{ + struct srv_data *rd = data; + + struct io_uring_params p = { }; + struct __kernel_timespec ts; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + int ret, conn, sock_index; + unsigned head; + int fd, val; + char buf[1024]; + + ret = t_create_ring_params(4, &ring, &p); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", strerror(-ret)); + goto err; + } + + ret = io_uring_register_files(&ring, &fd, 1); + if (ret) { + fprintf(stderr, "file register %d\n", ret); + goto err; + } + + memset(&server_addr, 0, sizeof(struct sockaddr_in)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(8000); + server_addr.sin_addr.s_addr = htons(INADDR_ANY); + + sock_index = 0; + sqe = io_uring_get_sqe(&ring); + io_uring_prep_socket_direct(sqe, AF_INET, SOCK_STREAM, 0, + sock_index, 0); + + sqe = io_uring_get_sqe(&ring); + val = 1; + io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, 0, + SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_bind(sqe, sock_index, (struct sockaddr *) &server_addr, + sizeof(struct sockaddr_in)); + sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_listen(sqe, sock_index, 1); + sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK; + + ret = io_uring_submit(&ring); + if (ret < 0) { + printf("submission failed. %d\n", ret); + goto err; + } + + msec_to_ts(&ts, 300); + ret = io_uring_wait_cqes(&ring, &cqe, 4, &ts, NULL); + if (ret < 0) { + printf("submission failed. %d\n", ret); + goto err; + } + + io_uring_for_each_cqe(&ring, head, cqe) { + if (cqe->res < 0) { + printf("Server startup failed. step %d got %d \n", head, cqe->res); + goto err; + } + } + io_uring_cq_advance(&ring, 4); + + pthread_mutex_unlock(&rd->mutex); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_accept(sqe, sock_index, NULL, NULL, 0); + sqe->flags |= IOSQE_FIXED_FILE; + + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + + + if (cqe->res < 0) { + printf("accept failed. %d\n", cqe->res); + goto err; + } + conn = cqe->res; + io_uring_cqe_seen(&ring, cqe); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_recv(sqe, conn, buf, BUFSIZ, 0); + io_uring_submit(&ring); + io_uring_wait_cqe_timeout(&ring, &cqe, &ts); + + if (cqe->res < 0) { + printf("bad receive cqe. %d\n", cqe->res); + goto err; + } + val = cqe->res; + + if (val != strlen(magic) || strncmp(buf, magic, val)) { + printf("didn't receive expected string\n"); + ret = -1; + goto err; + } + + io_uring_queue_exit(&ring); + + ret = 0; +err: + return (void *)(intptr_t)ret; +} + +static int do_client() +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct sockaddr_in peer_addr; + socklen_t addr_len = sizeof(peer_addr); + struct io_uring ring; + int ret, fd = -1, sock_index; + int i; + + ret = io_uring_queue_init(3, &ring, 0); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", strerror(-ret)); + return -1; + } + + ret = io_uring_register_files(&ring, &fd, 1); + if (ret) { + fprintf(stderr, "file register %d\n", ret); + goto err; + } + + + peer_addr.sin_family = AF_INET; + peer_addr.sin_port = server_addr.sin_port; + peer_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + sock_index = 0; + sqe = io_uring_get_sqe(&ring); + io_uring_prep_socket_direct(sqe, AF_INET, SOCK_STREAM, 0, + sock_index, 0); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_connect(sqe, sock_index, (struct sockaddr*) &peer_addr, addr_len); + sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(&ring); + + io_uring_prep_send(sqe, sock_index, magic, strlen(magic), 0); + sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK; + + io_uring_submit(&ring); + io_uring_wait_cqe_nr(&ring, &cqe, 3); + + io_uring_for_each_cqe(&ring, i, cqe) { + if (cqe->res < 0) { + printf("client cqe. idx=%d, %d\n", i, cqe->res); + } + } + io_uring_cq_advance(&ring, 2); + + return 0; +err: + return -1; +} + +int main(int argc, char *argv[]) +{ + pthread_mutexattr_t attr; + pthread_t srv_thread; + struct srv_data srv_data; + int ret; + void *retval; + + if (argc > 1) + return 0; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, 1); + pthread_mutex_init(&srv_data.mutex, &attr); + pthread_mutex_lock(&srv_data.mutex); + + ret = pthread_create(&srv_thread, NULL, do_server, &srv_data); + if (ret) { + fprintf(stderr, "Thread create failed: %d\n", ret); + pthread_mutex_unlock(&srv_data.mutex); + return 1; + } + pthread_mutex_lock(&srv_data.mutex); + do_client(); + + pthread_join(srv_thread, &retval); + return (intptr_t)retval; +} + + -- 2.44.0