123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * Test that the final close of a file does indeed get it closed, if the
- * ring is setup with DEFER_TASKRUN and the task is waiting in cqring_wait()
- * during. Also see:
- *
- * https://github.com/axboe/liburing/issues/1235
- *
- * for a bug report, and the zig code on which this test program is based.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <signal.h>
- #include <pthread.h>
- #include "liburing.h"
- #include "helpers.h"
- enum {
- IS_ACCEPT = 0,
- IS_SEND = 0x100,
- IS_SEND2 = 0x101,
- IS_SEND3 = 0x102,
- IS_CLOSE = 0x200,
- };
- struct thread_data {
- int parent_pid;
- };
- static void *thread_fn(void *__data)
- {
- struct thread_data *data = __data;
- struct sockaddr_in saddr;
- int sockfd, ret;
- char msg[64];
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(9999);
- inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) {
- perror("socket");
- goto done;
- }
- ret = connect(sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
- if (ret < 0) {
- perror("connect");
- close(sockfd);
- goto done;
- }
- do {
- memset(msg, 0, sizeof(msg));
- ret = recv(sockfd, msg, sizeof(msg), 0);
- } while (ret > 0);
- close(sockfd);
- done:
- kill(data->parent_pid, SIGUSR1);
- return NULL;
- }
- /* we got SIGUSR1, exit normally */
- static void sig_usr1(int sig)
- {
- exit(T_EXIT_PASS);
- }
- /* timed out, failure */
- static void sig_timeout(int sig)
- {
- exit(T_EXIT_FAIL);
- }
- int main(int argc, char *argv[])
- {
- struct io_uring ring;
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- struct sockaddr_in saddr;
- char *msg1 = "message number 1\n";
- char *msg2 = "message number 2\n";
- char *msg3 = "message number 3\n";
- int val, send_fd, ret, sockfd;
- struct sigaction act[2] = { };
- struct thread_data td;
- pthread_t thread;
- if (argc > 1)
- return T_EXIT_SKIP;
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
- saddr.sin_port = htons(9999);
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) {
- perror("socket");
- return T_EXIT_FAIL;
- }
- val = 1;
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
- ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
- if (ret < 0) {
- perror("bind");
- close(sockfd);
- return T_EXIT_FAIL;
- }
- ret = listen(sockfd, 1);
- if (ret < 0) {
- perror("listen");
- close(sockfd);
- return T_EXIT_FAIL;
- }
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN);
- if (ret == -EINVAL) {
- close(sockfd);
- return T_EXIT_SKIP;
- }
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_multishot_accept(sqe, sockfd, NULL, NULL, 0);
- sqe->user_data = IS_ACCEPT;
- io_uring_submit(&ring);
- /* check for no multishot accept */
- ret = io_uring_peek_cqe(&ring, &cqe);
- if (!ret && cqe->res == -EINVAL) {
- close(sockfd);
- return T_EXIT_SKIP;
- }
- /* expected exit */
- act[0].sa_handler = sig_usr1;
- sigaction(SIGUSR1, &act[0], NULL);
- /* if this hits, we have failed */
- act[1].sa_handler = sig_timeout;
- sigaction(SIGALRM, &act[1], NULL);
- alarm(5);
-
- /* start receiver */
- td.parent_pid = getpid();
- pthread_create(&thread, NULL, thread_fn, &td);
- do {
- ret = io_uring_submit_and_wait(&ring, 1);
- if (ret < 0) {
- fprintf(stderr, "submit: %d\n", ret);
- return T_EXIT_FAIL;
- }
- ret = io_uring_peek_cqe(&ring, &cqe);
- if (ret) {
- fprintf(stderr, "peek: %d\n", ret);
- return T_EXIT_FAIL;
- }
- switch (cqe->user_data) {
- case IS_ACCEPT:
- send_fd = cqe->res;
- io_uring_cqe_seen(&ring, cqe);
- /*
- * prep two sends, with the 2nd linked to a close
- * operation. Once the close has been completed, that
- * will terminate the receiving thread and that will
- * in turn send this task a SIGUSR1 signal. If the
- * kernel is buggy, then we never get SIGUSR1 and we
- * will sit forever waiting and be timed out.
- */
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_send(sqe, send_fd, msg1, strlen(msg1), 0);
- sqe->user_data = IS_SEND;
- sqe->flags = IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_LINK;
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_send(sqe, send_fd, msg2, strlen(msg2), 0);
- sqe->user_data = IS_SEND2;
- sqe->flags = IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_LINK;
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_send(sqe, send_fd, msg3, strlen(msg3), 0);
- sqe->user_data = IS_SEND3;
- sqe->flags = IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_LINK;
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_close(sqe, send_fd);
- sqe->user_data = IS_CLOSE;
- sqe->flags = IOSQE_CQE_SKIP_SUCCESS;
- break;
- case IS_SEND:
- case IS_SEND2:
- fprintf(stderr, "Should not see send response\n");
- io_uring_cqe_seen(&ring, cqe);
- return T_EXIT_FAIL;
- case IS_CLOSE:
- fprintf(stderr, "Should not see close response\n");
- io_uring_cqe_seen(&ring, cqe);
- return T_EXIT_FAIL;
- default:
- fprintf(stderr, "got unknown cqe\n");
- return T_EXIT_FAIL;
- }
- } while (1);
- /* will never get here */
- return T_EXIT_FAIL;
- }
|