From: Breno Leitao <[email protected]>
To: [email protected], [email protected]
Cc: [email protected]
Subject: [PATCH 3/5] tests/socket-getsetsock-cmd: New test for {g,s}etsockopt
Date: Fri, 20 Oct 2023 06:39:15 -0700 [thread overview]
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
Introduce a new test to exercise the io_uring getsockopt and setsockopt
commands.
On the liburing side, use the io_uring_prep_cmd_sock() function to
prepare the SQE.
The test executes the same operation using a regular systemcall, such as
getsockopt(2), and the io_uring getsockopt command and compare the
results. The same for the setsockopt counterpart.
Signed-off-by: Breno Leitao <[email protected]>
---
test/Makefile | 1 +
test/socket-getsetsock-cmd.c | 328 +++++++++++++++++++++++++++++++++++
2 files changed, 329 insertions(+)
create mode 100644 test/socket-getsetsock-cmd.c
diff --git a/test/Makefile b/test/Makefile
index c15b2a3..6b25e58 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -167,6 +167,7 @@ test_srcs := \
skip-cqe.c \
socket.c \
socket-io-cmd.c \
+ socket-getsetsock-cmd.c \
socket-rw.c \
socket-rw-eagain.c \
socket-rw-offset.c \
diff --git a/test/socket-getsetsock-cmd.c b/test/socket-getsetsock-cmd.c
new file mode 100644
index 0000000..5b6aa48
--- /dev/null
+++ b/test/socket-getsetsock-cmd.c
@@ -0,0 +1,328 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Check that {g,s}etsockopt CMD operations on sockets are
+ * consistent.
+ *
+ * The tests basically do the same socket operation using regular system calls
+ * and io_uring commands, and then compare the results.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/tcp.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define USERDATA 0xff42ff
+#define MSG "foobarbaz"
+
+struct fds {
+ int tx;
+ int rx;
+};
+
+static struct fds create_sockets(void)
+{
+ struct fds retval;
+ int fd[2];
+
+ t_create_socket_pair(fd, true);
+
+ retval.tx = fd[0];
+ retval.rx = fd[1];
+
+ return retval;
+}
+
+static struct io_uring create_ring(void)
+{
+ struct io_uring ring;
+ int ring_flags = 0;
+ int err;
+
+ err = io_uring_queue_init(32, &ring, ring_flags);
+ assert(err == 0);
+
+ return ring;
+}
+
+static int submit_cmd_sqe(struct io_uring *ring, int32_t fd,
+ int op, int level, int optname,
+ void *optval, int optlen)
+{
+ struct io_uring_sqe *sqe;
+ int err;
+
+ assert(fd > 0);
+
+ sqe = io_uring_get_sqe(ring);
+ assert(sqe != NULL);
+
+ io_uring_prep_cmd_sock(sqe, op, fd, level, optname, optval, optlen);
+ sqe->user_data = USERDATA;
+
+ /* Submitting SQE */
+ err = io_uring_submit_and_wait(ring, 1);
+ if (err != 1)
+ fprintf(stderr, "Failure: io_uring_submit_and_wait returned %d\n", err);
+
+ return err;
+}
+
+static int receive_cqe(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int err;
+
+ err = io_uring_wait_cqe(ring, &cqe);
+ assert(err == 0);
+ assert(cqe->user_data == USERDATA);
+ io_uring_cqe_seen(ring, cqe);
+
+ /* Return the result of the operation */
+ return cqe->res;
+}
+
+/*
+ * Run getsock operation using SO_RCVBUF using io_uring cmd operation and
+ * getsockopt(2) and compare the results.
+ */
+static int run_get_rcvbuf(struct io_uring *ring, struct fds *sockfds)
+{
+ int sval, uval, ulen, err;
+ unsigned int slen;
+
+ /* System call values */
+ slen = sizeof(sval);
+ /* io_uring values */
+ ulen = sizeof(uval);
+
+ /* get through io_uring cmd */
+ err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT,
+ SOL_SOCKET, SO_RCVBUF, &uval, ulen);
+ assert(err == 1);
+
+ /* Wait for the CQE */
+ err = receive_cqe(ring);
+ if (err == -EOPNOTSUPP)
+ return T_EXIT_SKIP;
+ if (err < 0) {
+ fprintf(stderr, "Error received. %d\n", err);
+ return T_EXIT_FAIL;
+ }
+ /* The output of CQE->res contains the length */
+ ulen = err;
+
+ /* Executes the same operation using system call */
+ err = getsockopt(sockfds->rx, SOL_SOCKET, SO_RCVBUF, &sval, &slen);
+ assert(err == 0);
+
+ /* Make sure that io_uring operation returns the same value as the systemcall */
+ assert(ulen == slen);
+ assert(uval == sval);
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Run getsock operation using SO_PEERNAME using io_uring cmd operation
+ * and getsockopt(2) and compare the results.
+ */
+static int run_get_peername(struct io_uring *ring, struct fds *sockfds)
+{
+ struct sockaddr sval, uval = {};
+ socklen_t slen = sizeof(sval);
+ socklen_t ulen = sizeof(uval);
+ int err;
+
+ /* Get values from the systemcall */
+ err = getsockopt(sockfds->tx, SOL_SOCKET, SO_PEERNAME, &sval, &slen);
+ assert(err == 0);
+
+ /* Getting SO_PEERNAME */
+ err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT,
+ SOL_SOCKET, SO_PEERNAME, &uval, ulen);
+ assert(err == 1);
+
+ /* Wait for the CQE */
+ err = receive_cqe(ring);
+ if (err == -EOPNOTSUPP)
+ return T_EXIT_SKIP;
+
+ if (err < 0) {
+ fprintf(stderr, "%s: Error in the CQE: %d\n", __func__, err);
+ return T_EXIT_FAIL;
+ }
+
+ /* The length comes from cqe->res, which is returned from receive_cqe() */
+ ulen = err;
+
+ /* Make sure that io_uring operation returns the same values as the systemcall */
+ assert(sval.sa_family == uval.sa_family);
+ assert(slen == ulen);
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Run getsockopt tests. Basically comparing io_uring output and systemcall results
+ */
+static int run_getsockopt_test(struct io_uring *ring, struct fds *sockfds)
+{
+ int err;
+
+ fprintf(stderr, "Testing getsockopt SO_PEERNAME\n");
+ err = run_get_peername(ring, sockfds);
+ if (err)
+ return err;
+
+ fprintf(stderr, "Testing getsockopt SO_RCVBUF\n");
+ err = run_get_rcvbuf(ring, sockfds);
+
+ return err;
+}
+
+/*
+ * Given a `val` value, set it in SO_REUSEPORT using io_uring cmd, and read using
+ * getsockopt(2), and make sure they match.
+ */
+static int run_setsockopt_reuseport(struct io_uring *ring, struct fds *sockfds, int val)
+{
+ unsigned int slen, ulen;
+ int sval, uval = val;
+ int err;
+
+ slen = sizeof(sval);
+ ulen = sizeof(uval);
+
+ /* Setting SO_REUSEPORT */
+ err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT,
+ SOL_SOCKET, SO_REUSEPORT, &uval, ulen);
+ assert(err == 1);
+
+ err = receive_cqe(ring);
+ if (err == -EOPNOTSUPP)
+ return T_EXIT_SKIP;
+
+ /* Get values from the systemcall */
+ err = getsockopt(sockfds->rx, SOL_SOCKET, SO_REUSEPORT, &sval, &slen);
+ assert(err == 0);
+
+ /* Make sure the set using io_uring cmd matches what systemcall returns */
+ assert(uval == sval);
+ assert(ulen == slen);
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Given a `val` value, set the TCP_USER_TIMEOUT using io_uring and read using
+ * getsockopt(2). Make sure they match
+ */
+static int run_setsockopt_usertimeout(struct io_uring *ring, struct fds *sockfds, int val)
+{
+ int optname = TCP_USER_TIMEOUT;
+ int level = IPPROTO_TCP;
+ unsigned int slen, ulen;
+ int sval, uval, err;
+
+ slen = sizeof(uval);
+ ulen = sizeof(uval);
+
+ uval = val;
+
+ /* Setting timeout */
+ err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT,
+ level, optname, &uval, ulen);
+ assert(err == 1);
+
+ err = receive_cqe(ring);
+ if (err == -EOPNOTSUPP)
+ return T_EXIT_SKIP;
+ if (err < 0) {
+ fprintf(stderr, "%s: Got an error: %d\n", __func__, err);
+ return T_EXIT_FAIL;
+ }
+
+ /* Get the value from the systemcall, to make sure it was set */
+ err = getsockopt(sockfds->rx, level, optname, &sval, &slen);
+ assert(err == 0);
+ assert(uval == sval);
+
+ return T_EXIT_PASS;
+}
+
+/* Test setsockopt() for SOL_SOCKET */
+static int run_setsockopt_test(struct io_uring *ring, struct fds *sockfds)
+{
+ int err, i;
+
+ fprintf(stderr, "Testing setsockopt SOL_SOCKET/SO_REUSEPORT\n");
+ for (i = 0; i <= 1; i++) {
+ err = run_setsockopt_reuseport(ring, sockfds, i);
+ if (err)
+ return err;
+ }
+
+ fprintf(stderr, "Testing setsockopt IPPROTO_TCP/TCP_FASTOPEN\n");
+ for (i = 1; i <= 10; i++) {
+ err = run_setsockopt_usertimeout(ring, sockfds, i);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+/* Send data throughts the sockets */
+void send_data(struct fds *s)
+{
+ int written_bytes;
+ /* Send data sing the sockstruct->send */
+ written_bytes = write(s->tx, MSG, strlen(MSG));
+ assert(written_bytes == strlen(MSG));
+}
+
+int main(int argc, char *argv[])
+{
+ struct fds sockfds;
+ struct io_uring ring;
+ int err;
+
+ if (argc > 1)
+ return 0;
+
+ /* Simply io_uring ring creation */
+ ring = create_ring();
+
+ /* Create sockets */
+ sockfds = create_sockets();
+
+ send_data(&sockfds);
+
+ err = run_getsockopt_test(&ring, &sockfds);
+ if (err) {
+ if (err == T_EXIT_SKIP) {
+ fprintf(stderr, "Skipping tests.\n");
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "Failed to run test: %d\n", err);
+ return err;
+ }
+
+ err = run_setsockopt_test(&ring, &sockfds);
+ if (err) {
+ if (err == T_EXIT_SKIP) {
+ fprintf(stderr, "Skipping tests.\n");
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "Failed to run test: %d\n", err);
+ return err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return err;
+}
--
2.34.1
next prev parent reply other threads:[~2023-10-20 13:39 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-20 13:39 [PATCH 0/5] liburing: Add {g,s}etsockopt command support Breno Leitao
2023-10-20 13:39 ` [PATCH 1/5] io_uring: uapi: Sync the {g,s}etsockopt fields Breno Leitao
2023-10-20 13:39 ` [PATCH 2/5] liburing.h: Populate SQE for {s,g} etsockopt Breno Leitao
2023-10-20 13:39 ` Breno Leitao [this message]
2023-10-20 13:39 ` [PATCH 4/5] man/io_uring_prep_cmd: Fix argument name Breno Leitao
2023-10-20 13:39 ` [PATCH 5/5] man/io_uring_prep_cmd: Add the new sockopt commands Breno Leitao
2023-10-20 15:06 ` [PATCH 0/5] liburing: Add {g,s}etsockopt command support Jens Axboe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox