uringmachine 0.19.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/TODO.md +0 -1
  4. data/examples/bm_fileno.rb +33 -0
  5. data/examples/bm_mutex.rb +85 -0
  6. data/examples/bm_mutex_single.rb +33 -0
  7. data/examples/bm_queue.rb +27 -28
  8. data/examples/bm_send.rb +2 -5
  9. data/examples/bm_snooze.rb +20 -42
  10. data/examples/fiber_scheduler_demo.rb +15 -51
  11. data/examples/fiber_scheduler_fork.rb +24 -0
  12. data/examples/nc_ssl.rb +71 -0
  13. data/ext/um/extconf.rb +5 -15
  14. data/ext/um/um.c +57 -41
  15. data/ext/um/um.h +21 -11
  16. data/ext/um/um_async_op_class.c +2 -2
  17. data/ext/um/um_buffer.c +1 -1
  18. data/ext/um/um_class.c +94 -23
  19. data/ext/um/um_const.c +51 -3
  20. data/ext/um/um_mutex_class.c +1 -1
  21. data/ext/um/um_queue_class.c +1 -1
  22. data/ext/um/um_stream.c +5 -5
  23. data/ext/um/um_stream_class.c +3 -0
  24. data/ext/um/um_sync.c +22 -27
  25. data/ext/um/um_utils.c +59 -19
  26. data/grant-2025/journal.md +229 -0
  27. data/grant-2025/tasks.md +66 -0
  28. data/lib/uringmachine/fiber_scheduler.rb +180 -48
  29. data/lib/uringmachine/version.rb +1 -1
  30. data/lib/uringmachine.rb +6 -0
  31. data/test/test_fiber_scheduler.rb +138 -0
  32. data/test/test_stream.rb +2 -2
  33. data/test/test_um.rb +427 -34
  34. data/vendor/liburing/.github/workflows/ci.yml +94 -1
  35. data/vendor/liburing/.github/workflows/test_build.c +9 -0
  36. data/vendor/liburing/configure +27 -0
  37. data/vendor/liburing/examples/Makefile +6 -0
  38. data/vendor/liburing/examples/helpers.c +8 -0
  39. data/vendor/liburing/examples/helpers.h +5 -0
  40. data/vendor/liburing/liburing.spec +1 -1
  41. data/vendor/liburing/src/Makefile +9 -3
  42. data/vendor/liburing/src/include/liburing/barrier.h +11 -5
  43. data/vendor/liburing/src/include/liburing/io_uring/query.h +41 -0
  44. data/vendor/liburing/src/include/liburing/io_uring.h +50 -0
  45. data/vendor/liburing/src/include/liburing/sanitize.h +16 -4
  46. data/vendor/liburing/src/include/liburing.h +445 -121
  47. data/vendor/liburing/src/liburing-ffi.map +15 -0
  48. data/vendor/liburing/src/liburing.map +8 -0
  49. data/vendor/liburing/src/sanitize.c +4 -1
  50. data/vendor/liburing/src/setup.c +7 -4
  51. data/vendor/liburing/test/232c93d07b74.c +4 -16
  52. data/vendor/liburing/test/Makefile +15 -1
  53. data/vendor/liburing/test/accept.c +2 -13
  54. data/vendor/liburing/test/conn-unreach.c +132 -0
  55. data/vendor/liburing/test/fd-pass.c +32 -7
  56. data/vendor/liburing/test/fdinfo.c +39 -12
  57. data/vendor/liburing/test/fifo-futex-poll.c +114 -0
  58. data/vendor/liburing/test/fifo-nonblock-read.c +1 -12
  59. data/vendor/liburing/test/futex.c +1 -1
  60. data/vendor/liburing/test/helpers.c +99 -2
  61. data/vendor/liburing/test/helpers.h +9 -0
  62. data/vendor/liburing/test/io_uring_passthrough.c +6 -12
  63. data/vendor/liburing/test/mock_file.c +379 -0
  64. data/vendor/liburing/test/mock_file.h +47 -0
  65. data/vendor/liburing/test/nop.c +2 -2
  66. data/vendor/liburing/test/nop32-overflow.c +150 -0
  67. data/vendor/liburing/test/nop32.c +126 -0
  68. data/vendor/liburing/test/pipe.c +166 -0
  69. data/vendor/liburing/test/poll-race-mshot.c +13 -1
  70. data/vendor/liburing/test/recv-mshot-fair.c +81 -34
  71. data/vendor/liburing/test/recvsend_bundle.c +1 -1
  72. data/vendor/liburing/test/resize-rings.c +2 -0
  73. data/vendor/liburing/test/ring-query.c +322 -0
  74. data/vendor/liburing/test/ringbuf-loop.c +87 -0
  75. data/vendor/liburing/test/runtests.sh +2 -2
  76. data/vendor/liburing/test/send-zerocopy.c +43 -5
  77. data/vendor/liburing/test/send_recv.c +102 -32
  78. data/vendor/liburing/test/shutdown.c +2 -12
  79. data/vendor/liburing/test/socket-nb.c +3 -14
  80. data/vendor/liburing/test/socket-rw-eagain.c +2 -12
  81. data/vendor/liburing/test/socket-rw-offset.c +2 -12
  82. data/vendor/liburing/test/socket-rw.c +2 -12
  83. data/vendor/liburing/test/sqe-mixed-bad-wrap.c +87 -0
  84. data/vendor/liburing/test/sqe-mixed-nop.c +82 -0
  85. data/vendor/liburing/test/sqe-mixed-uring_cmd.c +153 -0
  86. data/vendor/liburing/test/timestamp.c +56 -19
  87. data/vendor/liburing/test/vec-regbuf.c +2 -4
  88. data/vendor/liburing/test/wq-aff.c +7 -0
  89. metadata +24 -2
@@ -244,3 +244,18 @@ LIBURING_2.11 {
244
244
  io_uring_memory_size_params;
245
245
  io_uring_register_sync_msg;
246
246
  } LIBURING_2.10;
247
+
248
+ LIBURING_2.12 {
249
+ global:
250
+ io_uring_prep_pipe;
251
+ io_uring_prep_pipe_direct;
252
+ io_uring_cqe_nr;
253
+ } LIBURING_2.11;
254
+
255
+ LIBURING_2.13 {
256
+ global:
257
+ io_uring_prep_nop128;
258
+ io_uring_prep_uring_cmd;
259
+ io_uring_prep_uring_cmd128;
260
+ io_uring_get_sqe128;
261
+ } LIBURING_2.12;
@@ -126,3 +126,11 @@ LIBURING_2.11 {
126
126
  io_uring_memory_size_params;
127
127
  io_uring_register_sync_msg;
128
128
  } LIBURING_2.10;
129
+
130
+ LIBURING_2.12 {
131
+
132
+ } LIBURING_2.11;
133
+
134
+ LIBURING_2.13 {
135
+
136
+ } LIBURING_2.12;
@@ -119,7 +119,10 @@ static inline void initialize_sanitize_handlers()
119
119
  sanitize_handlers[IORING_OP_EPOLL_WAIT] = sanitize_sqe_addr;
120
120
  sanitize_handlers[IORING_OP_READV_FIXED] = sanitize_sqe_addr;
121
121
  sanitize_handlers[IORING_OP_WRITEV_FIXED] = sanitize_sqe_addr;
122
- _Static_assert(IORING_OP_WRITEV_FIXED + 1 == IORING_OP_LAST, "Need an implementation for all IORING_OP_* codes");
122
+ sanitize_handlers[IORING_OP_PIPE] = sanitize_sqe_addr;
123
+ sanitize_handlers[IORING_OP_NOP128] = sanitize_sqe_nop;
124
+ sanitize_handlers[IORING_OP_URING_CMD128] = sanitize_sqe_optval;
125
+ _Static_assert(IORING_OP_URING_CMD128 + 1 == IORING_OP_LAST, "Need an implementation for all IORING_OP_* codes");
123
126
  sanitize_handlers_initialized = true;
124
127
  }
125
128
 
@@ -218,7 +218,7 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
218
218
  {
219
219
  unsigned long page_size = get_page_size();
220
220
  unsigned sq_entries, cq_entries;
221
- size_t ring_mem, sqes_mem;
221
+ size_t sqes_size = 0, ring_mem, sqes_mem;
222
222
  unsigned long mem_used = 0;
223
223
  void *ptr;
224
224
  int ret;
@@ -260,7 +260,8 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
260
260
  buf_size = huge_page_size;
261
261
  map_hugetlb = MAP_HUGETLB;
262
262
  }
263
- ptr = __sys_mmap(NULL, buf_size, PROT_READ|PROT_WRITE,
263
+ sqes_size = buf_size;
264
+ ptr = __sys_mmap(NULL, sqes_size, PROT_READ|PROT_WRITE,
264
265
  MAP_SHARED|MAP_ANONYMOUS|map_hugetlb,
265
266
  -1, 0);
266
267
  if (IS_ERR(ptr))
@@ -285,7 +286,8 @@ static int io_uring_alloc_huge(unsigned entries, struct io_uring_params *p,
285
286
  MAP_SHARED|MAP_ANONYMOUS|map_hugetlb,
286
287
  -1, 0);
287
288
  if (IS_ERR(ptr)) {
288
- __sys_munmap(sq->sqes, 1);
289
+ if (sqes_size)
290
+ __sys_munmap(sq->sqes, sqes_size);
289
291
  return PTR_ERR(ptr);
290
292
  }
291
293
  sq->ring_ptr = ptr;
@@ -330,7 +332,7 @@ int __io_uring_queue_init_params(unsigned entries, struct io_uring *ring,
330
332
  if (fd < 0) {
331
333
  if ((p->flags & IORING_SETUP_NO_MMAP) &&
332
334
  !(ring->int_flags & INT_FLAG_APP_MEM)) {
333
- __sys_munmap(ring->sq.sqes, 1);
335
+ __sys_munmap(ring->sq.sqes, ret);
334
336
  io_uring_unmap_rings(&ring->sq, &ring->cq);
335
337
  }
336
338
  return fd;
@@ -630,6 +632,7 @@ static struct io_uring_buf_ring *br_setup(struct io_uring *ring,
630
632
  MAP_SHARED | MAP_POPULATE, ring->ring_fd, off);
631
633
  if (IS_ERR(br)) {
632
634
  *err = PTR_ERR(br);
635
+ io_uring_unregister_buf_ring(ring, bgid);
633
636
  return NULL;
634
637
  }
635
638
 
@@ -100,14 +100,8 @@ static void *rcv(void *arg)
100
100
  int s1 = accept(s0, NULL, NULL);
101
101
  assert(s1 != -1);
102
102
 
103
- if (p->non_blocking) {
104
- int flags = fcntl(s1, F_GETFL, 0);
105
- assert(flags != -1);
106
-
107
- flags |= O_NONBLOCK;
108
- res = fcntl(s1, F_SETFL, flags);
109
- assert(res != -1);
110
- }
103
+ if (p->non_blocking)
104
+ t_set_nonblock(s1);
111
105
 
112
106
  struct io_uring m_io_uring;
113
107
  void *ret = NULL;
@@ -208,14 +202,8 @@ static void *snd(void *arg)
208
202
  assert(ret != -1);
209
203
  }
210
204
 
211
- if (p->non_blocking) {
212
- int flags = fcntl(s0, F_GETFL, 0);
213
- assert(flags != -1);
214
-
215
- flags |= O_NONBLOCK;
216
- ret = fcntl(s0, F_SETFL, flags);
217
- assert(ret != -1);
218
- }
205
+ if (p->non_blocking)
206
+ t_set_nonblock(s0);
219
207
 
220
208
  struct io_uring m_io_uring;
221
209
 
@@ -31,6 +31,10 @@ ifeq ($(CONFIG_USE_SANITIZER),y)
31
31
  XCFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls
32
32
  endif
33
33
 
34
+ ifeq ($(CONFIG_USE_TSAN),y)
35
+ XCFLAGS += -fsanitize=thread -fno-omit-frame-pointer -fno-optimize-sibling-calls
36
+ endif
37
+
34
38
  CXXFLAGS ?= $(CFLAGS)
35
39
  override CFLAGS += $(XCFLAGS) -DLIBURING_BUILD_TEST
36
40
  override CXXFLAGS += $(XCFLAGS) -std=c++11 -DLIBURING_BUILD_TEST
@@ -62,6 +66,7 @@ test_srcs := \
62
66
  buf-ring-put.c \
63
67
  ce593a6c480a.c \
64
68
  close-opath.c \
69
+ conn-unreach.c \
65
70
  connect.c \
66
71
  connect-rep.c \
67
72
  coredump.c \
@@ -98,6 +103,7 @@ test_srcs := \
98
103
  fdinfo.c \
99
104
  fdinfo-sqpoll.c \
100
105
  fifo-nonblock-read.c \
106
+ fifo-futex-poll.c \
101
107
  file-exit-unreg.c \
102
108
  file-register.c \
103
109
  files-exit-hang-poll.c \
@@ -147,12 +153,15 @@ test_srcs := \
147
153
  no-mmap-inval.c \
148
154
  nop-all-sizes.c \
149
155
  nop.c \
156
+ nop32.c \
157
+ nop32-overflow.c \
150
158
  ooo-file-unreg.c \
151
159
  openat2.c \
152
160
  open-close.c \
153
161
  open-direct-link.c \
154
162
  open-direct-pick.c \
155
163
  personality.c \
164
+ pipe.c \
156
165
  pipe-bug.c \
157
166
  pipe-eof.c \
158
167
  pipe-reuse.c \
@@ -191,6 +200,7 @@ test_srcs := \
191
200
  register-restrictions.c \
192
201
  rename.c \
193
202
  resize-rings.c \
203
+ ringbuf-loop.c \
194
204
  ringbuf-read.c \
195
205
  ringbuf-status.c \
196
206
  ring-leak2.c \
@@ -219,7 +229,6 @@ test_srcs := \
219
229
  socket-rw-offset.c \
220
230
  splice.c \
221
231
  sq-full.c \
222
- sq-full-cpp.cc \
223
232
  sqpoll-disable-exit.c \
224
233
  sqpoll-exec.c \
225
234
  sq-poll-dup.c \
@@ -228,6 +237,9 @@ test_srcs := \
228
237
  sq-poll-share.c \
229
238
  sqpoll-sleep.c \
230
239
  sq-space_left.c \
240
+ sqe-mixed-nop.c \
241
+ sqe-mixed-bad-wrap.c \
242
+ sqe-mixed-uring_cmd.c \
231
243
  sqwait.c \
232
244
  stdout.c \
233
245
  submit-and-wait.c \
@@ -253,6 +265,8 @@ test_srcs := \
253
265
  zcrx.c \
254
266
  vec-regbuf.c \
255
267
  timestamp.c \
268
+ ring-query.c \
269
+ mock_file.c \
256
270
  # EOL
257
271
 
258
272
  # Please keep this list sorted alphabetically.
@@ -218,23 +218,12 @@ static int set_client_fd(struct sockaddr_in *addr)
218
218
  ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
219
219
  assert(ret != -1);
220
220
 
221
- int32_t flags = fcntl(fd, F_GETFL, 0);
222
- assert(flags != -1);
223
-
224
- flags |= O_NONBLOCK;
225
- ret = fcntl(fd, F_SETFL, flags);
226
- assert(ret != -1);
221
+ t_set_nonblock(fd);
227
222
 
228
223
  ret = connect(fd, (struct sockaddr *)addr, sizeof(*addr));
229
224
  assert(ret == -1);
230
225
 
231
- flags = fcntl(fd, F_GETFL, 0);
232
- assert(flags != -1);
233
-
234
- flags &= ~O_NONBLOCK;
235
- ret = fcntl(fd, F_SETFL, flags);
236
- assert(ret != -1);
237
-
226
+ t_clear_nonblock(fd);
238
227
  return fd;
239
228
  }
240
229
 
@@ -0,0 +1,132 @@
1
+ /* SPDX-License-Identifier: MIT */
2
+ /*
3
+ * Check that IORING_OP_CONNECT properly returns -ECONNRESET when
4
+ * attempting to connect to an unreachable address. See:
5
+ *
6
+ * https://github.com/axboe/liburing/discussions/1335
7
+ */
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <stdint.h>
11
+ #include <string.h>
12
+ #include <assert.h>
13
+
14
+ #include <errno.h>
15
+ #include <fcntl.h>
16
+ #include <unistd.h>
17
+ #include <sys/socket.h>
18
+ #include <sys/un.h>
19
+ #include <netinet/tcp.h>
20
+ #include <netinet/in.h>
21
+ #include <arpa/inet.h>
22
+
23
+ #include "liburing.h"
24
+ #include "helpers.h"
25
+
26
+ static int check_cqe(struct io_uring *ring, struct io_uring_cqe *cqe)
27
+ {
28
+ if (cqe->res == -EINVAL)
29
+ return T_EXIT_SKIP;
30
+
31
+ switch (cqe->user_data) {
32
+ case 1:
33
+ if (cqe->res != -ECONNRESET) {
34
+ fprintf(stderr, "Unexpected connect: %d\n", cqe->res);
35
+ return T_EXIT_FAIL;
36
+ }
37
+ break;
38
+ case 2:
39
+ if (cqe->res) {
40
+ fprintf(stderr, "Unexpected shutdown: %d\n", cqe->res);
41
+ return T_EXIT_FAIL;
42
+ }
43
+ break;
44
+ }
45
+ io_uring_cqe_seen(ring, cqe);
46
+ return T_EXIT_PASS;
47
+ }
48
+
49
+ static int test(struct io_uring *ring, struct sockaddr_in *saddr, int p_fd)
50
+ {
51
+ struct io_uring_cqe *cqe;
52
+ struct io_uring_sqe *sqe1, *sqe2;
53
+ socklen_t val_len;
54
+ int ret, val;
55
+
56
+ sqe1 = io_uring_get_sqe(ring);
57
+ io_uring_prep_connect(sqe1, p_fd, (struct sockaddr *)saddr, sizeof(*saddr));
58
+ sqe1->user_data = 1;
59
+
60
+ ret = io_uring_submit(ring);
61
+ assert(ret != -1);
62
+
63
+ usleep(200000); // 200ms
64
+ sqe2 = io_uring_get_sqe(ring);
65
+ io_uring_prep_shutdown(sqe2, p_fd, SHUT_RDWR);
66
+ sqe2->user_data = 2;
67
+
68
+ ret = io_uring_submit(ring);
69
+ assert(ret != -1);
70
+
71
+ ret = io_uring_wait_cqe(ring, &cqe);
72
+ if (ret < 0) {
73
+ fprintf(stderr, "wait: %s\n", strerror(-ret));
74
+ return T_EXIT_FAIL;
75
+ }
76
+
77
+ ret = check_cqe(ring, cqe);
78
+ if (ret != T_EXIT_PASS)
79
+ return ret;
80
+
81
+ val = 0;
82
+ val_len = sizeof(val);
83
+ ret = getsockopt(p_fd, SOL_SOCKET, SO_ERROR, &val, &val_len);
84
+ assert(ret != -1);
85
+
86
+ ret = io_uring_wait_cqe(ring, &cqe);
87
+ if (ret < 0) {
88
+ fprintf(stderr, "wait: %s\n", strerror(-ret));
89
+ return T_EXIT_FAIL;
90
+ }
91
+
92
+ return check_cqe(ring, cqe);
93
+ }
94
+
95
+ int main(int argc, char *argv[])
96
+ {
97
+ struct sockaddr_in addr = { };
98
+ struct io_uring ring;
99
+ int val, p_fd, ret;
100
+
101
+ if (argc > 1)
102
+ return T_EXIT_SKIP;
103
+
104
+ p_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
105
+
106
+ val = 1;
107
+ ret = setsockopt(p_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
108
+ assert(ret != -1);
109
+
110
+ // NB. these are to make things faster
111
+ val = 2;
112
+ ret = setsockopt(p_fd, IPPROTO_TCP, TCP_SYNCNT, &val, sizeof(val));
113
+ assert(ret != -1);
114
+
115
+ val = 500; // 500ms
116
+ ret = setsockopt(p_fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &val, sizeof(val));
117
+ assert(ret != -1);
118
+
119
+ t_set_nonblock(p_fd);
120
+
121
+ addr.sin_family = AF_INET;
122
+ /* any unreachable address */
123
+ addr.sin_addr.s_addr = inet_addr("172.31.5.5");
124
+ addr.sin_port = htons(12345);
125
+
126
+ ret = io_uring_queue_init(2, &ring, 0);
127
+ assert(ret >= 0);
128
+
129
+ ret = test(&ring, &addr, p_fd);
130
+ io_uring_queue_exit(&ring);
131
+ return ret;
132
+ }
@@ -50,14 +50,15 @@ static int verify_fixed_read(struct io_uring *ring, int fixed_fd, int fail)
50
50
  return 0;
51
51
  }
52
52
 
53
- static int test(const char *filename, int source_fd, int target_fd)
53
+ static int test(const char *filename, int source_fd, int target_fd,
54
+ unsigned int ring_flags)
54
55
  {
55
56
  struct io_uring sring, dring;
56
57
  struct io_uring_sqe *sqe;
57
58
  struct io_uring_cqe *cqe;
58
59
  int ret;
59
60
 
60
- ret = io_uring_queue_init(8, &sring, 0);
61
+ ret = io_uring_queue_init(8, &sring, ring_flags);
61
62
  if (ret) {
62
63
  fprintf(stderr, "ring setup failed: %d\n", ret);
63
64
  return T_EXIT_FAIL;
@@ -202,36 +203,60 @@ int main(int argc, char *argv[])
202
203
  sprintf(fname, ".fd-pass.%d", getpid());
203
204
  t_create_file_pattern(fname, FSIZE, PAT);
204
205
 
205
- ret = test(fname, 0, 1);
206
+ ret = test(fname, 0, 1, 0);
206
207
  if (ret == T_EXIT_FAIL) {
207
208
  fprintf(stderr, "test failed 0 1\n");
208
209
  ret = T_EXIT_FAIL;
209
210
  }
210
211
 
211
- ret = test(fname, 0, 2);
212
+ ret = test(fname, 0, 2, 0);
212
213
  if (ret == T_EXIT_FAIL) {
213
214
  fprintf(stderr, "test failed 0 2\n");
214
215
  ret = T_EXIT_FAIL;
215
216
  }
216
217
 
217
- ret = test(fname, 1, 1);
218
+ ret = test(fname, 1, 1, 0);
218
219
  if (ret == T_EXIT_FAIL) {
219
220
  fprintf(stderr, "test failed 1 1\n");
220
221
  ret = T_EXIT_FAIL;
221
222
  }
222
223
 
223
- ret = test(fname, 1, 0);
224
+ ret = test(fname, 1, 0, 0);
224
225
  if (ret == T_EXIT_FAIL) {
225
226
  fprintf(stderr, "test failed 1 0\n");
226
227
  ret = T_EXIT_FAIL;
227
228
  }
228
229
 
229
- ret = test(fname, 1, IORING_FILE_INDEX_ALLOC);
230
+ ret = test(fname, 1, IORING_FILE_INDEX_ALLOC, 0);
230
231
  if (ret == T_EXIT_FAIL) {
231
232
  fprintf(stderr, "test failed 1 ALLOC\n");
232
233
  ret = T_EXIT_FAIL;
233
234
  }
234
235
 
236
+ ret = test(fname, 0, 2, IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
237
+ if (ret == T_EXIT_FAIL) {
238
+ fprintf(stderr, "test failed 0 2 defer\n");
239
+ ret = T_EXIT_FAIL;
240
+ }
241
+
242
+ ret = test(fname, 1, 1, IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
243
+ if (ret == T_EXIT_FAIL) {
244
+ fprintf(stderr, "test failed 1 1 defer\n");
245
+ ret = T_EXIT_FAIL;
246
+ }
247
+
248
+ ret = test(fname, 1, 0, IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
249
+ if (ret == T_EXIT_FAIL) {
250
+ fprintf(stderr, "test failed 1 0 defer\n");
251
+ ret = T_EXIT_FAIL;
252
+ }
253
+
254
+ ret = test(fname, 1, IORING_FILE_INDEX_ALLOC, IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
255
+ if (ret == T_EXIT_FAIL) {
256
+ fprintf(stderr, "test failed 1 ALLOC defer\n");
257
+ ret = T_EXIT_FAIL;
258
+ }
259
+
235
260
  unlink(fname);
236
261
  return ret;
237
262
  }
@@ -57,7 +57,7 @@ static void fdinfo_read(struct io_uring *ring)
57
57
 
58
58
  static int __test_io(const char *file, struct io_uring *ring, int write,
59
59
  int buffered, int sqthread, int fixed, int nonvec,
60
- int buf_select, int seq, int exp_len)
60
+ int buf_select, int seq, int exp_len, int mixed)
61
61
  {
62
62
  struct io_uring_sqe *sqe;
63
63
  struct io_uring_cqe *cqe;
@@ -66,9 +66,9 @@ static int __test_io(const char *file, struct io_uring *ring, int write,
66
66
  off_t offset;
67
67
 
68
68
  #ifdef VERBOSE
69
- fprintf(stdout, "%s: start %d/%d/%d/%d/%d: ", __FUNCTION__, write,
69
+ fprintf(stdout, "%s: start %d/%d/%d/%d/%d/%d: ", __FUNCTION__, write,
70
70
  buffered, sqthread,
71
- fixed, nonvec);
71
+ fixed, nonvec, mixed);
72
72
  #endif
73
73
  if (write)
74
74
  open_flags = O_WRONLY;
@@ -107,6 +107,17 @@ static int __test_io(const char *file, struct io_uring *ring, int write,
107
107
 
108
108
  offset = 0;
109
109
  for (i = 0; i < BUFFERS; i++) {
110
+ if (mixed && i & 1) {
111
+ sqe = io_uring_get_sqe128(ring);
112
+ if (!sqe) {
113
+ fprintf(stderr, "sqe get failed\n");
114
+ goto err;
115
+ }
116
+ io_uring_prep_nop128(sqe);
117
+ sqe->user_data = i;
118
+ continue;
119
+ }
120
+
110
121
  sqe = io_uring_get_sqe(ring);
111
122
  if (!sqe) {
112
123
  fprintf(stderr, "sqe get failed\n");
@@ -167,12 +178,17 @@ static int __test_io(const char *file, struct io_uring *ring, int write,
167
178
  offset += BS;
168
179
  }
169
180
 
181
+ __io_uring_flush_sq(ring);
170
182
  fdinfo_read(ring);
171
183
 
172
184
  ret = io_uring_submit(ring);
173
- if (ret != BUFFERS) {
185
+ if (!mixed && ret != BUFFERS) {
174
186
  fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
175
187
  goto err;
188
+ } else if (mixed && ret != BUFFERS + (BUFFERS >> 1)) {
189
+ fprintf(stderr, "submit got %d, wanted %d\n", ret,
190
+ BUFFERS + (BUFFERS >> 1));
191
+ goto err;
176
192
  }
177
193
 
178
194
  for (i = 0; i < 10; i++) {
@@ -186,7 +202,13 @@ static int __test_io(const char *file, struct io_uring *ring, int write,
186
202
  fprintf(stderr, "wait_cqe=%d\n", ret);
187
203
  goto err;
188
204
  }
189
- if (cqe->res == -EINVAL && nonvec) {
205
+ if (mixed && cqe->user_data & 1) {
206
+ if (cqe->user_data > 32) {
207
+ fprintf(stderr, "cqe user data %lld, max expected %d\n",
208
+ cqe->user_data, BUFFERS - 1);
209
+ goto err;
210
+ }
211
+ } else if (cqe->res == -EINVAL && nonvec) {
190
212
  if (!warned) {
191
213
  fprintf(stdout, "Non-vectored IO not "
192
214
  "supported, skipping\n");
@@ -252,15 +274,19 @@ err:
252
274
  return 1;
253
275
  }
254
276
  static int test_io(const char *file, int write, int buffered, int sqthread,
255
- int fixed, int nonvec, int exp_len)
277
+ int fixed, int nonvec, int exp_len, int mixed)
256
278
  {
257
279
  struct io_uring ring;
258
- int ret, ring_flags = 0;
280
+ int ret, ring_flags = 0, entries = 64;
259
281
 
260
282
  if (sqthread)
261
283
  ring_flags = IORING_SETUP_SQPOLL;
284
+ if (mixed) {
285
+ ring_flags |= IORING_SETUP_SQE_MIXED;
286
+ entries = 128;
287
+ }
262
288
 
263
- ret = t_create_ring(64, &ring, ring_flags);
289
+ ret = t_create_ring(entries, &ring, ring_flags);
264
290
  if (ret == T_SETUP_SKIP)
265
291
  return 0;
266
292
  if (ret != T_SETUP_OK) {
@@ -269,7 +295,7 @@ static int test_io(const char *file, int write, int buffered, int sqthread,
269
295
  }
270
296
 
271
297
  ret = __test_io(file, &ring, write, buffered, sqthread, fixed, nonvec,
272
- 0, 0, exp_len);
298
+ 0, 0, exp_len, mixed);
273
299
  io_uring_queue_exit(&ring);
274
300
  return ret;
275
301
  }
@@ -379,17 +405,18 @@ int main(int argc, char *argv[])
379
405
  vecs = t_create_buffers(BUFFERS, BS);
380
406
 
381
407
  /* if we don't have nonvec read, skip testing that */
382
- nr = has_nonvec_read() ? 32 : 16;
408
+ nr = has_nonvec_read() ? 64 : 32;
383
409
 
384
410
  for (i = 0; i < nr; i++) {
385
411
  int write = (i & 1) != 0;
386
412
  int buffered = (i & 2) != 0;
387
413
  int sqthread = (i & 4) != 0;
388
414
  int fixed = (i & 8) != 0;
389
- int nonvec = (i & 16) != 0;
415
+ int mixed = (i & 16) != 0;
416
+ int nonvec = (i & 32) != 0;
390
417
 
391
418
  ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
392
- BS);
419
+ BS, mixed);
393
420
  if (ret == T_EXIT_SKIP)
394
421
  continue;
395
422
  if (ret) {
@@ -0,0 +1,114 @@
1
+ /* SPDX-License-Identifier: MIT */
2
+ /*
3
+ * Description: Run a failing futex wait command followed up a POLL_ADD
4
+ * on a fifo.
5
+ *
6
+ */
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <linux/futex.h>
10
+ #include <linux/poll.h>
11
+ #include <unistd.h>
12
+
13
+ #include "liburing.h"
14
+ #include "helpers.h"
15
+
16
+ #ifndef FUTEX2_SIZE_U32
17
+ #define FUTEX2_SIZE_U32 0x02
18
+ #endif
19
+
20
+ int main(int argc, char *argv[])
21
+ {
22
+ struct io_uring_sqe *sqe;
23
+ struct io_uring_cqe *cqe;
24
+ struct io_uring ring;
25
+ int ret, pipe;
26
+
27
+ if (argc > 1)
28
+ return T_EXIT_SKIP;
29
+
30
+ /*
31
+ * Setup a FIFO for polling. FIFOs are interesting because they end
32
+ * up using two wait queue entries for poll, hitting double poll
33
+ * in io_uring.
34
+ */
35
+ ret = mkfifo("fifo", 0644);
36
+ if (ret < 0) {
37
+ perror("mkfifo");
38
+ return 1;
39
+ }
40
+ pipe = open("fifo", O_RDWR);
41
+ if (pipe < 0) {
42
+ perror("open fifo");
43
+ ret = T_EXIT_FAIL;
44
+ goto err;
45
+ }
46
+
47
+ ret = io_uring_queue_init(64, &ring, 0);
48
+ if (ret) {
49
+ ret = T_EXIT_FAIL;
50
+ goto err;
51
+ }
52
+
53
+ /*
54
+ * Submit invalid futex wait, will error inline.
55
+ */
56
+ sqe = io_uring_get_sqe(&ring);
57
+ io_uring_prep_futex_wait(sqe, NULL, 0, FUTEX_BITSET_MATCH_ANY, FUTEX2_SIZE_U32, 0);
58
+ sqe->user_data = 1;
59
+ io_uring_submit(&ring);
60
+
61
+ ret = io_uring_wait_cqe(&ring, &cqe);
62
+ if (ret) {
63
+ fprintf(stderr, "wait %d \n", ret);
64
+ ret = T_EXIT_FAIL;
65
+ goto err_ring;
66
+ }
67
+ if (cqe->res != -EFAULT) {
68
+ if (cqe->res == -EINVAL) {
69
+ ret = T_EXIT_SKIP;
70
+ goto err_ring;
71
+ }
72
+ fprintf(stderr, "Unexpected futex return: %d\n", cqe->res);
73
+ ret = T_EXIT_FAIL;
74
+ goto err_ring;
75
+ }
76
+ io_uring_cqe_seen(&ring, cqe);
77
+
78
+ ret = io_uring_register_files(&ring, &pipe, 1);
79
+ if (ret < 0) {
80
+ fprintf(stderr, "io_uring register files failed \n");
81
+ ret = T_EXIT_FAIL;
82
+ goto err_ring;
83
+ }
84
+
85
+ /*
86
+ * Submit poll request for POLLIN on the fifo
87
+ */
88
+ sqe = io_uring_get_sqe(&ring);
89
+ io_uring_prep_poll_add(sqe, 0, POLLIN);
90
+ sqe->flags |= IOSQE_FIXED_FILE;
91
+ sqe->user_data = 2;
92
+ io_uring_submit(&ring);
93
+
94
+ /*
95
+ * Trigger the poll request
96
+ */
97
+ ret = write(pipe, "FIFO test\n", 10);
98
+ if (ret < 0)
99
+ perror("fifo write");
100
+
101
+ ret = io_uring_wait_cqe(&ring, &cqe);
102
+ if (ret) {
103
+ fprintf(stderr, "wait %d \n", ret);
104
+ ret = T_EXIT_FAIL;
105
+ }
106
+ io_uring_cqe_seen(&ring, cqe);
107
+ err_ring:
108
+ io_uring_queue_exit(&ring);
109
+ err:
110
+ if (pipe != -1)
111
+ close(pipe);
112
+ unlink("fifo");
113
+ return ret;
114
+ }