@@ -241,22 +241,22 @@ only the
passthrough command for NVMe passthrough needs this. Available since 5.19.
.TP
.B IORING_SETUP_SINGLE_ISSUER
-A hint to the kernel that only a single task can submit requests, which is used
-for internal optimisations. The kernel enforces the rule, which only affects
-.I
-io_uring_enter(2)
-calls submitting requests and will fail them with
+A hint to the kernel that only a single task (or thread) will submit requests, which is
+used for internal optimisations. The submission task is either the task that created the
+ring, or if
+.B IORING_SETUP_R_DISABLED
+is specified then it is the task that enables the ring through
+.BR io_uring_register (2) .
+The kernel enforces this rule, failing requests with
.B -EEXIST
if the restriction is violated.
-The submitter task may differ from the task that created the ring.
Note that when
.B IORING_SETUP_SQPOLL
is set it is considered that the polling task is doing all submissions
on behalf of the userspace and so it always complies with the rule disregarding
how many userspace tasks do
-.I
-io_uring_enter(2).
-Available since 5.20.
+.BR io_uring_enter(2).
+Available since 6.0.
.TP
.B IORING_SETUP_DEFER_TASKRUN
By default, io_uring will process all outstanding work at the end of any system
@@ -123,6 +123,7 @@ void *thread(void *t)
{
struct thread_data *td = t;
+ io_uring_enable_rings(&td->ring);
io_uring_prep_read(io_uring_get_sqe(&td->ring), td->efd, td->buff, sizeof(td->buff), 0);
io_uring_submit(&td->ring);
@@ -138,11 +139,12 @@ static int test_thread_shutdown(void)
uint64_t val = 1;
ret = io_uring_queue_init(8, &td.ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN);
+ IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_R_DISABLED);
if (ret)
return ret;
- CHECK(io_uring_get_events(&td.ring) == -EEXIST);
+ CHECK(io_uring_get_events(&td.ring) == -EBADFD);
td.efd = eventfd(0, 0);
CHECK(td.efd >= 0);
@@ -150,6 +152,8 @@ static int test_thread_shutdown(void)
CHECK(pthread_create(&t1, NULL, thread, &td) == 0);
CHECK(pthread_join(t1, NULL) == 0);
+ CHECK(io_uring_get_events(&td.ring) == -EEXIST);
+
CHECK(write(td.efd, &val, sizeof(val)) == sizeof(val));
CHECK(io_uring_wait_cqe(&td.ring, &cqe) == -EEXIST);
@@ -96,30 +96,48 @@ int main(int argc, char *argv[])
if (!fork_t()) {
ret = try_submit(&ring);
if (ret != -EEXIST)
- fprintf(stderr, "not owner child could submit %i\n", ret);
+ fprintf(stderr, "1: not owner child could submit %i\n", ret);
return ret != -EEXIST;
}
wait_child_t();
io_uring_queue_exit(&ring);
/* test that the first submitter but not creator can submit */
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER);
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_R_DISABLED);
if (ret)
error(1, ret, "ring init (2) %i", ret);
if (!fork_t()) {
+ io_uring_enable_rings(&ring);
ret = try_submit(&ring);
if (ret)
- fprintf(stderr, "not owner child could submit %i\n", ret);
+ fprintf(stderr, "2: not owner child could submit %i\n", ret);
return !!ret;
}
wait_child_t();
io_uring_queue_exit(&ring);
+ /* test that only the first enabler can submit */
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_R_DISABLED);
+ if (ret)
+ error(1, ret, "ring init (3) %i", ret);
+
+ io_uring_enable_rings(&ring);
+ if (!fork_t()) {
+ ret = try_submit(&ring);
+ if (ret != -EEXIST)
+ fprintf(stderr, "3: not owner child could submit %i\n", ret);
+ return ret != -EEXIST;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+
/* test that anyone can submit to a SQPOLL|SINGLE_ISSUER ring */
ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_SQPOLL);
if (ret)
- error(1, ret, "ring init (3) %i", ret);
+ error(1, ret, "ring init (4) %i", ret);
ret = try_submit(&ring);
if (ret) {
@@ -139,13 +157,13 @@ int main(int argc, char *argv[])
/* test that IORING_ENTER_REGISTERED_RING doesn't break anything */
ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER);
if (ret)
- error(1, ret, "ring init (4) %i", ret);
+ error(1, ret, "ring init (5) %i", ret);
if (!fork_t()) {
ret = try_submit(&ring);
- if (ret)
- fprintf(stderr, "not owner child could submit %i\n", ret);
- return !!ret;
+ if (ret != -EEXIST)
+ fprintf(stderr, "4: not owner child could submit %i\n", ret);
+ return ret != -EEXIST;
}
wait_child_t();
io_uring_queue_exit(&ring);
IORING_SETUP_SINGLE_ISSUER now registers the task at ring creation, so update the tests and documentation to reflect this. Signed-off-by: Dylan Yudaken <dylany@fb.com> --- man/io_uring_setup.2 | 18 +++++++++--------- test/defer-taskrun.c | 8 ++++++-- test/single-issuer.c | 34 ++++++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 19 deletions(-)