1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Test case testing exit without cleanup and io-wq work pending or queued.
4 *
5 * From Florian Fischer <florian.fl.fischer@fau.de>
6 * Link: https://lore.kernel.org/io-uring/20211202165606.mqryio4yzubl7ms5@pasture/
7 *
8 */
9 #include <assert.h>
10 #include <err.h>
11 #include <errno.h>
12 #include <pthread.h>
13 #include <semaphore.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <sys/sysinfo.h>
17 #include <unistd.h>
18
19 #include "liburing.h"
20 #include "helpers.h"
21
22 // on fast enough machines with enough cores, the first few threads will post
23 // enough sem's to cause the main thread to exit while some threads are half way
24 // initialization. This causes a null deference somewhere in thread cleanup,
25 // which trips ASAN.
26 #ifndef CONFIG_USE_SANITIZER
27 #define IORING_ENTRIES 8
28
29 static pthread_t *threads;
30 static pthread_barrier_t init_barrier;
31 static int sleep_fd, notify_fd;
32 static sem_t sem;
33
thread_func(void * arg)34 static void *thread_func(void *arg)
35 {
36 struct io_uring ring;
37 int res;
38
39 res = io_uring_queue_init(IORING_ENTRIES, &ring, 0);
40 if (res)
41 err(EXIT_FAILURE, "io_uring_queue_init failed");
42
43 pthread_barrier_wait(&init_barrier);
44
45 for(;;) {
46 struct io_uring_cqe *cqe;
47 struct io_uring_sqe *sqe;
48 uint64_t buf;
49 int res;
50
51 sqe = io_uring_get_sqe(&ring);
52 assert(sqe);
53
54 io_uring_prep_read(sqe, sleep_fd, &buf, sizeof(buf), 0);
55
56 res = io_uring_submit_and_wait(&ring, 1);
57 if (res < 0)
58 err(EXIT_FAILURE, "io_uring_submit_and_wait failed");
59
60 res = io_uring_peek_cqe(&ring, &cqe);
61 assert(!res);
62 if (cqe->res < 0) {
63 errno = -cqe->res;
64 err(EXIT_FAILURE, "read failed");
65 }
66 assert(cqe->res == sizeof(buf));
67
68 sem_post(&sem);
69
70 io_uring_cqe_seen(&ring, cqe);
71 }
72
73 return NULL;
74 }
75
main(int argc,char * argv[])76 int main(int argc, char *argv[])
77 {
78 int res, fds[2], i, cpus;
79 const uint64_t n = 0x42;
80
81 if (argc > 1)
82 return T_EXIT_SKIP;
83
84 cpus = get_nprocs();
85 res = pthread_barrier_init(&init_barrier, NULL, cpus);
86 if (res)
87 err(EXIT_FAILURE, "pthread_barrier_init failed");
88
89 res = sem_init(&sem, 0, 0);
90 if (res)
91 err(EXIT_FAILURE, "sem_init failed");
92
93 threads = t_malloc(sizeof(pthread_t) * cpus);
94
95 res = pipe(fds);
96 if (res)
97 err(EXIT_FAILURE, "pipe failed");
98
99 sleep_fd = fds[0];
100 notify_fd = fds[1];
101
102 for (i = 0; i < cpus; i++) {
103 errno = pthread_create(&threads[i], NULL, thread_func, NULL);
104 if (errno)
105 err(EXIT_FAILURE, "pthread_create failed");
106 }
107
108 // Write #cpus notifications
109 for (i = 0; i < cpus; i++) {
110 res = write(notify_fd, &n, sizeof(n));
111 if (res < 0)
112 err(EXIT_FAILURE, "write failed");
113 assert(res == sizeof(n));
114 }
115
116 // Await that all notifications were received
117 for (i = 0; i < cpus; i++)
118 sem_wait(&sem);
119
120 // Exit without resource cleanup
121 exit(EXIT_SUCCESS);
122 }
123 #else
main(int argc,char * argv[])124 int main(int argc, char *argv[])
125 {
126 return T_EXIT_SKIP;
127 }
128 #endif
129