uringmachine 0.19.1 → 0.21.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -4
  3. data/CHANGELOG.md +32 -1
  4. data/TODO.md +0 -39
  5. data/examples/bm_fileno.rb +33 -0
  6. data/examples/bm_mutex.rb +85 -0
  7. data/examples/bm_mutex_single.rb +33 -0
  8. data/examples/bm_queue.rb +29 -29
  9. data/examples/bm_send.rb +2 -5
  10. data/examples/bm_snooze.rb +20 -42
  11. data/examples/bm_write.rb +4 -1
  12. data/examples/fiber_scheduler_demo.rb +15 -51
  13. data/examples/fiber_scheduler_fork.rb +24 -0
  14. data/examples/nc_ssl.rb +71 -0
  15. data/ext/um/extconf.rb +5 -15
  16. data/ext/um/um.c +310 -74
  17. data/ext/um/um.h +66 -29
  18. data/ext/um/um_async_op.c +1 -1
  19. data/ext/um/um_async_op_class.c +2 -2
  20. data/ext/um/um_buffer.c +1 -1
  21. data/ext/um/um_class.c +178 -31
  22. data/ext/um/um_const.c +51 -3
  23. data/ext/um/um_mutex_class.c +1 -1
  24. data/ext/um/um_op.c +37 -0
  25. data/ext/um/um_queue_class.c +1 -1
  26. data/ext/um/um_stream.c +5 -5
  27. data/ext/um/um_stream_class.c +3 -0
  28. data/ext/um/um_sync.c +28 -39
  29. data/ext/um/um_utils.c +59 -19
  30. data/grant-2025/journal.md +353 -0
  31. data/grant-2025/tasks.md +135 -0
  32. data/lib/uringmachine/fiber_scheduler.rb +316 -57
  33. data/lib/uringmachine/version.rb +1 -1
  34. data/lib/uringmachine.rb +6 -0
  35. data/test/test_fiber_scheduler.rb +640 -0
  36. data/test/test_stream.rb +2 -2
  37. data/test/test_um.rb +722 -54
  38. data/uringmachine.gemspec +5 -5
  39. data/vendor/liburing/.github/workflows/ci.yml +94 -1
  40. data/vendor/liburing/.github/workflows/test_build.c +9 -0
  41. data/vendor/liburing/configure +27 -0
  42. data/vendor/liburing/examples/Makefile +6 -0
  43. data/vendor/liburing/examples/helpers.c +8 -0
  44. data/vendor/liburing/examples/helpers.h +5 -0
  45. data/vendor/liburing/liburing.spec +1 -1
  46. data/vendor/liburing/src/Makefile +9 -3
  47. data/vendor/liburing/src/include/liburing/barrier.h +11 -5
  48. data/vendor/liburing/src/include/liburing/io_uring/query.h +41 -0
  49. data/vendor/liburing/src/include/liburing/io_uring.h +51 -0
  50. data/vendor/liburing/src/include/liburing/sanitize.h +16 -4
  51. data/vendor/liburing/src/include/liburing.h +458 -121
  52. data/vendor/liburing/src/liburing-ffi.map +16 -0
  53. data/vendor/liburing/src/liburing.map +8 -0
  54. data/vendor/liburing/src/sanitize.c +4 -1
  55. data/vendor/liburing/src/setup.c +7 -4
  56. data/vendor/liburing/test/232c93d07b74.c +4 -16
  57. data/vendor/liburing/test/Makefile +15 -1
  58. data/vendor/liburing/test/accept.c +2 -13
  59. data/vendor/liburing/test/bind-listen.c +175 -13
  60. data/vendor/liburing/test/conn-unreach.c +132 -0
  61. data/vendor/liburing/test/fd-pass.c +32 -7
  62. data/vendor/liburing/test/fdinfo.c +39 -12
  63. data/vendor/liburing/test/fifo-futex-poll.c +114 -0
  64. data/vendor/liburing/test/fifo-nonblock-read.c +1 -12
  65. data/vendor/liburing/test/futex.c +1 -1
  66. data/vendor/liburing/test/helpers.c +99 -2
  67. data/vendor/liburing/test/helpers.h +9 -0
  68. data/vendor/liburing/test/io_uring_passthrough.c +6 -12
  69. data/vendor/liburing/test/mock_file.c +379 -0
  70. data/vendor/liburing/test/mock_file.h +47 -0
  71. data/vendor/liburing/test/nop.c +2 -2
  72. data/vendor/liburing/test/nop32-overflow.c +150 -0
  73. data/vendor/liburing/test/nop32.c +126 -0
  74. data/vendor/liburing/test/pipe.c +166 -0
  75. data/vendor/liburing/test/poll-race-mshot.c +13 -1
  76. data/vendor/liburing/test/read-write.c +4 -4
  77. data/vendor/liburing/test/recv-mshot-fair.c +81 -34
  78. data/vendor/liburing/test/recvsend_bundle.c +1 -1
  79. data/vendor/liburing/test/resize-rings.c +2 -0
  80. data/vendor/liburing/test/ring-query.c +322 -0
  81. data/vendor/liburing/test/ringbuf-loop.c +87 -0
  82. data/vendor/liburing/test/ringbuf-read.c +4 -4
  83. data/vendor/liburing/test/runtests.sh +2 -2
  84. data/vendor/liburing/test/send-zerocopy.c +43 -5
  85. data/vendor/liburing/test/send_recv.c +103 -32
  86. data/vendor/liburing/test/shutdown.c +2 -12
  87. data/vendor/liburing/test/socket-nb.c +3 -14
  88. data/vendor/liburing/test/socket-rw-eagain.c +2 -12
  89. data/vendor/liburing/test/socket-rw-offset.c +2 -12
  90. data/vendor/liburing/test/socket-rw.c +2 -12
  91. data/vendor/liburing/test/sqe-mixed-bad-wrap.c +87 -0
  92. data/vendor/liburing/test/sqe-mixed-nop.c +82 -0
  93. data/vendor/liburing/test/sqe-mixed-uring_cmd.c +153 -0
  94. data/vendor/liburing/test/timestamp.c +56 -19
  95. data/vendor/liburing/test/vec-regbuf.c +2 -4
  96. data/vendor/liburing/test/wq-aff.c +7 -0
  97. metadata +37 -15
@@ -244,3 +244,19 @@ 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
+ io_uring_prep_cmd_getsockname;
262
+ } 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
 
@@ -22,7 +22,7 @@ static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
22
22
  }
23
23
 
24
24
  static const char *magic = "Hello World!";
25
- static int use_port = 8000;
25
+ static bool no_getsockname = false;
26
26
 
27
27
  enum {
28
28
  SRV_INDEX = 0,
@@ -74,18 +74,82 @@ static int connect_client(struct io_uring *ring, unsigned short peer_port)
74
74
  return T_SETUP_OK;
75
75
  }
76
76
 
77
- static int setup_srv(struct io_uring *ring, struct sockaddr_in *server_addr)
77
+ /*
78
+ * getsockname was added to the kernel a few releases after bind/listen.
79
+ * In order to provide a backward-compatible test, fallback to
80
+ * non-io-uring if we are on an older kernel, allowing the test to
81
+ * continue.
82
+ */
83
+ static int do_getsockname(struct io_uring *ring, int direct_socket,
84
+ int peer, struct sockaddr *saddr,
85
+ socklen_t *saddr_len)
86
+ {
87
+ struct io_uring_sqe *sqe;
88
+ struct io_uring_cqe *cqe;
89
+ int res = 0, fd;
90
+
91
+ if (!no_getsockname) {
92
+ /* attempt io_uring. Command might not exist */
93
+ sqe = io_uring_get_sqe(ring);
94
+ io_uring_prep_cmd_getsockname(sqe, direct_socket,
95
+ saddr, saddr_len, peer);
96
+ sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
97
+ io_uring_submit(ring);
98
+ io_uring_wait_cqe(ring, &cqe);
99
+ res = cqe->res;
100
+ io_uring_cqe_seen(ring, cqe);
101
+ }
102
+
103
+ if (no_getsockname || res == -ENOTSUP) {
104
+ /*
105
+ * Older kernel. install the fd and use the getsockname
106
+ * syscall.
107
+ */
108
+ no_getsockname = true;
109
+
110
+ sqe = io_uring_get_sqe(ring);
111
+ io_uring_prep_fixed_fd_install(sqe, direct_socket, 0);
112
+ io_uring_submit(ring);
113
+ io_uring_wait_cqe(ring, &cqe);
114
+ fd = cqe->res;
115
+ io_uring_cqe_seen(ring, cqe);
116
+
117
+ if (fd < 0) {
118
+ fprintf(stderr, "installing direct fd failed. %d\n",
119
+ cqe->res);
120
+ return T_EXIT_FAIL;
121
+ }
122
+ if (peer)
123
+ res = getpeername(fd, saddr, saddr_len);
124
+ else
125
+ res = getsockname(fd, saddr, saddr_len);
126
+
127
+ if (res) {
128
+ fprintf(stderr, "get%sname syscall failed. %d\n",
129
+ peer? "peer":"sock", errno);
130
+ return T_EXIT_FAIL;
131
+ }
132
+ close(fd);
133
+ } else if (res < 0) {
134
+ fprintf(stderr, "getsockname server failed. %d\n", cqe->res);
135
+ return T_EXIT_FAIL;
136
+ }
137
+ return 0;
138
+ }
139
+
140
+ static int setup_srv(struct io_uring *ring)
78
141
  {
142
+ struct sockaddr_in server_addr;
79
143
  struct io_uring_sqe *sqe;
80
144
  struct io_uring_cqe *cqe;
81
145
  struct __kernel_timespec ts;
82
146
  int ret, val, submitted;
83
147
  unsigned head;
84
148
 
85
- memset(server_addr, 0, sizeof(struct sockaddr_in));
86
- server_addr->sin_family = AF_INET;
87
- server_addr->sin_port = htons(use_port++);
88
- server_addr->sin_addr.s_addr = htons(INADDR_ANY);
149
+ memset(&server_addr, 0, sizeof(struct sockaddr_in));
150
+ server_addr.sin_family = AF_INET;
151
+ server_addr.sin_port = htons(0);
152
+ server_addr.sin_addr.s_addr = htons(INADDR_ANY);
89
153
 
90
154
  sqe = io_uring_get_sqe(ring);
91
155
  io_uring_prep_socket_direct(sqe, AF_INET, SOCK_STREAM, 0, SRV_INDEX, 0);
@@ -98,7 +162,7 @@ static int setup_srv(struct io_uring *ring, struct sockaddr_in *server_addr)
98
162
  sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
99
163
 
100
164
  sqe = io_uring_get_sqe(ring);
101
- io_uring_prep_bind(sqe, SRV_INDEX, (struct sockaddr *) server_addr,
165
+ io_uring_prep_bind(sqe, SRV_INDEX, (struct sockaddr *) &server_addr,
102
166
  sizeof(struct sockaddr_in));
103
167
  sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
104
168
 
@@ -132,12 +196,13 @@ static int setup_srv(struct io_uring *ring, struct sockaddr_in *server_addr)
132
196
 
133
197
  static int test_good_server(unsigned int ring_flags)
134
198
  {
135
- struct sockaddr_in server_addr;
199
+ struct sockaddr_in saddr = {};
200
+ socklen_t saddr_len = sizeof(saddr);
136
201
  struct __kernel_timespec ts;
137
202
  struct io_uring_sqe *sqe;
138
203
  struct io_uring_cqe *cqe;
139
204
  struct io_uring ring;
140
- int ret;
205
+ int ret, port;
141
206
  int fds[3];
142
207
  char buf[1024];
143
208
 
@@ -155,18 +220,22 @@ static int test_good_server(unsigned int ring_flags)
155
220
  return T_SETUP_SKIP;
156
221
  }
157
222
 
158
- ret = setup_srv(&ring, &server_addr);
223
+ ret = setup_srv(&ring);
159
224
  if (ret != T_SETUP_OK) {
160
225
  fprintf(stderr, "srv startup failed.\n");
161
226
  return T_EXIT_FAIL;
162
227
  }
163
228
 
164
- if (connect_client(&ring, server_addr.sin_port) != T_SETUP_OK) {
229
+ if (do_getsockname(&ring, SRV_INDEX, 0, (struct sockaddr*) &saddr,
230
+ &saddr_len))
231
+ return T_EXIT_FAIL;
232
+
233
+ if (connect_client(&ring, saddr.sin_port) != T_SETUP_OK) {
165
234
  fprintf(stderr, "cli startup failed.\n");
166
235
  return T_SETUP_SKIP;
167
236
  }
168
237
 
169
- /* Wait for a request */
238
+ /* Wait for a connection */
170
239
  sqe = io_uring_get_sqe(&ring);
171
240
  io_uring_prep_accept_direct(sqe, SRV_INDEX, NULL, NULL, 0, CONN_INDEX);
172
241
  sqe->flags |= IOSQE_FIXED_FILE;
@@ -179,6 +248,22 @@ static int test_good_server(unsigned int ring_flags)
179
248
  }
180
249
  io_uring_cqe_seen(&ring, cqe);
181
250
 
251
+ /* Test that getsockname on the peer (getpeername) yields a
252
+ * sane result.
253
+ */
254
+ port = saddr.sin_port;
255
+ saddr.sin_port = 0;
256
+ if (do_getsockname(&ring, CLI_INDEX, 1,
257
+ (struct sockaddr*)&saddr, &saddr_len))
258
+ return T_EXIT_FAIL;
259
+
260
+ if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) ||
261
+ saddr.sin_port != port) {
262
+ fprintf(stderr, "getsockname peer got wrong address: %s:%d\n",
263
+ inet_ntoa(saddr.sin_addr), saddr.sin_port);
264
+ return T_EXIT_FAIL;
265
+ }
266
+
182
267
  sqe = io_uring_get_sqe(&ring);
183
268
  io_uring_prep_recv(sqe, CONN_INDEX, buf, sizeof(buf), 0);
184
269
  sqe->flags |= IOSQE_FIXED_FILE;
@@ -355,6 +440,77 @@ fail:
355
440
  return ret;
356
441
  }
357
442
 
443
+ static int test_bad_sockname(void)
444
+ {
445
+ struct sockaddr_in saddr;
446
+ socklen_t saddr_len;
447
+ struct io_uring_sqe *sqe;
448
+ struct io_uring_cqe *cqe;
449
+ struct io_uring ring;
450
+ int sock = -1, err;
451
+ int ret = T_EXIT_FAIL;
452
+
453
+ memset(&saddr, 0, sizeof(struct sockaddr_in));
454
+ saddr.sin_family = AF_INET;
455
+ saddr.sin_port = htons(8001);
456
+ saddr.sin_addr.s_addr = htons(INADDR_ANY);
457
+
458
+ err = t_create_ring(1, &ring, 0);
459
+ if (err < 0) {
460
+ fprintf(stderr, "queue_init: %d\n", err);
461
+ return T_SETUP_SKIP;
462
+ }
463
+
464
+ sock = socket(AF_INET, SOCK_STREAM, 0);
465
+ if (sock < 0) {
466
+ perror("socket");
467
+ goto fail;
468
+ }
469
+
470
+ err = t_bind_ephemeral_port(sock, &saddr);
471
+ if (err) {
472
+ fprintf(stderr, "bind: %s\n", strerror(-err));
473
+ goto fail;
474
+ }
475
+
476
+ /* getsockname on a !socket fd. with getsockname(2), this would
477
+ * return -ENOTSOCK, but we can't do it in an io_uring_cmd.
478
+ */
479
+ sqe = io_uring_get_sqe(&ring);
480
+ saddr_len = sizeof(saddr);
481
+ io_uring_prep_cmd_getsockname(sqe, 1, (struct sockaddr*)&saddr, &saddr_len, 0);
482
+ err = io_uring_submit(&ring);
483
+ if (err < 0)
484
+ goto fail;
485
+ err = io_uring_wait_cqe(&ring, &cqe);
486
+ if (err)
487
+ goto fail;
488
+ if (cqe->res != -ENOTSUP)
489
+ goto fail;
490
+ io_uring_cqe_seen(&ring, cqe);
491
+
492
+ /* getsockname with weird parameters */
493
+ sqe = io_uring_get_sqe(&ring);
494
+ io_uring_prep_cmd_getsockname(sqe, sock, (struct sockaddr*)&saddr,
495
+ &saddr_len, 3);
496
+ err = io_uring_submit(&ring);
497
+ if (err < 0)
498
+ goto fail;
499
+ err = io_uring_wait_cqe(&ring, &cqe);
500
+ if (err)
501
+ goto fail;
502
+ if (cqe->res != -EINVAL)
503
+ goto fail;
504
+ io_uring_cqe_seen(&ring, cqe);
505
+
506
+ ret = T_EXIT_PASS;
507
+ fail:
508
+ io_uring_queue_exit(&ring);
509
+ if (sock != -1)
510
+ close(sock);
511
+ return ret;
512
+ }
513
+
358
514
  int main(int argc, char *argv[])
359
515
  {
360
516
  struct io_uring_probe *probe;
@@ -403,6 +559,12 @@ int main(int argc, char *argv[])
403
559
  fprintf(stderr, "bad listen failed\n");
404
560
  return T_EXIT_FAIL;
405
561
  }
406
-
562
+ if (!no_getsockname) {
563
+ ret = test_bad_sockname();
564
+ if (ret) {
565
+ fprintf(stderr, "bad sockname failed\n");
566
+ return T_EXIT_FAIL;
567
+ }
568
+ }
407
569
  return T_EXIT_PASS;
408
570
  }
@@ -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
  }