#include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* * Description: verify that timestamp retrieval that needs retrying with * a current list of SKBs works. * * Based on a test case from Google Big Sleep. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "liburing.h" #include "helpers.h" /* Compatibility with slightly older kernel headers */ #ifndef SOCKET_URING_OP_TX_TIMESTAMP #define SOCKET_URING_OP_TX_TIMESTAMP 4 #endif /* * Create a socket whose error queue contains both timestamp information and * actual errors. */ static int create_sock_with_timestamps_and_errors(void) { struct sockaddr_in recv_addr = { }; struct sockaddr_in closed = { }; int recv_sock, sock, val; char data = 'A'; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) err(1, "socket"); /* Enable TX timestamps */ val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_OPT_ID | SOF_TIMESTAMPING_OPT_TSONLY; if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)) < 0) err(1, "setsockopt(SO_TIMESTAMPING)"); /* Enable IP_RECVERR */ val = 1; if (setsockopt(sock, IPPROTO_IP, IP_RECVERR, &val, sizeof(val)) < 0) err(1, "setsockopt(IP_RECVERR)"); recv_sock = socket(AF_INET, SOCK_DGRAM, 0); if (recv_sock < 0) err(1, "socket"); recv_addr.sin_family = AF_INET; recv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(recv_sock, (struct sockaddr*)&recv_addr, sizeof(recv_addr)) < 0) err(1, "bind receiver"); socklen_t recv_len = sizeof(recv_addr); if (getsockname(recv_sock, (struct sockaddr*)&recv_addr, &recv_len) < 0) err(1, "getsockname"); /* Send packets to generate TX timestamps. */ for (int i = 0; i < 2; i++) { if (sendto(sock, &data, sizeof(data), 0, (struct sockaddr*)&recv_addr, sizeof(recv_addr)) < 0) err(1, "sendto (%d)", i); } closed.sin_family = AF_INET; closed.sin_addr.s_addr = inet_addr("127.0.0.1"); closed.sin_port = htons(9); /* assuming no one is listening on port 9 */ if (sendto(sock, &data, sizeof(data), 0, (struct sockaddr*) &closed, sizeof(closed)) < 0) warn("sendto"); return sock; } int main(int argc, char *argv[]) { struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; struct io_uring ring; struct io_uring_params params = { .flags = IORING_SETUP_CQE32, /* required for timestamps */ }; char buf[1024], ctrl_recv[1024]; struct iovec iov_recv = { .iov_base = buf, .iov_len = sizeof(buf) }; struct msghdr msg_recv = { .msg_iov = &iov_recv, .msg_iovlen = 1, .msg_control = ctrl_recv, .msg_controllen = sizeof(ctrl_recv), }; int sock, ret; if (argc > 1) return T_EXIT_SKIP; sock = create_sock_with_timestamps_and_errors(); ret = io_uring_queue_init_params(1, &ring, ¶ms); if (ret < 0) { if (ret == -EINVAL) return T_EXIT_SKIP; err(1, "io_uring_queue_init_params: %d", ret); } sqe = io_uring_get_sqe(&ring); *sqe = (struct io_uring_sqe) { .opcode = IORING_OP_URING_CMD, .fd = sock, .cmd_op = SOCKET_URING_OP_TX_TIMESTAMP, }; io_uring_submit(&ring); ret = io_uring_wait_cqe(&ring, &cqe); if (ret < 0) errx(1, "io_uring_wait_cqe: %d", ret); ret = cqe->res; io_uring_cqe_seen(&ring, cqe); /* kernel doesn't support retrieving timestamps */ if (ret == -EOPNOTSUPP) return T_EXIT_SKIP; recvmsg(sock, &msg_recv, MSG_ERRQUEUE | MSG_DONTWAIT); return T_EXIT_PASS; }