Skip to contentSkip to conversation

io_uring: submission path cleanup

PATCH Superseded Pavel Begunkov 3 participants 21 messages Dec 16, 2019 – Dec 17, 2019

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover io_uring: submission path cleanup

Pretty straighforward cleanups. The last patch saves the exact behaviour, but do link enqueuing from a more suitable place.

Pavel Begunkov (3):
io_uring: rename prev to head
io_uring: move trace_submit_sqe into submit_sqe
io_uring: move *queue_link_head() from common path

 fs/io_uring.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)
Signature · 1 line
2.24.0
#1 lore raw

PATCH 1/3 io_uring: rename prev to head

Calling "prev" a head of a link is a bit misleading. Rename it

---
 fs/io_uring.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
fs/io_uring.c +5 -5
index 339b57aac5ca..96ddfc52cb0f 100644
@@ -3399,10 +3399,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	 * conditions are true (normal request), then just queue it.
 	 */
 	if (*link) {
-		struct io_kiocb *prev = *link;
+		struct io_kiocb *head = *link;
 
 		if (req->sqe->flags & IOSQE_IO_DRAIN)
-			(*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
+			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
 		if (req->sqe->flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
@@ -3415,11 +3415,11 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		ret = io_req_defer_prep(req);
 		if (ret) {
 			/* fail even hard links since we don't submit */
-			prev->flags |= REQ_F_FAIL_LINK;
+			head->flags |= REQ_F_FAIL_LINK;
 			goto err_req;
 		}
-		trace_io_uring_link(ctx, req, prev);
-		list_add_tail(&req->link_list, &prev->link_list);
+		trace_io_uring_link(ctx, req, head);
+		list_add_tail(&req->link_list, &head->link_list);
 	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
 		req->flags |= REQ_F_LINK;
 		if (req->sqe->flags & IOSQE_IO_HARDLINK)
Signature · 1 line
2.24.0
#2 lore raw

PATCH 2/3 io_uring: move trace_submit_sqe into submit_sqe

In reply to Pavel Begunkov

For better locality, call trace_io_uring_submit_sqe() from submit_sqe() rather than io_submit_sqes(). No functional change.

---
 fs/io_uring.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
fs/io_uring.c +2 -3
index 96ddfc52cb0f..bac9e711e38d 100644
@@ -3375,7 +3375,8 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	struct io_ring_ctx *ctx = req->ctx;
 	int ret;
 
-	req->user_data = req->sqe->user_data;
+	req->user_data = READ_ONCE(req->sqe->user_data);
+	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
 	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
@@ -3569,8 +3570,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		req->has_user = *mm != NULL;
 		req->in_async = async;
 		req->needs_fixed_file = async;
-		trace_io_uring_submit_sqe(ctx, req->sqe->user_data,
-					  true, async);
 		if (!io_submit_sqe(req, statep, &link))
 			break;
 		/*
Signature · 1 line
2.24.0
#3 lore raw

PATCH 3/3 io_uring: move *queue_link_head() from common path

In reply to Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +14 -18
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {
+			io_queue_link_head(head);
+			*link = NULL;
+		}
+	} else if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
 		req->flags |= REQ_F_LINK;
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		INIT_LIST_HEAD(&req->link_list);
@@ -3540,10 +3548,8 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 	}
 
 	for (i = 0; i < nr; i++) {
-		struct io_kiocb *req;
-		unsigned int sqe_flags;
+		struct io_kiocb *req = io_get_req(ctx, statep);
 
-		req = io_get_req(ctx, statep);
 		if (unlikely(!req)) {
 			if (!submitted)
 				submitted = -EAGAIN;
@@ -3563,8 +3569,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		}
 
 		submitted++;
-		sqe_flags = req->sqe->flags;
-
 		req->ring_file = ring_file;
 		req->ring_fd = ring_fd;
 		req->has_user = *mm != NULL;
@@ -3572,14 +3576,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		req->needs_fixed_file = async;
 		if (!io_submit_sqe(req, statep, &link))
 			break;
-		/*
-		 * If previous wasn't linked and we have a linked command,
-		 * that's the end of the chain. Submit the previous link.
-		 */
-		if (!(sqe_flags & IOSQE_IO_LINK) && link) {
-			io_queue_link_head(link);
-			link = NULL;
-		}
 	}
 
 	if (link)
Signature · 1 line
2.24.0
#4 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Quoted 47 lines
fs/io_uring.c +6 -14
+			io_queue_link_head(head);
+			*link = NULL;
+		}
+	} else if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
 		req->flags |= REQ_F_LINK;
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		INIT_LIST_HEAD(&req->link_list);
@@ -3540,10 +3548,8 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 	}
 
 	for (i = 0; i < nr; i++) {
-		struct io_kiocb *req;
-		unsigned int sqe_flags;
+		struct io_kiocb *req = io_get_req(ctx, statep);
 
-		req = io_get_req(ctx, statep);
 		if (unlikely(!req)) {
 			if (!submitted)
 				submitted = -EAGAIN;
@@ -3563,8 +3569,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		}
 
 		submitted++;
-		sqe_flags = req->sqe->flags;
-
 		req->ring_file = ring_file;
 		req->ring_fd = ring_fd;
 		req->has_user = *mm != NULL;
@@ -3572,14 +3576,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		req->needs_fixed_file = async;
 		if (!io_submit_sqe(req, statep, &link))
 			break;
-		/*
-		 * If previous wasn't linked and we have a linked command,
-		 * that's the end of the chain. Submit the previous link.
-		 */
-		if (!(sqe_flags & IOSQE_IO_LINK) && link) {
-			io_queue_link_head(link);
-			link = NULL;
-		}
 	}
 
 	if (link)
Signature · 1 line
Pavel Begunkov
maintainer
#5 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

What do you think?

Signature · 1 line
Jens Axboe
maintainer
#6 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 67 lines · Jens Axboe
Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

Quoted 7 lines

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Signature · 1 line
Jens Axboe
#7 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 70 lines · Jens Axboe
Quoted 69 lines · Jens Axboe
Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Quoted 9 lines
Quoted 5 lines

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Signature · 1 line
Pavel Begunkov
maintainer
#8 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 75 lines · Pavel Begunkov
Quoted 74 lines · Jens Axboe
Quoted 73 lines · Jens Axboe
Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

I haven't.

Quoted 3 lines

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Not sure I follow what you're saying here, can you elaborate?

Signature · 1 line
Jens Axboe
#9 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 83 lines · Jens Axboe
Quoted 75 lines · Pavel Begunkov
Quoted 74 lines · Jens Axboe
Quoted 73 lines · Jens Axboe
Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

Quoted 8 lines

I haven't.

Quoted 3 lines

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Not sure I follow what you're saying here, can you elaborate?

Sure

#define IOSQE_IO_LINK (1U << 2) /* links next sqe */
#define IOSQE_IO_HARDLINK (1U << 3) /* like LINK, but stronger */

That's 2 consequent bits, so 4 states:
0,0 -> not a link
1,0 -> common link
0,1 -> hard link
1,1 -> reserved, space for another link-quirk type

But that would require additional check, i.e.

if (flags&(LINK|HARDLINK) == (LINK|HARDLINK)) ...

Signature · 1 line
Pavel Begunkov
maintainer
#10 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 104 lines · Pavel Begunkov
Quoted 83 lines · Jens Axboe
Quoted 75 lines · Pavel Begunkov
Quoted 74 lines · Jens Axboe
Quoted 73 lines · Jens Axboe
Quoted 56 lines · Pavel Begunkov
Quoted 43 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +5 -3
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 7 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

Quoted 8 lines

I haven't.

Quoted 3 lines

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Not sure I follow what you're saying here, can you elaborate?

Sure

#define IOSQE_IO_LINK (1U << 2) /* links next sqe */
#define IOSQE_IO_HARDLINK (1U << 3) /* like LINK, but stronger */

That's 2 consequent bits, so 4 states:
0,0 -> not a link
1,0 -> common link
0,1 -> hard link
1,1 -> reserved, space for another link-quirk type

Quoted 3 lines

But that would require additional check, i.e.

if (flags&(LINK|HARDLINK) == (LINK|HARDLINK)) ...

Ah, I see. In terms of usability, I think it makes more sense to have

IOSQE_LINK | IOSQE_HARDLINK

be the same as just IOSQE_LINK. It would be nice to retain that for something else, but I think it'll be more confusing to users.

Signature · 1 line
Jens Axboe
#11 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 61 lines · Jens Axboe
Quoted 60 lines · Pavel Begunkov
Quoted 37 lines · Jens Axboe
Quoted 29 lines · Pavel Begunkov
Quoted 28 lines · Jens Axboe
Quoted 27 lines · Jens Axboe
Quoted 10 lines · Pavel Begunkov

Review

Quoted 4 lines · Pavel Begunkov
(quoted diff) +3 -1
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

Quoted 8 lines

I haven't.

Quoted 3 lines

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Not sure I follow what you're saying here, can you elaborate?

Sure

#define IOSQE_IO_LINK (1U << 2) /* links next sqe */
#define IOSQE_IO_HARDLINK (1U << 3) /* like LINK, but stronger */

That's 2 consequent bits, so 4 states:
0,0 -> not a link
1,0 -> common link
0,1 -> hard link
1,1 -> reserved, space for another link-quirk type

But that would require additional check, i.e.

Quoted 7 lines
Quoted 1 line

if (flags&(LINK|HARDLINK) == (LINK|HARDLINK)) ...

Ah, I see. In terms of usability, I think it makes more sense to have

IOSQE_LINK | IOSQE_HARDLINK

be the same as just IOSQE_LINK. It would be nice to retain that for

Probably, you meant it to be the same as __IOSQE_HARDLINK__

Quoted 2 lines

something else, but I think it'll be more confusing to users.

Yeah, and it's easier for something like:

sqe->flags |= IOSQE_LINK;
[some code]
if (timer_or_whatever())

	sqe->flags |= IOSQE_HARDLINK;
Signature · 1 line
Pavel Begunkov
maintainer
#12 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 75 lines · Pavel Begunkov
Quoted 61 lines · Jens Axboe
Quoted 60 lines · Pavel Begunkov
Quoted 37 lines · Jens Axboe
Quoted 29 lines · Pavel Begunkov
Quoted 28 lines · Jens Axboe
Quoted 27 lines · Jens Axboe
Quoted 10 lines · Pavel Begunkov

Review

Quoted 4 lines · Pavel Begunkov
(quoted diff) +3 -1
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

This looks suspicious (as well as in the current revision). Returning back to my questions a few days ago can sqe->flags have IOSQE_IO_HARDLINK, but not IOSQE_IO_LINK? I don't find any check.

Quoted 2 lines

In other words, should it be as follows?
!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))

Yeah, I think that should check for both. I'm fine with either approach in general:

- IOSQE_IO_HARDLINK must have IOSQE_IO_LINK set

or

- IOSQE_IO_HARDLINK implies IOSQE_IO_LINK

Seems like the former is easier to verify in terms of functionality, since we can rest easy if we check this early and -EINVAL if that isn't the case.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

What do you think?

If you agree, want to send in a patch for that for 5.5? Then I can respin for-5.6/io_uring on top of that, and we can apply your cleanups there.

Yes, that's the idea. Already got a patch, if you haven't done it yet.

Quoted 8 lines

I haven't.

Quoted 3 lines

Just was thinking, whether to add a check for not setting both flags at the same moment in the "imply" case. Would give us 1 state in 2 bits for future use.

Not sure I follow what you're saying here, can you elaborate?

Sure

#define IOSQE_IO_LINK (1U << 2) /* links next sqe */
#define IOSQE_IO_HARDLINK (1U << 3) /* like LINK, but stronger */

That's 2 consequent bits, so 4 states:
0,0 -> not a link
1,0 -> common link
0,1 -> hard link
1,1 -> reserved, space for another link-quirk type

But that would require additional check, i.e.

Quoted 7 lines
Quoted 1 line

if (flags&(LINK|HARDLINK) == (LINK|HARDLINK)) ...

Ah, I see. In terms of usability, I think it makes more sense to have

IOSQE_LINK | IOSQE_HARDLINK

be the same as just IOSQE_LINK. It would be nice to retain that for

Probably, you meant it to be the same as __IOSQE_HARDLINK__

Quoted 2 lines

something else, but I think it'll be more confusing to users.

Quoted 6 lines

Yeah, and it's easier for something like:

sqe->flags |= IOSQE_LINK;
[some code]
if (timer_or_whatever())

	sqe->flags |= IOSQE_HARDLINK;

Precisely. So let's keep it as-is.

Signature · 1 line
Jens Axboe
#13 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

In reply to Pavel Begunkov
Quoted 14 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +0 -0
index bac9e711e38d..a880ed1409cb 100644

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3379

Quoted 7 lines
fs/io_uring.c +2 -0
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);

Just out of curiosity, why READ_ONCE it necessary here? I though, that since io_submit_sqes happens within a uring_lock, it's already protected. Do I miss something?

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 8 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

Yes, as you mentioned in the previous email, it seems correct that if IOSQE_IO_HARDLINK imply IOSQE_IO_LINK, then here we need to check both.

Quoted 4 lines
fs/io_uring.c +4 -0
+			io_queue_link_head(head);
+			*link = NULL;
+		}
+	} else if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
#14 lore raw

Re: [PATCH 3/3] io_uring: move *queue_link_head() from common path

Quoted 20 lines · Dmitry Dolgov
Quoted 20 lines · Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +1 -0
index bac9e711e38d..a880ed1409cb 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
Quoted 6 lines

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3379

Quoted 1 line
fs/io_uring.c +1 -0
+	sqe_flags = READ_ONCE(req->sqe->flags);

Just out of curiosity, why READ_ONCE it necessary here? I though, that since io_submit_sqes happens within a uring_lock, it's already protected. Do I miss something?

SQEs are rw-shared with the userspace, that's it. Probably, there are more places where proper READ_ONCE() annotations have been lost.

Quoted 16 lines

fs/io_uring.c static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, · line 3428

Quoted 8 lines
fs/io_uring.c +3 -1
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & IOSQE_IO_LINK)) {

Yes, as you mentioned in the previous email, it seems correct that if IOSQE_IO_HARDLINK imply IOSQE_IO_LINK, then here we need to check both.

Quoted 4 lines
fs/io_uring.c +4 -0
+			io_queue_link_head(head);
+			*link = NULL;
+		}
+	} else if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
Signature · 1 line
Pavel Begunkov
maintainer
#15 lore raw
In reply to Pavel Begunkov
Quoted 10 lines · Pavel Begunkov

Pretty straighforward cleanups. The last patch saves the exact behaviour, but do link enqueuing from a more suitable place.

Pavel Begunkov (3):
io_uring: rename prev to head
io_uring: move trace_submit_sqe into submit_sqe
io_uring: move *queue_link_head() from common path

 fs/io_uring.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

Can you respin this on top of the hardlink patch?

Signature · 1 line
Jens Axboe
#16 lore raw

v2 cover io_uring: submission path cleanup

In reply to Pavel Begunkov

Pretty straighforward cleanups. The last patch saves the exact behaviour, but do link enqueuing from a more suitable place.

v2: rebase

Pavel Begunkov (3):
io_uring: rename prev to head
io_uring: move trace_submit_sqe into submit_sqe
io_uring: move *queue_link_head() from common path

 fs/io_uring.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)
Signature · 1 line
2.24.0
#17 lore raw

PATCH 1/3 io_uring: rename prev to head

Calling "prev" a head of a link is a bit misleading. Rename it

---
 fs/io_uring.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
fs/io_uring.c +5 -5
index eb6d897ea087..e8ce224dc82c 100644
@@ -3399,10 +3399,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	 * conditions are true (normal request), then just queue it.
 	 */
 	if (*link) {
-		struct io_kiocb *prev = *link;
+		struct io_kiocb *head = *link;
 
 		if (req->sqe->flags & IOSQE_IO_DRAIN)
-			(*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
+			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
 		if (req->sqe->flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
@@ -3415,11 +3415,11 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		ret = io_req_defer_prep(req);
 		if (ret) {
 			/* fail even hard links since we don't submit */
-			prev->flags |= REQ_F_FAIL_LINK;
+			head->flags |= REQ_F_FAIL_LINK;
 			goto err_req;
 		}
-		trace_io_uring_link(ctx, req, prev);
-		list_add_tail(&req->link_list, &prev->link_list);
+		trace_io_uring_link(ctx, req, head);
+		list_add_tail(&req->link_list, &head->link_list);
 	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
 		req->flags |= REQ_F_LINK;
 		if (req->sqe->flags & IOSQE_IO_HARDLINK)
Signature · 1 line
2.24.0
#18 lore raw

PATCH v2 3/3 io_uring: move *queue_link_head() from common path

In reply to Pavel Begunkov

Move io_queue_link_head() to links handling code in io_submit_sqe(), so it wouldn't need extra checks and would have better data locality.

---
 fs/io_uring.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)
fs/io_uring.c +14 -18
index ee461bcd3121..8ab96ca0ad28 100644
@@ -3373,13 +3373,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 			  struct io_kiocb **link)
 {
 	struct io_ring_ctx *ctx = req->ctx;
+	unsigned int sqe_flags;
 	int ret;
 
+	sqe_flags = READ_ONCE(req->sqe->flags);
 	req->user_data = READ_ONCE(req->sqe->user_data);
 	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
-	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
+	if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
 		ret = -EINVAL;
 		goto err_req;
 	}
@@ -3402,10 +3404,10 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	if (*link) {
 		struct io_kiocb *head = *link;
 
-		if (req->sqe->flags & IOSQE_IO_DRAIN)
+		if (sqe_flags & IOSQE_IO_DRAIN)
 			head->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		if (io_alloc_async_ctx(req)) {
@@ -3421,9 +3423,15 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 		}
 		trace_io_uring_link(ctx, req, head);
 		list_add_tail(&req->link_list, &head->link_list);
-	} else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
+
+		/* last request of a link, enqueue the link */
+		if (!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))) {
+			io_queue_link_head(head);
+			*link = NULL;
+		}
+	} else if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
 		req->flags |= REQ_F_LINK;
-		if (req->sqe->flags & IOSQE_IO_HARDLINK)
+		if (sqe_flags & IOSQE_IO_HARDLINK)
 			req->flags |= REQ_F_HARDLINK;
 
 		INIT_LIST_HEAD(&req->link_list);
@@ -3540,10 +3548,8 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 	}
 
 	for (i = 0; i < nr; i++) {
-		struct io_kiocb *req;
-		unsigned int sqe_flags;
+		struct io_kiocb *req = io_get_req(ctx, statep);
 
-		req = io_get_req(ctx, statep);
 		if (unlikely(!req)) {
 			if (!submitted)
 				submitted = -EAGAIN;
@@ -3563,8 +3569,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		}
 
 		submitted++;
-		sqe_flags = req->sqe->flags;
-
 		req->ring_file = ring_file;
 		req->ring_fd = ring_fd;
 		req->has_user = *mm != NULL;
@@ -3572,14 +3576,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		req->needs_fixed_file = async;
 		if (!io_submit_sqe(req, statep, &link))
 			break;
-		/*
-		 * If previous wasn't linked and we have a linked command,
-		 * that's the end of the chain. Submit the previous link.
-		 */
-		if (!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) && link) {
-			io_queue_link_head(link);
-			link = NULL;
-		}
 	}
 
 	if (link)
Signature · 1 line
2.24.0
#19 lore raw

PATCH 2/3 io_uring: move trace_submit_sqe into submit_sqe

In reply to Pavel Begunkov

For better locality, call trace_io_uring_submit_sqe() from submit_sqe() rather than io_submit_sqes(). No functional change.

---
 fs/io_uring.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
fs/io_uring.c +2 -3
index e8ce224dc82c..ee461bcd3121 100644
@@ -3375,7 +3375,8 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 	struct io_ring_ctx *ctx = req->ctx;
 	int ret;
 
-	req->user_data = req->sqe->user_data;
+	req->user_data = READ_ONCE(req->sqe->user_data);
+	trace_io_uring_submit_sqe(ctx, req->user_data, true, req->in_async);
 
 	/* enforce forwards compatibility on users */
 	if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) {
@@ -3569,8 +3570,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
 		req->has_user = *mm != NULL;
 		req->in_async = async;
 		req->needs_fixed_file = async;
-		trace_io_uring_submit_sqe(ctx, req->sqe->user_data,
-					  true, async);
 		if (!io_submit_sqe(req, statep, &link))
 			break;
 		/*
Signature · 1 line
2.24.0
maintainer Applied
#20 lore raw
In reply to Pavel Begunkov
Quoted 5 lines · Pavel Begunkov

Pretty straighforward cleanups. The last patch saves the exact behaviour, but do link enqueuing from a more suitable place.

v2: rebase

Quoted 7 lines

Pavel Begunkov (3):
io_uring: rename prev to head
io_uring: move trace_submit_sqe into submit_sqe
io_uring: move *queue_link_head() from common path

 fs/io_uring.c | 47 +++++++++++++++++++++--------------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

Applied, thanks.

Signature · 1 line
Jens Axboe