• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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