uringmachine 0.8.2 → 0.11

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/TODO.md +0 -1
  4. data/examples/bm_side_running.rb +83 -0
  5. data/examples/bm_sqlite.rb +1 -1
  6. data/ext/um/um.c +66 -4
  7. data/ext/um/um.h +36 -0
  8. data/ext/um/um_class.c +6 -0
  9. data/ext/um/um_const.c +36 -0
  10. data/ext/um/um_ext.c +2 -0
  11. data/ext/um/um_stream.c +344 -0
  12. data/ext/um/um_stream_class.c +140 -0
  13. data/ext/um/um_utils.c +4 -0
  14. data/lib/uringmachine/actor.rb +1 -1
  15. data/lib/uringmachine/version.rb +1 -1
  16. data/lib/uringmachine.rb +35 -17
  17. data/test/test_fiber.rb +23 -3
  18. data/test/test_stream.rb +133 -0
  19. data/test/test_um.rb +109 -2
  20. data/uringmachine.gemspec +0 -2
  21. data/vendor/liburing/.github/workflows/{build.yml → ci.yml} +107 -42
  22. data/vendor/liburing/.gitignore +1 -0
  23. data/vendor/liburing/CHANGELOG +10 -0
  24. data/vendor/liburing/README +5 -0
  25. data/vendor/liburing/configure +1 -1
  26. data/vendor/liburing/examples/Makefile +1 -0
  27. data/vendor/liburing/examples/helpers.c +25 -0
  28. data/vendor/liburing/examples/helpers.h +13 -0
  29. data/vendor/liburing/examples/io_uring-test.c +3 -0
  30. data/vendor/liburing/examples/proxy.c +1 -1
  31. data/vendor/liburing/examples/reg-wait.c +41 -6
  32. data/vendor/liburing/examples/send-zerocopy.c +79 -32
  33. data/vendor/liburing/examples/zcrx.c +436 -0
  34. data/vendor/liburing/liburing.spec +1 -1
  35. data/vendor/liburing/src/Makefile +0 -1
  36. data/vendor/liburing/src/arch/generic/syscall.h +2 -2
  37. data/vendor/liburing/src/arch/syscall-defs.h +2 -2
  38. data/vendor/liburing/src/include/liburing/io_uring.h +101 -17
  39. data/vendor/liburing/src/include/liburing.h +179 -59
  40. data/vendor/liburing/src/int_flags.h +4 -1
  41. data/vendor/liburing/src/liburing-ffi.map +14 -2
  42. data/vendor/liburing/src/liburing.map +9 -2
  43. data/vendor/liburing/src/queue.c +35 -30
  44. data/vendor/liburing/src/register.c +46 -15
  45. data/vendor/liburing/src/sanitize.c +6 -9
  46. data/vendor/liburing/src/setup.c +37 -71
  47. data/vendor/liburing/src/syscall.c +2 -2
  48. data/vendor/liburing/test/232c93d07b74.c +1 -0
  49. data/vendor/liburing/test/Makefile +9 -0
  50. data/vendor/liburing/test/accept-test.c +1 -0
  51. data/vendor/liburing/test/cmd-discard.c +16 -8
  52. data/vendor/liburing/test/connect.c +11 -7
  53. data/vendor/liburing/test/epwait.c +420 -0
  54. data/vendor/liburing/test/eventfd-ring.c +30 -5
  55. data/vendor/liburing/test/fallocate.c +1 -1
  56. data/vendor/liburing/test/fixed-hugepage.c +10 -7
  57. data/vendor/liburing/test/fixed-seg.c +187 -0
  58. data/vendor/liburing/test/helpers.c +121 -0
  59. data/vendor/liburing/test/helpers.h +13 -0
  60. data/vendor/liburing/test/init-mem.c +2 -0
  61. data/vendor/liburing/test/io_uring_passthrough.c +78 -62
  62. data/vendor/liburing/test/iopoll-overflow.c +5 -4
  63. data/vendor/liburing/test/iopoll.c +20 -10
  64. data/vendor/liburing/test/iowait.c +141 -0
  65. data/vendor/liburing/test/nvme.h +2 -0
  66. data/vendor/liburing/test/pipe-bug.c +11 -5
  67. data/vendor/liburing/test/pipe-eof.c +11 -1
  68. data/vendor/liburing/test/read-inc-file.c +150 -0
  69. data/vendor/liburing/test/read-write.c +21 -14
  70. data/vendor/liburing/test/recv-bundle-short-ooo.c +435 -0
  71. data/vendor/liburing/test/recv-multishot.c +2 -2
  72. data/vendor/liburing/test/reg-wait.c +449 -120
  73. data/vendor/liburing/test/regbuf-clone.c +53 -0
  74. data/vendor/liburing/test/resize-rings.c +25 -2
  75. data/vendor/liburing/test/rsrc_tags.c +67 -14
  76. data/vendor/liburing/test/send-zerocopy.c +52 -130
  77. data/vendor/liburing/test/sendmsg_iov_clean.c +216 -0
  78. data/vendor/liburing/test/socket-nb.c +158 -0
  79. data/vendor/liburing/test/sqwait.c +9 -11
  80. data/vendor/liburing/test/timeout.c +198 -0
  81. data/vendor/liburing/test/vec-regbuf.c +609 -0
  82. data/vendor/liburing/test/wait-timeout.c +1 -1
  83. data/vendor/liburing/test/wq-aff.c +5 -1
  84. data/vendor/liburing/test/zcrx.c +928 -0
  85. metadata +16 -32
  86. data/vendor/liburing/.github/workflows/codespell.yml +0 -25
  87. data/vendor/liburing/.github/workflows/shellcheck.yml +0 -20
@@ -14,6 +14,8 @@
14
14
  #include "liburing.h"
15
15
  #include "helpers.h"
16
16
 
17
+ static bool only_defer, no_defer;
18
+
17
19
  #define NVECS 128
18
20
 
19
21
  #define min(a, b) ((a) < (b) ? (a) : (b))
@@ -286,6 +288,9 @@ static int test_reads(struct io_uring *ring, int fd, int async)
286
288
  }
287
289
  }
288
290
 
291
+ for (i = 0; i < NVECS; i++)
292
+ free(vecs[i].iov_base);
293
+
289
294
  return 0;
290
295
  }
291
296
 
@@ -306,8 +311,12 @@ static int test_basic(struct io_uring *ring, int async)
306
311
  p.sq_entries = 32;
307
312
  p.cq_entries = 64;
308
313
  ret = io_uring_resize_rings(ring, &p);
309
- if (ret == -EINVAL)
314
+ if (ret == -EINVAL || ret == -ENOMEM) {
310
315
  return T_EXIT_SKIP;
316
+ } else if (ret < 0) {
317
+ fprintf(stderr, "resize=%d\n", ret);
318
+ return T_EXIT_FAIL;
319
+ }
311
320
 
312
321
  sqe = io_uring_get_sqe(ring);
313
322
  io_uring_prep_nop(sqe);
@@ -548,7 +557,14 @@ static int test(int flags, int fd, int async)
548
557
  struct io_uring ring;
549
558
  int ret;
550
559
 
560
+ if (no_defer)
561
+ return T_EXIT_SKIP;
562
+ if (!(flags & IORING_SETUP_DEFER_TASKRUN) && only_defer)
563
+ return T_EXIT_SKIP;
564
+
551
565
  ret = io_uring_queue_init_params(8, &ring, &p);
566
+ if (ret == -EINVAL)
567
+ return T_EXIT_SKIP;
552
568
  if (ret < 0) {
553
569
  fprintf(stderr, "ring setup failed: %d\n", ret);
554
570
  return T_EXIT_FAIL;
@@ -556,6 +572,12 @@ static int test(int flags, int fd, int async)
556
572
 
557
573
  ret = test_basic(&ring, async);
558
574
  if (ret == T_EXIT_SKIP) {
575
+ if (!(flags & IORING_SETUP_DEFER_TASKRUN)) {
576
+ io_uring_queue_exit(&ring);
577
+ only_defer = true;
578
+ } else {
579
+ no_defer = true;
580
+ }
559
581
  return T_EXIT_SKIP;
560
582
  } else if (ret == T_EXIT_FAIL) {
561
583
  fprintf(stderr, "test_basic %x failed\n", flags);
@@ -615,7 +637,7 @@ int main(int argc, char *argv[])
615
637
 
616
638
  ret = test(0, fd, 0);
617
639
  if (ret == T_EXIT_SKIP)
618
- return T_EXIT_SKIP;
640
+ goto try_defer;
619
641
  else if (ret == T_EXIT_FAIL)
620
642
  return T_EXIT_FAIL;
621
643
 
@@ -631,6 +653,7 @@ int main(int argc, char *argv[])
631
653
  if (ret == T_EXIT_FAIL)
632
654
  return T_EXIT_FAIL;
633
655
 
656
+ try_defer:
634
657
  ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, fd, 0);
635
658
  if (ret == T_EXIT_FAIL)
636
659
  return T_EXIT_FAIL;
@@ -151,7 +151,7 @@ static int test_buffers_update(void)
151
151
  struct io_uring ring;
152
152
  const int nr = 5;
153
153
  int buf_idx = 1, i, ret;
154
- int pipes[2];
154
+ int fds[2];
155
155
  char tmp_buf[1024];
156
156
  char tmp_buf2[1024];
157
157
  struct iovec vecs[nr];
@@ -172,7 +172,7 @@ static int test_buffers_update(void)
172
172
  printf("ring setup failed\n");
173
173
  return 1;
174
174
  }
175
- if (pipe(pipes) < 0) {
175
+ if (pipe(fds) < 0) {
176
176
  perror("pipe");
177
177
  return 1;
178
178
  }
@@ -184,7 +184,7 @@ static int test_buffers_update(void)
184
184
 
185
185
  /* test that CQE is not emitted before we're done with a buffer */
186
186
  sqe = io_uring_get_sqe(&ring);
187
- io_uring_prep_read_fixed(sqe, pipes[0], tmp_buf, 10, 0, 1);
187
+ io_uring_prep_read_fixed(sqe, fds[0], tmp_buf, 10, 0, 1);
188
188
  sqe->user_data = 100;
189
189
  ret = io_uring_submit(&ring);
190
190
  if (ret != 1) {
@@ -204,8 +204,8 @@ static int test_buffers_update(void)
204
204
 
205
205
  ret = io_uring_peek_cqe(&ring, &cqe); /* nothing should be there */
206
206
  assert(ret == -EAGAIN);
207
- close(pipes[0]);
208
- close(pipes[1]);
207
+ close(fds[0]);
208
+ close(fds[1]);
209
209
 
210
210
  ret = io_uring_wait_cqe(&ring, &cqe);
211
211
  assert(!ret && cqe->user_data == 100);
@@ -312,7 +312,6 @@ static int test_buffers_empty_buffers(void)
312
312
  return 0;
313
313
  }
314
314
 
315
-
316
315
  static int test_files(int ring_flags)
317
316
  {
318
317
  struct io_uring_cqe *cqe = NULL;
@@ -408,6 +407,53 @@ static int test_notag(void)
408
407
  return 0;
409
408
  }
410
409
 
410
+ static char buffer[16];
411
+
412
+ static int test_tagged_register_partial_fail(void)
413
+ {
414
+ __u64 tags[2] = { 1, 2 };
415
+ int fds[2] = { pipes[0], -1 };
416
+ struct iovec iovec[2];
417
+ struct io_uring ring;
418
+ int ret;
419
+
420
+ iovec[0].iov_base = buffer;
421
+ iovec[0].iov_len = 1;
422
+ iovec[1].iov_base = (void *)1UL;
423
+ iovec[1].iov_len = 1;
424
+
425
+ ret = io_uring_queue_init(1, &ring, 0);
426
+ if (ret) {
427
+ printf("ring setup failed\n");
428
+ return 1;
429
+ }
430
+
431
+ ret = io_uring_register_buffers_tags(&ring, iovec, tags, 2);
432
+ if (ret >= 0) {
433
+ fprintf(stderr, "io_uring_register_buffers_tags returned %i\n", ret);
434
+ return -EFAULT;
435
+ }
436
+
437
+ if (!check_cq_empty(&ring)) {
438
+ fprintf(stderr, "stray buffer CQEs found\n");
439
+ return -EFAULT;
440
+ }
441
+
442
+ ret = io_uring_register_files_tags(&ring, fds, tags, 2);
443
+ if (ret >= 0) {
444
+ fprintf(stderr, "io_uring_register_files_tags returned %i\n", ret);
445
+ return -EFAULT;
446
+ }
447
+
448
+ if (!check_cq_empty(&ring)) {
449
+ fprintf(stderr, "stray file CQEs found\n");
450
+ return -EFAULT;
451
+ }
452
+
453
+ io_uring_queue_exit(&ring);
454
+ return 0;
455
+ }
456
+
411
457
  int main(int argc, char *argv[])
412
458
  {
413
459
  int ring_flags[] = {0, IORING_SETUP_IOPOLL, IORING_SETUP_SQPOLL,
@@ -415,21 +461,28 @@ int main(int argc, char *argv[])
415
461
  int i, ret;
416
462
 
417
463
  if (argc > 1)
418
- return 0;
464
+ return T_EXIT_SKIP;
465
+
419
466
  if (!has_rsrc_update()) {
420
467
  fprintf(stderr, "doesn't support rsrc tags, skip\n");
421
- return 0;
468
+ return T_EXIT_SKIP;
422
469
  }
423
470
 
424
471
  if (pipe(pipes) < 0) {
425
472
  perror("pipe");
426
- return 1;
473
+ return T_EXIT_FAIL;
474
+ }
475
+
476
+ ret = test_tagged_register_partial_fail();
477
+ if (ret) {
478
+ printf("test_tagged_register_partial_fail() failed\n");
479
+ return T_EXIT_FAIL;
427
480
  }
428
481
 
429
482
  ret = test_notag();
430
483
  if (ret) {
431
484
  printf("test_notag failed\n");
432
- return ret;
485
+ return T_EXIT_FAIL;
433
486
  }
434
487
 
435
488
  for (i = 0; i < sizeof(ring_flags) / sizeof(ring_flags[0]); i++) {
@@ -441,21 +494,21 @@ int main(int argc, char *argv[])
441
494
  ret = test_files(flag);
442
495
  if (ret) {
443
496
  printf("test_tag failed, type %i\n", i);
444
- return ret;
497
+ return T_EXIT_FAIL;
445
498
  }
446
499
  }
447
500
 
448
501
  ret = test_buffers_update();
449
502
  if (ret) {
450
503
  printf("test_buffers_update failed\n");
451
- return ret;
504
+ return T_EXIT_FAIL;
452
505
  }
453
506
 
454
507
  ret = test_buffers_empty_buffers();
455
508
  if (ret) {
456
509
  printf("test_buffers_empty_buffers failed\n");
457
- return ret;
510
+ return T_EXIT_FAIL;
458
511
  }
459
512
 
460
- return 0;
513
+ return T_EXIT_PASS;
461
514
  }
@@ -69,9 +69,11 @@ static size_t page_sz;
69
69
  static char *tx_buffer, *rx_buffer;
70
70
  static struct iovec buffers_iov[__BUF_NR];
71
71
 
72
+ static bool has_regvec;
72
73
  static bool has_sendzc;
73
74
  static bool has_sendmsg;
74
75
  static bool hit_enomem;
76
+ static bool try_hugepages = 1;
75
77
 
76
78
  static int probe_zc_support(void)
77
79
  {
@@ -95,6 +97,7 @@ static int probe_zc_support(void)
95
97
 
96
98
  has_sendzc = p->ops_len > IORING_OP_SEND_ZC;
97
99
  has_sendmsg = p->ops_len > IORING_OP_SENDMSG_ZC;
100
+ has_regvec = p->ops_len > IORING_OP_READV_FIXED;
98
101
  io_uring_queue_exit(&ring);
99
102
  free(p);
100
103
  return 0;
@@ -255,117 +258,6 @@ static int test_send_faults(int sock_tx, int sock_rx)
255
258
  return T_EXIT_PASS;
256
259
  }
257
260
 
258
- static int create_socketpair_ip(struct sockaddr_storage *addr,
259
- int *sock_client, int *sock_server,
260
- bool ipv6, bool client_connect,
261
- bool msg_zc, bool tcp)
262
- {
263
- socklen_t addr_size;
264
- int family, sock, listen_sock = -1;
265
- int ret;
266
-
267
- memset(addr, 0, sizeof(*addr));
268
- if (ipv6) {
269
- struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
270
-
271
- family = AF_INET6;
272
- saddr->sin6_family = family;
273
- saddr->sin6_port = htons(0);
274
- addr_size = sizeof(*saddr);
275
- } else {
276
- struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
277
-
278
- family = AF_INET;
279
- saddr->sin_family = family;
280
- saddr->sin_port = htons(0);
281
- saddr->sin_addr.s_addr = htonl(INADDR_ANY);
282
- addr_size = sizeof(*saddr);
283
- }
284
-
285
- /* server sock setup */
286
- if (tcp) {
287
- sock = listen_sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
288
- } else {
289
- sock = *sock_server = socket(family, SOCK_DGRAM, 0);
290
- }
291
- if (sock < 0) {
292
- perror("socket");
293
- return 1;
294
- }
295
-
296
- ret = bind(sock, (struct sockaddr *)addr, addr_size);
297
- if (ret < 0) {
298
- perror("bind");
299
- return 1;
300
- }
301
-
302
- ret = getsockname(sock, (struct sockaddr *)addr, &addr_size);
303
- if (ret < 0) {
304
- fprintf(stderr, "getsockname failed %i\n", errno);
305
- return 1;
306
- }
307
-
308
- if (tcp) {
309
- ret = listen(sock, 128);
310
- assert(ret != -1);
311
- }
312
-
313
- if (ipv6) {
314
- struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
315
-
316
- inet_pton(AF_INET6, HOSTV6, &(saddr->sin6_addr));
317
- } else {
318
- struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
319
-
320
- inet_pton(AF_INET, HOST, &saddr->sin_addr);
321
- }
322
-
323
- /* client sock setup */
324
- if (tcp) {
325
- *sock_client = socket(family, SOCK_STREAM, IPPROTO_TCP);
326
- assert(client_connect);
327
- } else {
328
- *sock_client = socket(family, SOCK_DGRAM, 0);
329
- }
330
- if (*sock_client < 0) {
331
- perror("socket");
332
- return 1;
333
- }
334
- if (client_connect) {
335
- ret = connect(*sock_client, (struct sockaddr *)addr, addr_size);
336
- if (ret < 0) {
337
- perror("connect");
338
- return 1;
339
- }
340
- }
341
- if (msg_zc) {
342
- #ifdef SO_ZEROCOPY
343
- int val = 1;
344
-
345
- /*
346
- * NOTE: apps must not set SO_ZEROCOPY when using io_uring zc.
347
- * It's only here to test interactions with MSG_ZEROCOPY.
348
- */
349
- if (setsockopt(*sock_client, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
350
- perror("setsockopt zc");
351
- return 1;
352
- }
353
- #else
354
- fprintf(stderr, "no SO_ZEROCOPY\n");
355
- return 1;
356
- #endif
357
- }
358
- if (tcp) {
359
- *sock_server = accept(listen_sock, NULL, NULL);
360
- if (!*sock_server) {
361
- fprintf(stderr, "can't accept\n");
362
- return 1;
363
- }
364
- close(listen_sock);
365
- }
366
- return 0;
367
- }
368
-
369
261
  struct send_conf {
370
262
  bool fixed_buf;
371
263
  bool mix_register;
@@ -447,6 +339,11 @@ static int do_test_inet_send(struct io_uring *ring, int sock_client, int sock_se
447
339
  else
448
340
  io_uring_prep_sendmsg(sqe, sock_client, &msghdr[i], msg_flags);
449
341
 
342
+ if (real_fixed_buf) {
343
+ sqe->ioprio |= IORING_RECVSEND_FIXED_BUF;
344
+ sqe->buf_index = conf->buf_index;
345
+ }
346
+
450
347
  if (!conf->iovec) {
451
348
  io = &iov[i];
452
349
  iov_len = 1;
@@ -566,6 +463,16 @@ static int do_test_inet_send(struct io_uring *ring, int sock_client, int sock_se
566
463
  return 0;
567
464
  }
568
465
 
466
+ static int create_socketpair_ip(struct sockaddr_storage *addr,
467
+ int *sock_client, int *sock_server,
468
+ bool ipv6, bool client_connect,
469
+ bool msg_zc, bool tcp)
470
+ {
471
+ return t_create_socketpair_ip(addr, sock_client, sock_server, ipv6,
472
+ client_connect, msg_zc, tcp,
473
+ ipv6 ? HOSTV6 : HOST);
474
+ }
475
+
569
476
  static int test_inet_send(struct io_uring *ring)
570
477
  {
571
478
  struct send_conf conf;
@@ -590,7 +497,7 @@ static int test_inet_send(struct io_uring *ring)
590
497
  continue;
591
498
  #endif
592
499
  ret = create_socketpair_ip(&addr, &sock_client, &sock_server, ipv6,
593
- client_connect, msg_zc_set, tcp);
500
+ client_connect, msg_zc_set, tcp);
594
501
  if (ret) {
595
502
  fprintf(stderr, "sock prep failed %d\n", ret);
596
503
  return 1;
@@ -618,7 +525,11 @@ static int test_inet_send(struct io_uring *ring)
618
525
  conf.tcp = tcp;
619
526
  regbuf = conf.mix_register || conf.fixed_buf;
620
527
 
621
- if (conf.iovec && (!conf.use_sendmsg || regbuf || conf.cork))
528
+ if (!tcp && conf.long_iovec)
529
+ continue;
530
+ if (conf.use_sendmsg && regbuf && !has_regvec)
531
+ continue;
532
+ if (conf.iovec && (!conf.use_sendmsg || conf.cork))
622
533
  continue;
623
534
  if (!conf.zc) {
624
535
  if (regbuf)
@@ -636,7 +547,7 @@ static int test_inet_send(struct io_uring *ring)
636
547
  continue;
637
548
  if (!client_connect && conf.addr == NULL)
638
549
  continue;
639
- if (conf.use_sendmsg && (regbuf || !has_sendmsg))
550
+ if (conf.use_sendmsg && !has_sendmsg)
640
551
  continue;
641
552
  if (msg_zc_set && !conf.zc)
642
553
  continue;
@@ -900,6 +811,15 @@ static int run_basic_tests(void)
900
811
  return 0;
901
812
  }
902
813
 
814
+ static void free_buffers(void)
815
+ {
816
+ if (tx_buffer)
817
+ free(tx_buffer);
818
+ if (rx_buffer)
819
+ free(rx_buffer);
820
+ tx_buffer = rx_buffer = NULL;
821
+ }
822
+
903
823
  int main(int argc, char *argv[])
904
824
  {
905
825
  size_t len;
@@ -920,27 +840,29 @@ int main(int argc, char *argv[])
920
840
 
921
841
  page_sz = sysconf(_SC_PAGESIZE);
922
842
 
923
- len = LARGE_BUF_SIZE;
924
- tx_buffer = aligned_alloc(page_sz, len);
925
- rx_buffer = aligned_alloc(page_sz, len);
926
- if (tx_buffer && rx_buffer) {
927
- buffers_iov[BUF_T_LARGE].iov_base = tx_buffer;
928
- buffers_iov[BUF_T_LARGE].iov_len = len;
929
- } else {
930
- if (tx_buffer)
931
- free(tx_buffer);
932
- if (rx_buffer)
933
- free(rx_buffer);
843
+ if (try_hugepages) {
844
+ len = LARGE_BUF_SIZE;
845
+ tx_buffer = aligned_alloc(page_sz, len);
846
+ rx_buffer = aligned_alloc(page_sz, len);
934
847
 
935
- printf("skip large buffer tests, can't alloc\n");
848
+ if (tx_buffer && rx_buffer) {
849
+ buffers_iov[BUF_T_LARGE].iov_base = tx_buffer;
850
+ buffers_iov[BUF_T_LARGE].iov_len = len;
851
+ } else {
852
+ printf("skip large buffer tests, can't alloc\n");
853
+ free_buffers();
854
+ }
855
+ }
936
856
 
857
+ if (!tx_buffer) {
937
858
  len = 2 * page_sz;
938
859
  tx_buffer = aligned_alloc(page_sz, len);
939
860
  rx_buffer = aligned_alloc(page_sz, len);
940
- }
941
- if (!tx_buffer || !rx_buffer) {
942
- fprintf(stderr, "can't allocate buffers\n");
943
- return T_EXIT_FAIL;
861
+
862
+ if (!tx_buffer || !rx_buffer) {
863
+ fprintf(stderr, "can't allocate buffers\n");
864
+ return T_EXIT_FAIL;
865
+ }
944
866
  }
945
867
 
946
868
  srand((unsigned)time(NULL));
@@ -0,0 +1,216 @@
1
+ /* SPDX-License-Identifier: MIT */
2
+ /*
3
+ * Description: Test non-immediate sendmsg completion with non-static iovec
4
+ */
5
+ #include <stdio.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+ #include <unistd.h>
9
+ #include <errno.h>
10
+ #include <arpa/inet.h>
11
+ #include <sys/socket.h>
12
+ #include <pthread.h>
13
+ #include "liburing.h"
14
+ #include "helpers.h"
15
+
16
+ /* anything > 1 should be fine, do bigger than FAST_IOV to be sure */
17
+ #define IOVS 16
18
+ #define INFLIGHT 256
19
+
20
+ enum {
21
+ IS_ACCEPT = 0x89,
22
+ IS_SENDMSG = 0x91,
23
+ };
24
+
25
+ struct thread_data {
26
+ pthread_t thread;
27
+ pthread_barrier_t barrier;
28
+ int parent_pid;
29
+ };
30
+
31
+ static void *thread_fn(void *__data)
32
+ {
33
+ struct thread_data *data = __data;
34
+ struct sockaddr_in saddr;
35
+ int sockfd, ret;
36
+ char msg[64];
37
+
38
+ memset(&saddr, 0, sizeof(saddr));
39
+ saddr.sin_family = AF_INET;
40
+ saddr.sin_port = htons(9999);
41
+ inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
42
+
43
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
44
+ if (sockfd < 0) {
45
+ perror("socket");
46
+ goto done;
47
+ }
48
+
49
+ ret = connect(sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
50
+ if (ret < 0) {
51
+ perror("connect");
52
+ close(sockfd);
53
+ goto done;
54
+ }
55
+
56
+ pthread_barrier_wait(&data->barrier);
57
+ do {
58
+ usleep(100);
59
+ memset(msg, 0, sizeof(msg));
60
+ ret = recv(sockfd, msg, sizeof(msg), 0);
61
+ } while (ret > 0);
62
+
63
+ close(sockfd);
64
+ done:
65
+ kill(data->parent_pid, SIGUSR1);
66
+ return NULL;
67
+ }
68
+
69
+ static int queue_sends(struct io_uring *ring, int send_fd, struct thread_data *td, struct msghdr *msghdr)
70
+ {
71
+ struct io_uring_sqe *sqe;
72
+ char buf[64];
73
+ int i, ret, sbuf;
74
+ struct iovec *iovs = msghdr->msg_iov;
75
+
76
+ sbuf = 8 * 1024;
77
+ ret = setsockopt(send_fd, SOL_SOCKET, SO_SNDBUF, &sbuf, sizeof(sbuf));
78
+ if (ret < 0) {
79
+ perror("setsockopt");
80
+ return 1;
81
+ }
82
+
83
+ memset(buf, 0xaa, sizeof(buf));
84
+ for (i = 0; i < IOVS; i++) {
85
+ iovs[i].iov_base = buf;
86
+ iovs[i].iov_len = sizeof(buf);
87
+ }
88
+
89
+ /* fill send buffer */
90
+ for (i = 0;; i++) {
91
+ ret = sendmsg(send_fd, msghdr, MSG_DONTWAIT);
92
+ if (ret == -1) {
93
+ if (errno == EAGAIN)
94
+ break;
95
+ perror("sendmsg");
96
+ return 1;
97
+ }
98
+ }
99
+
100
+ /* kick receiver, start sendmsg */
101
+ msghdr->msg_iovlen = IOVS;
102
+ pthread_barrier_wait(&td->barrier);
103
+ for (i = 0; i < INFLIGHT; i++) {
104
+ sqe = io_uring_get_sqe(ring);
105
+ io_uring_prep_sendmsg(sqe, send_fd, msghdr, 0);
106
+ sqe->user_data = IS_SENDMSG;
107
+ }
108
+
109
+ return 0;
110
+ }
111
+
112
+ int main(int argc, char *argv[])
113
+ {
114
+ struct io_uring ring;
115
+ struct io_uring_sqe *sqe;
116
+ struct io_uring_cqe *cqe;
117
+ struct sockaddr_in saddr;
118
+ int val, send_fd, ret, sockfd, seen_sends = 0;
119
+ struct thread_data td;
120
+ struct iovec iovs[IOVS];
121
+ struct msghdr msghdr = {
122
+ .msg_iov = iovs,
123
+ .msg_iovlen = 1,
124
+ };
125
+
126
+ if (argc > 1)
127
+ return T_EXIT_SKIP;
128
+
129
+ memset(&saddr, 0, sizeof(saddr));
130
+ saddr.sin_family = AF_INET;
131
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
132
+ saddr.sin_port = htons(9999);
133
+
134
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
135
+ if (sockfd < 0) {
136
+ perror("socket");
137
+ return T_EXIT_FAIL;
138
+ }
139
+
140
+ val = 1;
141
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
142
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
143
+
144
+ ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
145
+ if (ret < 0) {
146
+ perror("bind");
147
+ close(sockfd);
148
+ return T_EXIT_FAIL;
149
+ }
150
+
151
+ ret = listen(sockfd, 1);
152
+ if (ret < 0) {
153
+ perror("listen");
154
+ close(sockfd);
155
+ return T_EXIT_FAIL;
156
+ }
157
+
158
+ ret = io_uring_queue_init(INFLIGHT, &ring, IORING_SETUP_SINGLE_ISSUER |
159
+ IORING_SETUP_DEFER_TASKRUN);
160
+ if (ret == -EINVAL) {
161
+ close(sockfd);
162
+ return T_EXIT_SKIP;
163
+ }
164
+
165
+ sqe = io_uring_get_sqe(&ring);
166
+ io_uring_prep_multishot_accept(sqe, sockfd, NULL, NULL, 0);
167
+ sqe->user_data = IS_ACCEPT;
168
+ io_uring_submit(&ring);
169
+
170
+ /* check for no multishot accept */
171
+ ret = io_uring_peek_cqe(&ring, &cqe);
172
+ if (!ret && cqe->res == -EINVAL) {
173
+ close(sockfd);
174
+ return T_EXIT_SKIP;
175
+ }
176
+
177
+ /* start receiver */
178
+ td.parent_pid = getpid();
179
+ pthread_barrier_init(&td.barrier, NULL, 2);
180
+ pthread_create(&td.thread, NULL, thread_fn, &td);
181
+
182
+ do {
183
+ ret = io_uring_submit_and_wait(&ring, 1);
184
+ if (ret < 0) {
185
+ fprintf(stderr, "submit: %d\n", ret);
186
+ break;
187
+ }
188
+ ret = io_uring_peek_cqe(&ring, &cqe);
189
+ if (ret) {
190
+ fprintf(stderr, "peek: %d\n", ret);
191
+ break;
192
+ }
193
+
194
+ switch (cqe->user_data) {
195
+ case IS_ACCEPT:
196
+ send_fd = cqe->res;
197
+ io_uring_cqe_seen(&ring, cqe);
198
+
199
+ ret = queue_sends(&ring, send_fd, &td, &msghdr);
200
+ if (ret)
201
+ exit(T_EXIT_FAIL);
202
+ break;
203
+ case IS_SENDMSG:
204
+ io_uring_cqe_seen(&ring, cqe);
205
+ seen_sends++;
206
+ if (seen_sends == INFLIGHT)
207
+ exit(0);
208
+ break;
209
+ default:
210
+ fprintf(stderr, "got unknown cqe\n");
211
+ return T_EXIT_FAIL;
212
+ }
213
+ } while (1);
214
+
215
+ return T_EXIT_FAIL;
216
+ }