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
@@ -0,0 +1,150 @@
1
+ /* SPDX-License-Identifier: MIT */
2
+ /*
3
+ * Description: run various nop tests
4
+ *
5
+ */
6
+ #include <errno.h>
7
+ #include <stdio.h>
8
+ #include <unistd.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <fcntl.h>
12
+
13
+ #include "liburing.h"
14
+ #include "helpers.h"
15
+ #include "test.h"
16
+
17
+ static int seq;
18
+
19
+ static int fail_cqe(struct io_uring_cqe *cqe, int off, const char *msg)
20
+ {
21
+ fprintf(stderr, "Bad cqe at off %d: %s\n", off, msg);
22
+ fprintf(stderr, "CQE ud=%lu, res=%u, flags=%x\n", (long) cqe->user_data, cqe->res, cqe->flags);
23
+ return T_EXIT_FAIL;
24
+ }
25
+
26
+ static int test_ring(unsigned flags)
27
+ {
28
+ struct io_uring_sqe *sqe;
29
+ struct io_uring_cqe *cqe;
30
+ struct io_uring ring;
31
+ struct io_uring_params p = { };
32
+ unsigned head;
33
+ int ret, i, cqe_nr;
34
+
35
+ p.flags = flags | IORING_SETUP_CQSIZE;
36
+ p.cq_entries = 8;
37
+ ret = io_uring_queue_init_params(8, &ring, &p);
38
+ if (ret) {
39
+ if (ret == -EINVAL)
40
+ return T_EXIT_SKIP;
41
+ fprintf(stderr, "ring setup failed: %d\n", ret);
42
+ return T_EXIT_FAIL;
43
+ }
44
+
45
+ /*
46
+ * Prep 8 NOP requests, with the first being a 16b one to
47
+ * unalign the ring. The 4 latter ones will hit overflow,
48
+ * and after the first 4 we should see a SKIP cqe.
49
+ */
50
+ for (i = 0; i < 8; i++) {
51
+ sqe = io_uring_get_sqe(&ring);
52
+ io_uring_prep_nop(sqe);
53
+ if (i)
54
+ sqe->nop_flags = IORING_NOP_CQE32;
55
+ sqe->user_data = ++seq;
56
+ }
57
+
58
+ io_uring_submit(&ring);
59
+
60
+ /*
61
+ * Should find the following CQEs:
62
+ *
63
+ * 1: 16b with ud 1
64
+ * 2: 32b with ud 2
65
+ * 3: 32b with ud 3
66
+ * 4: 32b with ud 4
67
+ * 5: 16b skip CQE
68
+ *
69
+ * And then we should have overflow pending to flush the rest
70
+ */
71
+ i = cqe_nr = 0;
72
+ io_uring_for_each_cqe(&ring, head, cqe) {
73
+ switch (i) {
74
+ case 0:
75
+ if (cqe->user_data != 1)
76
+ return fail_cqe(cqe, i, "Non-overflow UD");
77
+ if (cqe->flags & IORING_CQE_F_32)
78
+ return fail_cqe(cqe, i, "Non-overflow flags");
79
+ break;
80
+ case 1:
81
+ case 2:
82
+ case 3:
83
+ if (cqe->user_data != i + 1)
84
+ return fail_cqe(cqe, i, "Non-overflow 32b UD");
85
+ if (!(cqe->flags & IORING_CQE_F_32))
86
+ return fail_cqe(cqe, i, "Non-overflow 32b flags");
87
+ break;
88
+ case 4:
89
+ if (!(cqe->flags & IORING_CQE_F_SKIP))
90
+ return fail_cqe(cqe, i, "Non-overflow skip");
91
+ break;
92
+ default:
93
+ return fail_cqe(cqe, i, "Bogus CQE");
94
+ }
95
+ i++;
96
+ cqe_nr += io_uring_cqe_nr(cqe);
97
+ }
98
+ io_uring_cq_advance(&ring, cqe_nr);
99
+
100
+ /*
101
+ * This should flush overflow, and we should see:
102
+ *
103
+ * 1: 32b with ud 5
104
+ * 2: 32b with ud 6
105
+ * 3: 32b with ud 7
106
+ * 4: 32b with ud 8
107
+ */
108
+ for (i = 0; i < 4; i++) {
109
+ ret = io_uring_wait_cqe(&ring, &cqe);
110
+ if (ret) {
111
+ fprintf(stderr, "wait ret %d\n", ret);
112
+ return T_EXIT_FAIL;
113
+ }
114
+ switch (i) {
115
+ case 0:
116
+ case 1:
117
+ case 2:
118
+ case 3:
119
+ if (cqe->user_data != i + 5)
120
+ return fail_cqe(cqe, i + 5, "Overflow UD");
121
+ if (!(cqe->flags & IORING_CQE_F_32))
122
+ return fail_cqe(cqe, i + 5, "Overflow flags");
123
+ break;
124
+ default:
125
+ return fail_cqe(cqe, i + 5, "Bogus CQE");
126
+ }
127
+ io_uring_cqe_seen(&ring, cqe);
128
+ }
129
+
130
+ io_uring_queue_exit(&ring);
131
+ return ret;
132
+ }
133
+
134
+ int main(int argc, char *argv[])
135
+ {
136
+ int ret;
137
+
138
+ if (argc > 1)
139
+ return T_EXIT_SKIP;
140
+
141
+ ret = test_ring(IORING_SETUP_CQE_MIXED);
142
+ if (ret == T_EXIT_SKIP) {
143
+ return T_EXIT_SKIP;
144
+ } else if (ret != T_EXIT_PASS) {
145
+ fprintf(stderr, "Mixed ring test failed\n");
146
+ return ret;
147
+ }
148
+
149
+ return T_EXIT_PASS;
150
+ }
@@ -0,0 +1,126 @@
1
+ /* SPDX-License-Identifier: MIT */
2
+ /*
3
+ * Description: run various nop tests
4
+ *
5
+ */
6
+ #include <errno.h>
7
+ #include <stdio.h>
8
+ #include <unistd.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <fcntl.h>
12
+
13
+ #include "liburing.h"
14
+ #include "helpers.h"
15
+ #include "test.h"
16
+
17
+ static int seq;
18
+ static int extra1 = 100, extra2 = 200;
19
+ static int want_extra1 = 100, want_extra2 = 200;
20
+
21
+ static int test_single_nop(struct io_uring *ring, unsigned req_flags, bool cqe32)
22
+ {
23
+ struct io_uring_cqe *cqe;
24
+ struct io_uring_sqe *sqe;
25
+ int ret;
26
+
27
+ sqe = io_uring_get_sqe(ring);
28
+ if (!sqe) {
29
+ fprintf(stderr, "get sqe failed\n");
30
+ goto err;
31
+ }
32
+
33
+ io_uring_prep_nop(sqe);
34
+ sqe->user_data = ++seq;
35
+ sqe->flags |= req_flags;
36
+ if (cqe32) {
37
+ sqe->nop_flags = IORING_NOP_CQE32;
38
+ sqe->off = extra1++;
39
+ sqe->addr = extra2++;
40
+ }
41
+
42
+ ret = io_uring_submit(ring);
43
+ if (ret <= 0) {
44
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
45
+ goto err;
46
+ }
47
+
48
+ ret = io_uring_wait_cqe(ring, &cqe);
49
+ if (ret < 0) {
50
+ fprintf(stderr, "wait completion %d\n", ret);
51
+ goto err;
52
+ }
53
+ if (!cqe->user_data) {
54
+ fprintf(stderr, "Unexpected 0 user_data: %ld\n", (long) cqe->user_data);
55
+ goto err;
56
+ }
57
+ if (cqe32) {
58
+ if (!(cqe->flags & IORING_CQE_F_32)) {
59
+ fprintf(stderr, "CQE_F_32 not set\n");
60
+ goto err;
61
+ }
62
+ if (cqe->big_cqe[0] != want_extra1) {
63
+ fprintf(stderr, "Unexpected extra1: %ld, want %d\n", (long) cqe->big_cqe[0], want_extra1);
64
+ goto err;
65
+
66
+ }
67
+ want_extra1++;
68
+ if (cqe->big_cqe[1] != want_extra2) {
69
+ fprintf(stderr, "Unexpected extra2: %ld, want %d\n", (long) cqe->big_cqe[1], want_extra2);
70
+ goto err;
71
+ }
72
+ want_extra2++;
73
+ }
74
+ io_uring_cqe_seen(ring, cqe);
75
+ return T_EXIT_PASS;
76
+ err:
77
+ return T_EXIT_FAIL;
78
+ }
79
+
80
+ static int test_ring(unsigned flags)
81
+ {
82
+ struct io_uring ring;
83
+ struct io_uring_params p = { };
84
+ int ret, i;
85
+
86
+ p.flags = flags;
87
+ ret = io_uring_queue_init_params(8, &ring, &p);
88
+ if (ret) {
89
+ if (ret == -EINVAL)
90
+ return T_EXIT_SKIP;
91
+ fprintf(stderr, "ring setup failed: %d\n", ret);
92
+ return T_EXIT_FAIL;
93
+ }
94
+
95
+ test_single_nop(&ring, 0, 0);
96
+
97
+ for (i = 0; i < 16; i++) {
98
+ ret = test_single_nop(&ring, 0, 1);
99
+ if (ret) {
100
+ printf("fail off %d\n", i);
101
+ fprintf(stderr, "test_single_nop failed\n");
102
+ goto err;
103
+ }
104
+ }
105
+ err:
106
+ io_uring_queue_exit(&ring);
107
+ return ret;
108
+ }
109
+
110
+ int main(int argc, char *argv[])
111
+ {
112
+ int ret;
113
+
114
+ if (argc > 1)
115
+ return T_EXIT_SKIP;
116
+
117
+ ret = test_ring(IORING_SETUP_CQE_MIXED);
118
+ if (ret == T_EXIT_SKIP) {
119
+ return T_EXIT_SKIP;
120
+ } else if (ret != T_EXIT_PASS) {
121
+ fprintf(stderr, "Mixed ring test failed\n");
122
+ return ret;
123
+ }
124
+
125
+ return T_EXIT_PASS;
126
+ }
@@ -0,0 +1,166 @@
1
+ // SPDX-License-Identifier: MIT
2
+ /*
3
+ * Description: test pipe creation through io_uring
4
+ *
5
+ */
6
+ #include <stdio.h>
7
+ #include <string.h>
8
+ #include <unistd.h>
9
+
10
+ #include "helpers.h"
11
+ #include "liburing.h"
12
+
13
+ static int no_pipe;
14
+
15
+ struct params {
16
+ int fixed;
17
+ int async;
18
+ int too_small;
19
+ };
20
+
21
+ static int pipe_comms(struct io_uring *ring, int *fds, struct params *p)
22
+ {
23
+ struct io_uring_sqe *sqe;
24
+ struct io_uring_cqe *cqe;
25
+ char src[32], dst[32];
26
+ int i, ret;
27
+
28
+ memset(src, 0x5a, sizeof(src));
29
+ memset(dst, 0, sizeof(dst));
30
+
31
+ sqe = io_uring_get_sqe(ring);
32
+ io_uring_prep_write(sqe, fds[1], src, sizeof(src), 0);
33
+ if (p->fixed)
34
+ sqe->flags |= IOSQE_FIXED_FILE;
35
+ sqe->user_data = 1;
36
+ io_uring_submit(ring);
37
+
38
+ sqe = io_uring_get_sqe(ring);
39
+ io_uring_prep_read(sqe, fds[0], dst, sizeof(dst), 0);
40
+ if (p->fixed)
41
+ sqe->flags |= IOSQE_FIXED_FILE;
42
+ sqe->user_data = 2;
43
+ io_uring_submit(ring);
44
+
45
+ for (i = 0; i < 2; i++) {
46
+ ret = io_uring_wait_cqe(ring, &cqe);
47
+ if (ret) {
48
+ fprintf(stderr, "wait cqe %d\n", ret);
49
+ return 1;
50
+ }
51
+ if (cqe->res != 32) {
52
+ printf("ud=%d, res=%d\n", (int) cqe->user_data, cqe->res);
53
+ return 1;
54
+ }
55
+ io_uring_cqe_seen(ring, cqe);
56
+ }
57
+
58
+ return memcmp(dst, src, sizeof(src));
59
+ }
60
+
61
+ static int pipe_test(int init_flags, struct params *p)
62
+ {
63
+ struct io_uring ring;
64
+ struct io_uring_sqe *sqe;
65
+ struct io_uring_cqe *cqe;
66
+ int ret, fds[2];
67
+
68
+ ret = io_uring_queue_init(8, &ring, init_flags);
69
+ /* can hit -ENOMEM due to repeated ring creation and teardowns */
70
+ if (ret == -ENOMEM) {
71
+ usleep(1000);
72
+ return 0;
73
+ } else if (ret) {
74
+ fprintf(stderr, "ring_init: %d\n", ret);
75
+ return 1;
76
+ }
77
+
78
+ if (p->fixed) {
79
+ int sz;
80
+
81
+ if (p->too_small)
82
+ sz = 1;
83
+ else
84
+ sz = 100;
85
+ ret = io_uring_register_files_sparse(&ring, sz);
86
+ if (ret) {
87
+ if (ret == -EINVAL) {
88
+ no_pipe = 1;
89
+ return 0;
90
+ }
91
+ fprintf(stderr, "Failed to register sparse table %d\n", ret);
92
+ return 1;
93
+ }
94
+ }
95
+
96
+ fds[0] = fds[1] = -1;
97
+ sqe = io_uring_get_sqe(&ring);
98
+ if (!p->fixed)
99
+ io_uring_prep_pipe(sqe, fds, 0);
100
+ else
101
+ io_uring_prep_pipe_direct(sqe, fds, 0, IORING_FILE_INDEX_ALLOC);
102
+ if (p->async)
103
+ sqe->flags |= IOSQE_ASYNC;
104
+
105
+ io_uring_submit(&ring);
106
+ ret = io_uring_wait_cqe(&ring, &cqe);
107
+ if (ret) {
108
+ fprintf(stderr, "wait: %d\n", ret);
109
+ return 1;
110
+ }
111
+ if (cqe->res) {
112
+ if (cqe->res == -EINVAL) {
113
+ no_pipe = 1;
114
+ return 0;
115
+ }
116
+ if (p->fixed && p->too_small && cqe->res == -ENFILE)
117
+ goto done;
118
+ fprintf(stderr, "Bad cqe res %d\n", cqe->res);
119
+ return 1;
120
+ }
121
+
122
+ io_uring_cqe_seen(&ring, cqe);
123
+
124
+ ret = pipe_comms(&ring, fds, p);
125
+ if (!p->fixed) {
126
+ close(fds[0]);
127
+ close(fds[1]);
128
+ }
129
+ done:
130
+ io_uring_queue_exit(&ring);
131
+ return ret;
132
+ }
133
+
134
+ int main(int argc, char *argv[])
135
+ {
136
+ int init_flags[] = { 0, IORING_SETUP_SQPOLL, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER };
137
+ struct params ps[] = {
138
+ { 0, 0, 0 },
139
+ { 0, 1, 0 },
140
+ { 1, 0, 0 },
141
+ { 1, 1, 0 },
142
+ { 0, 0, 1 },
143
+ { 0, 1, 1 },
144
+ { 1, 0, 1 },
145
+ { 1, 1, 1 } };
146
+
147
+ int i, j;
148
+
149
+ if (argc > 1)
150
+ return T_EXIT_SKIP;
151
+
152
+ for (i = 0; i < ARRAY_SIZE(init_flags); i++) {
153
+ for (j = 0; j < ARRAY_SIZE(ps); j++) {
154
+ if (pipe_test(init_flags[i], &ps[j])) {
155
+ fprintf(stderr, "pipe %x %d/%d/%d failed\n",
156
+ init_flags[i], ps[j].fixed, ps[j].async,
157
+ ps[j].too_small);
158
+ return T_EXIT_FAIL;
159
+ }
160
+ if (no_pipe)
161
+ return T_EXIT_SKIP;
162
+ }
163
+ }
164
+
165
+ return T_EXIT_PASS;
166
+ }
@@ -158,7 +158,7 @@ static int test_mshot(struct io_uring *ring, struct data *d)
158
158
 
159
159
  d->fd = fd[1];
160
160
 
161
- if (posix_memalign((void *) &buf, 16384, BUF_SIZE * NREQS))
161
+ if (posix_memalign((void **) &buf, 16384, BUF_SIZE * NREQS))
162
162
  return T_EXIT_FAIL;
163
163
 
164
164
  br = io_uring_setup_buf_ring(ring, NREQS, 1, 0, &ret);
@@ -221,6 +221,18 @@ static int test_mshot(struct io_uring *ring, struct data *d)
221
221
  fprintf(stderr, "Got too many requests?\n");
222
222
  return T_EXIT_FAIL;
223
223
  }
224
+ /*
225
+ * We're using unix sockets, and later kernels got support added
226
+ * for msg_inq querying. On those kernels, we cannot rely on
227
+ * the multishot terminating on a zero receive, as io_uring
228
+ * will not do that retry as it KNOWS there's zero bytes
229
+ * pending. Hence we need to actively quiet at that point. Inc
230
+ * 'i' as well as we don't get the non-MORE CQE posted.
231
+ */
232
+ if (i == NREQS) {
233
+ i++;
234
+ break;
235
+ }
224
236
  } while (1);
225
237
 
226
238
  if (i != NREQS + 1) {
@@ -28,6 +28,7 @@
28
28
 
29
29
  #define NR_RDS 4
30
30
 
31
+ static bool no_iter_support;
31
32
  static bool no_limit_support;
32
33
 
33
34
  static int use_port = PORT;
@@ -48,6 +49,7 @@ struct recv_data {
48
49
  int total_bytes;
49
50
 
50
51
  int recv_bundle;
52
+ int mshot_limit;
51
53
 
52
54
  int use_port;
53
55
  int id;
@@ -64,13 +66,11 @@ static void arm_recv(struct io_uring *ring, struct recv_data *rd)
64
66
  struct io_uring_sqe *sqe;
65
67
  int len = PER_ITER_LIMIT;
66
68
 
67
- if (rd->total_bytes && rd->bytes_since_arm > PER_MSHOT_LIMIT)
68
- rd->mshot_too_big++;
69
-
70
69
  rd->bytes_since_arm = 0;
71
70
  sqe = io_uring_get_sqe(ring);
72
71
  io_uring_prep_recv_multishot(sqe, rd->accept_fd, NULL, len, 0);
73
- sqe->optlen = PER_MSHOT_LIMIT;
72
+ if (rd->mshot_limit)
73
+ sqe->optlen = PER_MSHOT_LIMIT;
74
74
  if (rd->recv_bundle)
75
75
  sqe->ioprio |= IORING_RECVSEND_BUNDLE;
76
76
  sqe->buf_group = RECV_BGID;
@@ -155,7 +155,7 @@ static int recv_get_cqe(struct io_uring *ring,
155
155
  int ret;
156
156
 
157
157
  do {
158
- ret = io_uring_submit_and_wait_timeout(ring, cqe, 1, &ts, NULL);
158
+ ret = io_uring_wait_cqe_timeout(ring, cqe, &ts);
159
159
  if (!ret) {
160
160
  struct recv_data *rd = io_uring_cqe_get_data(*cqe);
161
161
 
@@ -196,6 +196,7 @@ static int do_recv(struct io_uring *ring)
196
196
  struct recv_data *rd;
197
197
  int ret = 1;
198
198
  int done = 0;
199
+ int pending_submit = 0;
199
200
 
200
201
  last_rd = NULL;
201
202
  bytes_since_last = 0;
@@ -223,9 +224,15 @@ static int do_recv(struct io_uring *ring)
223
224
  done++;
224
225
  continue;
225
226
  }
227
+ if (rd->mshot_limit && rd->bytes_since_arm > PER_MSHOT_LIMIT)
228
+ rd->mshot_too_big++;
226
229
  io_uring_cqe_seen(ring, cqe);
227
- if (!(cqe->flags & IORING_CQE_F_MORE) && rd->recv_bytes)
230
+ if (!(cqe->flags & IORING_CQE_F_MORE) && rd->recv_bytes) {
228
231
  arm_recv(ring, rd);
232
+ pending_submit++;
233
+ }
234
+ if (pending_submit == NR_RDS)
235
+ io_uring_submit(ring);
229
236
  } while (done < NR_RDS);
230
237
 
231
238
  ret = 0;
@@ -241,7 +248,7 @@ static void *recv_fn(void *data)
241
248
  struct io_uring_buf_ring *br = NULL;
242
249
  struct io_uring ring;
243
250
  unsigned int buf_len;
244
- void *buf, *ptr;
251
+ void *buf = NULL, *ptr;
245
252
  int ret, sock[NR_RDS], i;
246
253
  int brflags, ring_setup = 0;
247
254
 
@@ -255,7 +262,7 @@ static void *recv_fn(void *data)
255
262
  ret = io_uring_queue_init_params(16, &ring, &p);
256
263
  if (ret) {
257
264
  if (ret == -EINVAL) {
258
- no_limit_support = true;
265
+ no_iter_support = true;
259
266
  goto skip;
260
267
  }
261
268
  fprintf(stderr, "ring init: %d\n", ret);
@@ -270,7 +277,7 @@ static void *recv_fn(void *data)
270
277
  br = io_uring_setup_buf_ring(&ring, RECV_BIDS, RECV_BGID, brflags, &ret);
271
278
  if (!br) {
272
279
  if (ret == -EINVAL) {
273
- no_limit_support = true;
280
+ no_iter_support = true;
274
281
  goto skip;
275
282
  }
276
283
  fprintf(stderr, "failed setting up recv ring %d\n", ret);
@@ -284,7 +291,7 @@ static void *recv_fn(void *data)
284
291
  ptr += buf_len;
285
292
  }
286
293
  io_uring_buf_ring_advance(br, RECV_BIDS);
287
-
294
+
288
295
  for (i = 0; i < NR_RDS; i++) {
289
296
  ret = recv_prep(&ring, &rds[i], &sock[i]);
290
297
  if (ret) {
@@ -299,7 +306,10 @@ static void *recv_fn(void *data)
299
306
 
300
307
  if (!io_uring_peek_cqe(&ring, &cqe)) {
301
308
  if (cqe->res == -EINVAL) {
302
- no_limit_support = true;
309
+ if (rds[0].mshot_limit)
310
+ no_limit_support = true;
311
+ else
312
+ no_iter_support = true;
303
313
  goto skip;
304
314
  }
305
315
  }
@@ -319,6 +329,7 @@ skip:
319
329
  io_uring_free_buf_ring(&ring, br, RECV_BIDS, RECV_BGID);
320
330
  if (ring_setup)
321
331
  io_uring_queue_exit(&ring);
332
+ free(buf);
322
333
  err:
323
334
  return (void *)(intptr_t)ret;
324
335
  }
@@ -379,7 +390,8 @@ struct res {
379
390
  int unfair;
380
391
  };
381
392
 
382
- static int test(int nr_send, int bundle, unsigned int queue_flags, struct res *r)
393
+ static int test(int nr_send, int bundle, int mshot_limit,
394
+ unsigned int queue_flags, struct res *r)
383
395
  {
384
396
  struct recv_data rds[NR_RDS] = { };
385
397
  pthread_t recv_thread;
@@ -401,6 +413,7 @@ static int test(int nr_send, int bundle, unsigned int queue_flags, struct res *r
401
413
  rd->max_sends = nr_send;
402
414
  rd->recv_bundle = bundle;
403
415
  rd->recv_bytes = nr_send * 4096;
416
+ rd->mshot_limit = mshot_limit;
404
417
  rd->id = i + 1;
405
418
  }
406
419
 
@@ -435,9 +448,9 @@ static int run_tests(void)
435
448
  struct res r;
436
449
  int ret;
437
450
 
438
- ret = test(2, 1, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
451
+ ret = test(2, 1, 0, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
439
452
  if (ret) {
440
- if (no_limit_support)
453
+ if (no_iter_support)
441
454
  return T_EXIT_SKIP;
442
455
  fprintf(stderr, "test DEFER bundle failed\n");
443
456
  return T_EXIT_FAIL;
@@ -449,52 +462,86 @@ static int run_tests(void)
449
462
  return T_EXIT_FAIL;
450
463
  }
451
464
 
452
- ret = test(2, 0, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
465
+ ret = test(2, 0, 0, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
453
466
  if (ret) {
454
467
  fprintf(stderr, "test DEFER failed\n");
455
468
  return T_EXIT_FAIL;
456
469
  }
457
470
 
458
- /* DEFER_TASKRUN should be fully fair and not have overshoots */
459
471
  if (r.unfair || r.mshot_too_big) {
460
472
  fprintf(stderr, "DEFER unfair=%d, too_big=%d\n", r.unfair, r.mshot_too_big);
461
473
  return T_EXIT_FAIL;
462
474
  }
463
475
 
464
- ret = test(2, 1, IORING_SETUP_COOP_TASKRUN, &r);
476
+ ret = test(2, 1, 0, IORING_SETUP_COOP_TASKRUN, &r);
465
477
  if (ret) {
466
478
  fprintf(stderr, "test COOP bundle failed\n");
467
479
  return T_EXIT_FAIL;
468
480
  }
469
481
 
470
- /*
471
- * normal task_work should not have overshoots, but may not be fair
472
- * because of the re-arming.
473
- */
474
- if (r.unfair)
475
- fprintf(stdout, "!DEFER bundle unfair, expected\n");
476
- if (r.mshot_too_big) {
477
- fprintf(stderr, "!DEFER bundle too_big=%d\n", r.mshot_too_big);
482
+ if (r.unfair || r.mshot_too_big) {
483
+ fprintf(stderr, "COOP bundle too_big=%d\n", r.mshot_too_big);
478
484
  return T_EXIT_FAIL;
479
485
  }
480
486
 
481
- ret = test(2, 0, IORING_SETUP_COOP_TASKRUN, &r);
487
+ ret = test(2, 0, 0, IORING_SETUP_COOP_TASKRUN, &r);
482
488
  if (ret) {
483
489
  fprintf(stderr, "test COOP failed\n");
484
490
  return T_EXIT_FAIL;
485
491
  }
486
492
 
487
- /*
488
- * normal task_work should not have overshoots, but may not be fair
489
- * because of the re-arming.
490
- */
491
- if (r.unfair)
492
- fprintf(stdout, "!DEFER unfair, expected\n");
493
- if (r.mshot_too_big) {
493
+ if (r.unfair || r.mshot_too_big) {
494
494
  fprintf(stderr, "!DEFER too_big=%d\n", r.mshot_too_big);
495
495
  return T_EXIT_FAIL;
496
496
  }
497
-
497
+
498
+ ret = test(2, 1, 1, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
499
+ if (ret) {
500
+ if (no_limit_support)
501
+ return T_EXIT_PASS;
502
+ fprintf(stderr, "test DEFER bundle cap failed\n");
503
+ return T_EXIT_FAIL;
504
+ }
505
+
506
+ /* DEFER_TASKRUN should be fully fair and not have overshoots */
507
+ if (r.unfair || r.mshot_too_big) {
508
+ fprintf(stderr, "DEFER bundle cap unfair=%d, too_big=%d\n", r.unfair, r.mshot_too_big);
509
+ return T_EXIT_FAIL;
510
+ }
511
+
512
+ ret = test(2, 0, 1, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER, &r);
513
+ if (ret) {
514
+ fprintf(stderr, "test DEFER cap failed\n");
515
+ return T_EXIT_FAIL;
516
+ }
517
+
518
+ if (r.unfair || r.mshot_too_big) {
519
+ fprintf(stderr, "DEFER cap unfair=%d, too_big=%d\n", r.unfair, r.mshot_too_big);
520
+ return T_EXIT_FAIL;
521
+ }
522
+
523
+ ret = test(2, 1, 1, IORING_SETUP_COOP_TASKRUN, &r);
524
+ if (ret) {
525
+ fprintf(stderr, "test COOP bundle cap failed\n");
526
+ return T_EXIT_FAIL;
527
+ }
528
+
529
+ if (r.unfair || r.mshot_too_big) {
530
+ fprintf(stderr, "COOP bundle cap too_big=%d\n", r.mshot_too_big);
531
+ return T_EXIT_FAIL;
532
+ }
533
+
534
+ ret = test(2, 0, 1, IORING_SETUP_COOP_TASKRUN, &r);
535
+ if (ret) {
536
+ fprintf(stderr, "test COOP cap failed\n");
537
+ return T_EXIT_FAIL;
538
+ }
539
+
540
+ if (r.unfair || r.mshot_too_big) {
541
+ fprintf(stderr, "COOP cap too_big=%d\n", r.mshot_too_big);
542
+ return T_EXIT_FAIL;
543
+ }
544
+
498
545
  return T_EXIT_PASS;
499
546
  }
500
547