io_uring fixes for 5.5-rc3
Jens Axboe
maintainerPATCH v2 1/1 io_uring fixes for 5.5-rc3
Hot on the heels of v1, here's the updated set. The main difference here is ensuring that ->opcode is stable for deferred (async or just linked) execution. Doesn't help that we fully prep commands, if the reissue ends up reading sqe fields again to figure out which command it is.
I think this improves it all around, and it makes me more comfortable with the persistent state. There's three patches that deal with poll add/remove, cancel async, and timeout remove. Those are the three commands we still missed that need to retain state across a deferral, and then a patch that stuffs sqe->opcode in a io_kiocb hole, and ensures we assign ->opcode and ->user_data when we retrieve the SQE.
Lastly, a patch that adds a warning about new commands that don't have a prep handler. We need that for ANY command that reads sqe fields, which is all of them obviously except NOP.
I dropped Pavel's submit-and-wait patch for now, hoping he'll send a new one tomorrow and I'll get that added
fs/io-wq.c | 2 +- fs/io_uring.c | 692 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 470 insertions(+), 224 deletions(-)
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH 1/11 io_uring: fix stale comment and a few typos
From: Brian Gianforcaro <[email protected]>
- Fix a few typos found while reading the code.
- Fix stale io_get_sqring comment referencing s->sqe, the 's' parameter was renamed to 'req', but the comment still holds.
--- fs/io-wq.c | 2 +- fs/io_uring.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-)
fs/io-wq.c +1 -1
@@ -948,7 +948,7 @@ static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe,/* * Now check if a free (going busy) or busy worker has the work * currently running. If we find it there, we'll return CANCEL_RUNNING -* as an indication that we attempte to signal cancellation. The +* as an indication that we attempt to signal cancellation. The * completion will run normally in this case. */ rcu_read_lock ();
fs/io_uring.c +4 -4
@@ -1178,7 +1178,7 @@ static int io_do_iopoll(struct io_ring_ctx *ctx, unsigned int *nr_events, }/* -* Poll for a mininum of 'min' events. Note that if min == 0 we consider that a +* Poll for a minimum of 'min' events. Note that if min == 0 we consider that a * non-spinning poll check - we'll still enter the driver poll loop, but only * as a non-spinning completion check. */
@@ -2573,7 +2573,7 @@ static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer)/* * Adjust the reqs sequence before the current one because it -* will consume a slot in the cq_ring and the the cq_tail +* will consume a slot in the cq_ring and the cq_tail * pointer will be increased, otherwise other timeout reqs may * return in advance without waiting for enough wait_nr. */
@@ -3430,7 +3430,7 @@ static void io_commit_sqring(struct io_ring_ctx *ctx) }/* -* Fetch an sqe, if one is available. Note that s->sqe will point to memory +* Fetch an sqe, if one is available. Note that req->sqe will point to memory * that is mapped by userspace. This means that care needs to be taken to * ensure that reads are stable, as we cannot rely on userspace always * being a good citizen. If members of the sqe are validated and then later
@@ -3694,7 +3694,7 @@ static inline bool io_should_wake(struct io_wait_queue *iowq, bool noflush)struct io_ring_ctx *ctx = iowq->ctx;/* -* Wake up if we have enough events, or if a timeout occured since we +* Wake up if we have enough events, or if a timeout occurred since we * started waiting. For timeouts, we always want to return to userspace, * regardless of event count. */
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 2/11 io_uring: fix sporadic -EFAULT from IORING_OP_RECVMSG
In reply to Jens AxboeIf we have to punt the recvmsg to async context, we copy all the context. But since the iovec used can be either on-stack (if small) or dynamically allocated, if it's on-stack, then we need to ensure we reset the iov pointer. If we don't, then we're reusing old stack data, and that can lead to -EFAULTs if things get overwritten.
Ensure we retain the right pointers for the iov, and free it as well if we end up having to go beyond UIO_FASTIOV number of vectors.
--- fs/io_uring.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-)
fs/io_uring.c +26 -14
@@ -2041,6 +2041,7 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct io_kiocb **nxt, bool force_nonblock) {#if defined(CONFIG_NET ) +struct io_async_msghdr *kmsg =NULL ;struct socket *sock;int ret;
@@ -2051,7 +2052,6 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,if (sock) {struct io_async_ctx io, *copy;struct sockaddr_storage addr; -struct msghdr *kmsg;unsigned flags; flags =READ_ONCE (sqe->msg_flags);
@@ -2061,17 +2061,21 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, flags |=MSG_DONTWAIT ;if (req->io) { - kmsg = &req->io->msg.msg; - kmsg->msg_name = &addr; + kmsg = &req->io->msg; + kmsg->msg.msg_name = &addr; +/* if iov is set, it's allocated already */ +if (!kmsg->iov) + kmsg->iov = kmsg->fast_iov; + kmsg->msg.msg_iter.iov = kmsg->iov; }else { - kmsg = &io.msg.msg; - kmsg->msg_name = &addr; + kmsg = &io.msg; + kmsg->msg.msg_name = &addr; ret =io_sendmsg_prep (req, &io);if (ret) goto out; } - ret =__sys_sendmsg_sock (sock, kmsg, flags); + ret =__sys_sendmsg_sock (sock, &kmsg->msg, flags);if (force_nonblock && ret == -EAGAIN ) { copy =kmalloc (sizeof (*copy),GFP_KERNEL );if (!copy) {
@@ -2082,13 +2086,15 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, req->io = copy;memcpy (&req->io->sqe, req->sqe,sizeof (*req->sqe)); req->sqe = &req->io->sqe; -return ret; +return -EAGAIN ; }if (ret == -ERESTARTSYS ) ret = -EINTR ; } out: +if (kmsg && kmsg->iov != kmsg->fast_iov) +kfree (kmsg->iov);io_cqring_add_event (req, ret);if (ret <0 )req_set_fail_links (req);
@@ -2120,6 +2126,7 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct io_kiocb **nxt, bool force_nonblock) {#if defined(CONFIG_NET ) +struct io_async_msghdr *kmsg =NULL ;struct socket *sock;int ret;
@@ -2131,7 +2138,6 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,struct user_msghdr __user *msg;struct io_async_ctx io, *copy;struct sockaddr_storage addr; -struct msghdr *kmsg;unsigned flags; flags =READ_ONCE (sqe->msg_flags);
@@ -2143,17 +2149,21 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, msg = (struct user_msghdr __user *) (unsigned long )READ_ONCE (sqe->addr);if (req->io) { - kmsg = &req->io->msg.msg; - kmsg->msg_name = &addr; + kmsg = &req->io->msg; + kmsg->msg.msg_name = &addr; +/* if iov is set, it's allocated already */ +if (!kmsg->iov) + kmsg->iov = kmsg->fast_iov; + kmsg->msg.msg_iter.iov = kmsg->iov; }else { - kmsg = &io.msg.msg; - kmsg->msg_name = &addr; + kmsg = &io.msg; + kmsg->msg.msg_name = &addr; ret =io_recvmsg_prep (req, &io);if (ret) goto out; } - ret =__sys_recvmsg_sock (sock, kmsg, msg, io.msg.uaddr, flags); + ret =__sys_recvmsg_sock (sock, &kmsg->msg, msg, kmsg->uaddr, flags);if (force_nonblock && ret == -EAGAIN ) { copy =kmalloc (sizeof (*copy),GFP_KERNEL );if (!copy) {
@@ -2164,13 +2174,15 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, req->io = copy;memcpy (&req->io->sqe, req->sqe,sizeof (*req->sqe)); req->sqe = &req->io->sqe; -return ret; +return -EAGAIN ; }if (ret == -ERESTARTSYS ) ret = -EINTR ; } out: +if (kmsg && kmsg->iov != kmsg->fast_iov) +kfree (kmsg->iov);io_cqring_add_event (req, ret);if (ret <0 )req_set_fail_links (req);
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 3/11 io_uring: fix pre-prepped issue with force_nonblock == true
In reply to Jens AxboeSome of these code paths assume that any force_nonblock == true issue is not prepped, but that's not true if we did prep as part of link setup earlier. Check if we already have an async context allocate before setting up a new one.
Cleanup the async context setup in general, we have a lot of duplicated code there.
Finally, add an async context ->free_req() to handle the case where a request is cancelled. We still need to free private data outside of req->io for that case. To avoid the indirect free call for the fast path, do the free manually and clear ->free_req() for that case.
--- fs/io_uring.c | 168 ++++++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 74 deletions(-)
fs/io_uring.c +94 -74
@@ -324,6 +324,7 @@ struct io_async_rw {struct io_async_ctx {struct io_uring_sqe sqe; +void (*free_req)(struct io_kiocb *);union {struct io_async_rw rw;struct io_async_msghdr msg;
@@ -879,8 +880,11 @@ static void __io_free_req(struct io_kiocb *req) {struct io_ring_ctx *ctx = req->ctx; -if (req->io) +if (req->io) { +if (req->io->free_req) + req->io->free_req (req);kfree (req->io); + }if (req->file && !(req->flags &REQ_F_FIXED_FILE ))fput (req->file);if (req->flags &REQ_F_INFLIGHT ) {
@@ -1701,7 +1705,7 @@ static ssize_t loop_rw_iter(int rw, struct file *file, struct kiocb *kiocb,return ret; } -static void io_req_map_io (struct io_kiocb *req,ssize_t io_size, +static void io_req_map_rw (struct io_kiocb *req,ssize_t io_size,struct iovec *iovec,struct iovec *fast_iov,struct iov_iter *iter) {
@@ -1715,19 +1719,35 @@ static void io_req_map_io(struct io_kiocb *req, ssize_t io_size, } } -static int io_setup_async_io (struct io_kiocb *req,ssize_t io_size, -struct iovec *iovec,struct iovec *fast_iov, -struct iov_iter *iter) +static int io_alloc_async_ctx (struct io_kiocb *req) { req->io =kmalloc (sizeof (*req->io),GFP_KERNEL );if (req->io) { -io_req_map_io (req, io_size, iovec, fast_iov, iter);memcpy (&req->io->sqe, req->sqe,sizeof (req->io->sqe)); req->sqe = &req->io->sqe; + req->io->free_req =NULL ;return 0 ; } -return -ENOMEM ; +return 1 ; +} + +static void io_rw_free_req (struct io_kiocb *req) +{ +if (req->io->rw.iov != req->io->rw.fast_iov) +kfree (req->io->rw.iov); +} + +static int io_setup_async_rw (struct io_kiocb *req,ssize_t io_size, +struct iovec *iovec,struct iovec *fast_iov, +struct iov_iter *iter) +{ +if (!req->io &&io_alloc_async_ctx (req)) +return -ENOMEM ; + +io_req_map_rw (req, io_size, iovec, fast_iov, iter); + req->io->free_req = io_rw_free_req; +return 0 ; } static int io_read_prep(struct io_kiocb *req, struct iovec **iovec,
@@ -1806,7 +1826,7 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt,kiocb_done (kiocb, ret2, nxt, req->in_async); }else { copy_iov: - ret =io_setup_async_io (req, io_size, iovec, + ret =io_setup_async_rw (req, io_size, iovec, inline_vecs, &iter);if (ret) goto out_free;
@@ -1815,6 +1835,8 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, } out_free:kfree (iovec); +if (req->io) + req->io->free_req =NULL ;return ret; }
@@ -1900,7 +1922,7 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt,kiocb_done (kiocb, ret2, nxt, req->in_async); }else { copy_iov: - ret =io_setup_async_io (req, io_size, iovec, + ret =io_setup_async_rw (req, io_size, iovec, inline_vecs, &iter);if (ret) goto out_free;
@@ -1909,6 +1931,8 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, } out_free:kfree (iovec); +if (req->io) + req->io->free_req =NULL ;return ret; }
@@ -2021,6 +2045,12 @@ static int io_sync_file_range(struct io_kiocb *req,return 0 ; } +static void io_sendrecv_msg_free (struct io_kiocb *req) +{ +if (req->io->msg.iov != req->io->msg.fast_iov) +kfree (req->io->msg.iov); +} +static int io_sendmsg_prep (struct io_kiocb *req,struct io_async_ctx *io) {#if defined(CONFIG_NET )
@@ -2050,7 +2080,7 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, sock =sock_from_file (req->file, &ret);if (sock) { -struct io_async_ctx io, *copy; +struct io_async_ctx io;struct sockaddr_storage addr;unsigned flags;
@@ -2077,15 +2107,12 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret =__sys_sendmsg_sock (sock, &kmsg->msg, flags);if (force_nonblock && ret == -EAGAIN ) { - copy =kmalloc (sizeof (*copy),GFP_KERNEL ); -if (!copy) { - ret = -ENOMEM ; - goto out; - } -memcpy (©->msg, &io.msg,sizeof (copy->msg)); - req->io = copy; -memcpy (&req->io->sqe, req->sqe,sizeof (*req->sqe)); - req->sqe = &req->io->sqe; +if (req->io) +return -EAGAIN ; +if (io_alloc_async_ctx (req)) +return -ENOMEM ; +memcpy (&req->io->msg, &io.msg,sizeof (io.msg)); + req->io->free_req = io_sendrecv_msg_free;return -EAGAIN ; }if (ret == -ERESTARTSYS )
@@ -2095,6 +2122,8 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, out:if (kmsg && kmsg->iov != kmsg->fast_iov)kfree (kmsg->iov); +if (req->io) + req->io->free_req =NULL ;io_cqring_add_event (req, ret);if (ret <0 )req_set_fail_links (req);
@@ -2136,7 +2165,7 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, sock =sock_from_file (req->file, &ret);if (sock) {struct user_msghdr __user *msg; -struct io_async_ctx io, *copy; +struct io_async_ctx io;struct sockaddr_storage addr;unsigned flags;
@@ -2165,15 +2194,12 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret =__sys_recvmsg_sock (sock, &kmsg->msg, msg, kmsg->uaddr, flags);if (force_nonblock && ret == -EAGAIN ) { - copy =kmalloc (sizeof (*copy),GFP_KERNEL ); -if (!copy) { - ret = -ENOMEM ; - goto out; - } -memcpy (copy, &io,sizeof (*copy)); - req->io = copy; -memcpy (&req->io->sqe, req->sqe,sizeof (*req->sqe)); - req->sqe = &req->io->sqe; +if (req->io) +return -EAGAIN ; +if (io_alloc_async_ctx (req)) +return -ENOMEM ; +memcpy (&req->io->msg, &io.msg,sizeof (io.msg)); + req->io->free_req = io_sendrecv_msg_free;return -EAGAIN ; }if (ret == -ERESTARTSYS )
@@ -2183,6 +2209,8 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, out:if (kmsg && kmsg->iov != kmsg->fast_iov)kfree (kmsg->iov); +if (req->io) + req->io->free_req =NULL ;io_cqring_add_event (req, ret);if (ret <0 )req_set_fail_links (req);
@@ -2272,15 +2300,13 @@ static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret =__sys_connect_file (req->file, &io->connect.address, addr_len, file_flags);if ((ret == -EAGAIN || ret == -EINPROGRESS ) && force_nonblock) { - io =kmalloc (sizeof (*io),GFP_KERNEL ); -if (!io) { +if (req->io) +return -EAGAIN ; +if (io_alloc_async_ctx (req)) { ret = -ENOMEM ; goto out; } -memcpy (&io->connect, &__io.connect,sizeof (io->connect)); - req->io = io; -memcpy (&io->sqe, req->sqe,sizeof (*req->sqe)); - req->sqe = &io->sqe; +memcpy (&req->io->connect, &__io.connect,sizeof (__io.connect));return -EAGAIN ; }if (ret == -ERESTARTSYS )
@@ -2511,7 +2537,6 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe,if (!poll->file)return -EBADF ; - req->io =NULL ;INIT_IO_WORK (&req->work, io_poll_complete_work); events =READ_ONCE (sqe->poll_events); poll->events =demangle_poll (events) |EPOLLERR |EPOLLHUP ;
@@ -2692,7 +2717,6 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, data->mode =HRTIMER_MODE_REL ;hrtimer_init (&data->timer,CLOCK_MONOTONIC , data->mode); - req->io = io;return 0 ; }
@@ -2701,22 +2725,16 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)unsigned count;struct io_ring_ctx *ctx = req->ctx;struct io_timeout_data *data; -struct io_async_ctx *io;struct list_head *entry;unsigned span =0 ; +int ret; - io = req->io; -if (!io) { -int ret; - - io =kmalloc (sizeof (*io),GFP_KERNEL ); -if (!io) +if (!req->io) { +if (io_alloc_async_ctx (req))return -ENOMEM ; - ret =io_timeout_prep (req, io, false); -if (ret) { -kfree (io); + ret =io_timeout_prep (req, req->io, false); +if (ret)return ret; - } } data = &req->io->timeout;
@@ -2858,23 +2876,35 @@ static int io_async_cancel(struct io_kiocb *req, const struct io_uring_sqe *sqe,return 0 ; } -static int io_req_defer_prep (struct io_kiocb *req,struct io_async_ctx *io) +static int io_req_defer_prep (struct io_kiocb *req) {struct iovec inline_vecs[UIO_FASTIOV ], *iovec = inline_vecs; +struct io_async_ctx *io = req->io;struct iov_iter iter;ssize_t ret; -memcpy (&io->sqe, req->sqe,sizeof (io->sqe)); - req->sqe = &io->sqe; -switch (io->sqe.opcode) {case IORING_OP_READV :case IORING_OP_READ_FIXED : +/* ensure prep does right import */ + req->io =NULL ; ret =io_read_prep (req, &iovec, &iter, true); + req->io = io; +if (ret <0 ) +break ; +io_req_map_rw (req, ret, iovec, inline_vecs, &iter); + ret =0 ;break ;case IORING_OP_WRITEV :case IORING_OP_WRITE_FIXED : +/* ensure prep does right import */ + req->io =NULL ; ret =io_write_prep (req, &iovec, &iter, true); + req->io = io; +if (ret <0 ) +break ; +io_req_map_rw (req, ret, iovec, inline_vecs, &iter); + ret =0 ;break ;case IORING_OP_SENDMSG : ret =io_sendmsg_prep (req, io);
@@ -2886,41 +2916,34 @@ static int io_req_defer_prep(struct io_kiocb *req, struct io_async_ctx *io) ret =io_connect_prep (req, io);break ;case IORING_OP_TIMEOUT : -return io_timeout_prep (req, io, false); + ret =io_timeout_prep (req, io, false); +break ;case IORING_OP_LINK_TIMEOUT : -return io_timeout_prep (req, io, true); + ret =io_timeout_prep (req, io, true); +break ;default : - req->io = io; -return 0 ; + ret =0 ; +break ; } -if (ret <0 ) -return ret; - - req->io = io; -io_req_map_io (req, ret, iovec, inline_vecs, &iter); -return 0 ; +return ret; }static int io_req_defer (struct io_kiocb *req) {struct io_ring_ctx *ctx = req->ctx; -struct io_async_ctx *io;int ret;/* Still need defer if there is pending req in defer list. */ if (!req_need_defer (req) &&list_empty (&ctx->defer_list))return 0 ; - io =kmalloc (sizeof (*io),GFP_KERNEL ); -if (!io) +if (io_alloc_async_ctx (req))return -EAGAIN ; - ret =io_req_defer_prep (req, io); -if (ret <0 ) { -kfree (io); + ret =io_req_defer_prep (req); +if (ret <0 )return ret; - }spin_lock_irq (&ctx->completion_lock);if (!req_need_defer (req) &&list_empty (&ctx->defer_list)) {
@@ -3366,7 +3389,6 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,*/ if (*link) {struct io_kiocb *prev = *link; -struct io_async_ctx *io;if (req->sqe->flags &IOSQE_IO_DRAIN ) (*link)->flags |=REQ_F_DRAIN_LINK |REQ_F_IO_DRAIN ;
@@ -3374,15 +3396,13 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,if (req->sqe->flags &IOSQE_IO_HARDLINK ) req->flags |=REQ_F_HARDLINK ; - io =kmalloc (sizeof (*io),GFP_KERNEL ); -if (!io) { +if (io_alloc_async_ctx (req)) { ret = -EAGAIN ; goto err_req; } - ret =io_req_defer_prep (req, io); + ret =io_req_defer_prep (req);if (ret) { -kfree (io);/* fail even hard links since we don't submit */ prev->flags |=REQ_F_FAIL_LINK ; goto err_req;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 4/11 io_uring: remove 'sqe' parameter to the OP helpers that take it
In reply to Jens AxboeWe pass in req->sqe for all of them, no need to pass it in as the request is always passed in. This is a necessary prep patch to be able to cleanup/fix the request prep path.
--- fs/io_uring.c | 80 ++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 36 deletions(-)
fs/io_uring.c +44 -36
@@ -1951,8 +1951,9 @@ static int io_nop(struct io_kiocb *req)return 0 ; } -static int io_prep_fsync (struct io_kiocb *req,const struct io_uring_sqe *sqe) +static int io_prep_fsync (struct io_kiocb *req) { +const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx;if (!req->file)
@@ -1966,9 +1967,10 @@ static int io_prep_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe)return 0 ; } -static int io_fsync (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt,bool force_nonblock) +static int io_fsync (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) { +const struct io_uring_sqe *sqe = req->sqe;loff_t sqe_off =READ_ONCE (sqe->off);loff_t sqe_len =READ_ONCE (sqe->len);loff_t end = sqe_off + sqe_len;
@@ -1979,7 +1981,7 @@ static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe,if (unlikely (fsync_flags & ~IORING_FSYNC_DATASYNC ))return -EINVAL ; - ret =io_prep_fsync (req, sqe); + ret =io_prep_fsync (req);if (ret)return ret;
@@ -1998,8 +2000,9 @@ static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe,return 0 ; } -static int io_prep_sfr (struct io_kiocb *req,const struct io_uring_sqe *sqe) +static int io_prep_sfr (struct io_kiocb *req) { +const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx;int ret =0 ;
@@ -2014,17 +2017,16 @@ static int io_prep_sfr(struct io_kiocb *req, const struct io_uring_sqe *sqe)return ret; } -static int io_sync_file_range (struct io_kiocb *req, -const struct io_uring_sqe *sqe, -struct io_kiocb **nxt, +static int io_sync_file_range (struct io_kiocb *req,struct io_kiocb **nxt,bool force_nonblock) { +const struct io_uring_sqe *sqe = req->sqe;loff_t sqe_off;loff_t sqe_len;unsigned flags;int ret; - ret =io_prep_sfr (req, sqe); + ret =io_prep_sfr (req);if (ret)return ret;
@@ -2067,10 +2069,11 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io)#endif } -static int io_sendmsg (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt,bool force_nonblock) +static int io_sendmsg (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) {#if defined(CONFIG_NET ) +const struct io_uring_sqe *sqe = req->sqe;struct io_async_msghdr *kmsg =NULL ;struct socket *sock;int ret;
@@ -2151,10 +2154,11 @@ static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io)#endif } -static int io_recvmsg (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt,bool force_nonblock) +static int io_recvmsg (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) {#if defined(CONFIG_NET ) +const struct io_uring_sqe *sqe = req->sqe;struct io_async_msghdr *kmsg =NULL ;struct socket *sock;int ret;
@@ -2221,10 +2225,11 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,#endif } -static int io_accept (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt,bool force_nonblock) +static int io_accept (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) {#if defined(CONFIG_NET ) +const struct io_uring_sqe *sqe = req->sqe;struct sockaddr __user *addr;int __user *addr_len;unsigned file_flags;
@@ -2272,10 +2277,11 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io)#endif } -static int io_connect (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt,bool force_nonblock) +static int io_connect (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) {#if defined(CONFIG_NET ) +const struct io_uring_sqe *sqe = req->sqe;struct io_async_ctx __io, *io;unsigned file_flags;int addr_len, ret;
@@ -2373,8 +2379,9 @@ static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr)* Find a running poll command that matches one specified in sqe->addr, * and remove it if found. */ -static int io_poll_remove (struct io_kiocb *req,const struct io_uring_sqe *sqe) +static int io_poll_remove (struct io_kiocb *req) { +const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx;int ret;
@@ -2520,9 +2527,9 @@ static void io_poll_req_insert(struct io_kiocb *req)hlist_add_head (&req->hash_node, list); } -static int io_poll_add (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt) +static int io_poll_add (struct io_kiocb *req,struct io_kiocb **nxt) { +const struct io_uring_sqe *sqe = req->sqe;struct io_poll_iocb *poll = &req->poll;struct io_ring_ctx *ctx = req->ctx;struct io_poll_table ipt;
@@ -2659,9 +2666,9 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data)/* * Remove or update an existing timeout command */ -static int io_timeout_remove (struct io_kiocb *req, -const struct io_uring_sqe *sqe) +static int io_timeout_remove (struct io_kiocb *req) { +const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx;unsigned flags;int ret;
@@ -2720,8 +2727,9 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io,return 0 ; } -static int io_timeout (struct io_kiocb *req,const struct io_uring_sqe *sqe) +static int io_timeout (struct io_kiocb *req) { +const struct io_uring_sqe *sqe = req->sqe;unsigned count;struct io_ring_ctx *ctx = req->ctx;struct io_timeout_data *data;
@@ -2861,9 +2869,9 @@ static void io_async_find_and_cancel(struct io_ring_ctx *ctx,io_put_req_find_next (req, nxt); } -static int io_async_cancel (struct io_kiocb *req,const struct io_uring_sqe *sqe, -struct io_kiocb **nxt) +static int io_async_cancel (struct io_kiocb *req,struct io_kiocb **nxt) { +const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx;if (unlikely (ctx->flags &IORING_SETUP_IOPOLL ))
@@ -2986,37 +2994,37 @@ static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, ret =io_write (req, nxt, force_nonblock);break ;case IORING_OP_FSYNC : - ret =io_fsync (req, req->sqe, nxt, force_nonblock); + ret =io_fsync (req, nxt, force_nonblock);break ;case IORING_OP_POLL_ADD : - ret =io_poll_add (req, req->sqe, nxt); + ret =io_poll_add (req, nxt);break ;case IORING_OP_POLL_REMOVE : - ret =io_poll_remove (req, req->sqe); + ret =io_poll_remove (req);break ;case IORING_OP_SYNC_FILE_RANGE : - ret =io_sync_file_range (req, req->sqe, nxt, force_nonblock); + ret =io_sync_file_range (req, nxt, force_nonblock);break ;case IORING_OP_SENDMSG : - ret =io_sendmsg (req, req->sqe, nxt, force_nonblock); + ret =io_sendmsg (req, nxt, force_nonblock);break ;case IORING_OP_RECVMSG : - ret =io_recvmsg (req, req->sqe, nxt, force_nonblock); + ret =io_recvmsg (req, nxt, force_nonblock);break ;case IORING_OP_TIMEOUT : - ret =io_timeout (req, req->sqe); + ret =io_timeout (req);break ;case IORING_OP_TIMEOUT_REMOVE : - ret =io_timeout_remove (req, req->sqe); + ret =io_timeout_remove (req);break ;case IORING_OP_ACCEPT : - ret =io_accept (req, req->sqe, nxt, force_nonblock); + ret =io_accept (req, nxt, force_nonblock);break ;case IORING_OP_CONNECT : - ret =io_connect (req, req->sqe, nxt, force_nonblock); + ret =io_connect (req, nxt, force_nonblock);break ;case IORING_OP_ASYNC_CANCEL : - ret =io_async_cancel (req, req->sqe, nxt); + ret =io_async_cancel (req, nxt);break ;default : ret = -EINVAL ;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 5/11 io_uring: any deferred command must have stable sqe data
In reply to Jens AxboeWe're currently not retaining sqe data for accept, fsync, and sync_file_range. None of these commands need data outside of what is directly provided, hence it can't go stale when the request is deferred. However, it can get reused, if an application reuses SQE entries.
Ensure that we retain the information we need and only read the sqe contents once, off the submission path. Most of this is just moving code into a prep and finish function.
--- fs/io_uring.c | 219 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 169 insertions(+), 50 deletions(-)
fs/io_uring.c +169 -50
@@ -304,6 +304,20 @@ struct io_timeout_data {u32 seq_offset; }; +struct io_accept { +struct file *file; +struct sockaddr __user *addr; +int __user *addr_len; +int flags; +}; + +struct io_sync { +struct file *file; +loff_t len; +loff_t off; +int flags; +}; +struct io_async_connect {struct sockaddr_storage address; };
@@ -344,6 +358,8 @@ struct io_kiocb {struct file *file;struct kiocb rw;struct io_poll_iocb poll; +struct io_accept accept; +struct io_sync sync; };const struct io_uring_sqe *sqe;
@@ -379,6 +395,7 @@ struct io_kiocb {#define REQ_F_INFLIGHT 16384/* on inflight list */ #define REQ_F_COMP_LOCKED 32768/* completion under lock */ #define REQ_F_HARDLINK 65536/* doesn't sever on completion < 0 */ +#define REQ_F_PREPPED 131072/* request already opcode prepared */ u64 user_data;u32 result;u32 sequence;
@@ -1956,6 +1973,8 @@ static int io_prep_fsync(struct io_kiocb *req)const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx; +if (req->flags &REQ_F_PREPPED ) +return 0 ;if (!req->file)return -EBADF ;
@@ -1964,39 +1983,70 @@ static int io_prep_fsync(struct io_kiocb *req)if (unlikely (sqe->addr || sqe->ioprio || sqe->buf_index))return -EINVAL ; + req->sync.flags =READ_ONCE (sqe->fsync_flags); +if (unlikely (req->sync.flags & ~IORING_FSYNC_DATASYNC )) +return -EINVAL ; + + req->sync.off =READ_ONCE (sqe->off); + req->sync.len =READ_ONCE (sqe->len); + req->flags |=REQ_F_PREPPED ;return 0 ; } +static bool io_req_cancelled (struct io_kiocb *req) +{ +if (req->work.flags &IO_WQ_WORK_CANCEL ) { +req_set_fail_links (req); +io_cqring_add_event (req, -ECANCELED ); +io_put_req (req); +return true; + } + +return false; +} + +static void io_fsync_finish (struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); +loff_t end = req->sync.off + req->sync.len; +struct io_kiocb *nxt =NULL ; +int ret; + +if (io_req_cancelled (req)) +return ; + + ret =vfs_fsync_range (req->rw.ki_filp, req->sync.off, + end >0 ? end :LLONG_MAX , + req->sync.flags &IORING_FSYNC_DATASYNC ); +if (ret <0 ) +req_set_fail_links (req); +io_cqring_add_event (req, ret); +io_put_req_find_next (req, &nxt); +if (nxt) + *workptr = &nxt->work; +} +static int io_fsync (struct io_kiocb *req,struct io_kiocb **nxt,bool force_nonblock) { -const struct io_uring_sqe *sqe = req->sqe; -loff_t sqe_off =READ_ONCE (sqe->off); -loff_t sqe_len =READ_ONCE (sqe->len); -loff_t end = sqe_off + sqe_len; -unsigned fsync_flags; +struct io_wq_work *work, *old_work;int ret; - fsync_flags =READ_ONCE (sqe->fsync_flags); -if (unlikely (fsync_flags & ~IORING_FSYNC_DATASYNC )) -return -EINVAL ; - ret =io_prep_fsync (req);if (ret)return ret;/* fsync always requires a blocking context */ -if (force_nonblock) +if (force_nonblock) { +io_put_req (req); + req->work.func = io_fsync_finish;return -EAGAIN ; + } - ret =vfs_fsync_range (req->rw.ki_filp, sqe_off, - end >0 ? end :LLONG_MAX , - fsync_flags &IORING_FSYNC_DATASYNC ); - -if (ret <0 ) -req_set_fail_links (req); -io_cqring_add_event (req, ret); -io_put_req_find_next (req, nxt); + work = old_work = &req->work; +io_fsync_finish (&work); +if (work && work != old_work) + *nxt = container_of(work, struct io_kiocb, work);return 0 ; }
@@ -2004,8 +2054,9 @@ static int io_prep_sfr(struct io_kiocb *req) {const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx; -int ret =0 ; +if (req->flags &REQ_F_PREPPED ) +return 0 ;if (!req->file)return -EBADF ;
@@ -2014,16 +2065,36 @@ static int io_prep_sfr(struct io_kiocb *req)if (unlikely (sqe->addr || sqe->ioprio || sqe->buf_index))return -EINVAL ; -return ret; + req->sync.off =READ_ONCE (sqe->off); + req->sync.len =READ_ONCE (sqe->len); + req->sync.flags =READ_ONCE (sqe->sync_range_flags); + req->flags |=REQ_F_PREPPED ; +return 0 ; +} + +static void io_sync_file_range_finish (struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); +struct io_kiocb *nxt =NULL ; +int ret; + +if (io_req_cancelled (req)) +return ; + + ret =sync_file_range (req->rw.ki_filp, req->sync.off, req->sync.len, + req->sync.flags); +if (ret <0 ) +req_set_fail_links (req); +io_cqring_add_event (req, ret); +io_put_req_find_next (req, &nxt); +if (nxt) + *workptr = &nxt->work; }static int io_sync_file_range (struct io_kiocb *req,struct io_kiocb **nxt,bool force_nonblock) { -const struct io_uring_sqe *sqe = req->sqe; -loff_t sqe_off; -loff_t sqe_len; -unsigned flags; +struct io_wq_work *work, *old_work;int ret; ret =io_prep_sfr (req);
@@ -2031,19 +2102,16 @@ static int io_sync_file_range(struct io_kiocb *req, struct io_kiocb **nxt,return ret;/* sync_file_range always requires a blocking context */ -if (force_nonblock) +if (force_nonblock) { +io_put_req (req); + req->work.func = io_sync_file_range_finish;return -EAGAIN ; + } - sqe_off =READ_ONCE (sqe->off); - sqe_len =READ_ONCE (sqe->len); - flags =READ_ONCE (sqe->sync_range_flags); - - ret =sync_file_range (req->rw.ki_filp, sqe_off, sqe_len, flags); - -if (ret <0 ) -req_set_fail_links (req); -io_cqring_add_event (req, ret); -io_put_req_find_next (req, nxt); + work = old_work = &req->work; +io_sync_file_range_finish (&work); +if (work && work != old_work) + *nxt = container_of(work, struct io_kiocb, work);return 0 ; }
@@ -2225,31 +2293,40 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt,#endif } -static int io_accept (struct io_kiocb *req,struct io_kiocb **nxt, -bool force_nonblock) -{#if defined(CONFIG_NET ) +static int io_accept_prep (struct io_kiocb *req) +{const struct io_uring_sqe *sqe = req->sqe; -struct sockaddr __user *addr; -int __user *addr_len; -unsigned file_flags; -int flags, ret; +struct io_accept *accept = &req->accept; + +if (req->flags &REQ_F_PREPPED ) +return 0 ;if (unlikely (req->ctx->flags & (IORING_SETUP_IOPOLL |IORING_SETUP_SQPOLL )))return -EINVAL ;if (sqe->ioprio || sqe->len || sqe->buf_index)return -EINVAL ; - addr = (struct sockaddr __user *) (unsigned long )READ_ONCE (sqe->addr); - addr_len = (int __user *) (unsigned long )READ_ONCE (sqe->addr2); - flags =READ_ONCE (sqe->accept_flags); - file_flags = force_nonblock ?O_NONBLOCK :0 ; + accept->addr = (struct sockaddr __user *) + (unsigned long )READ_ONCE (sqe->addr); + accept->addr_len = (int __user *) (unsigned long )READ_ONCE (sqe->addr2); + accept->flags =READ_ONCE (sqe->accept_flags); + req->flags |=REQ_F_PREPPED ; +return 0 ; +} - ret =__sys_accept4_file (req->file, file_flags, addr, addr_len, flags); -if (ret == -EAGAIN && force_nonblock) { - req->work.flags |=IO_WQ_WORK_NEEDS_FILES ; +static int __io_accept (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) +{ +struct io_accept *accept = &req->accept; +unsigned file_flags; +int ret; + + file_flags = force_nonblock ?O_NONBLOCK :0 ; + ret =__sys_accept4_file (req->file, file_flags, accept->addr, + accept->addr_len, accept->flags); +if (ret == -EAGAIN && force_nonblock)return -EAGAIN ; - }if (ret == -ERESTARTSYS ) ret = -EINTR ;if (ret <0 )
@@ -2257,6 +2334,39 @@ static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt,io_cqring_add_event (req, ret);io_put_req_find_next (req, nxt);return 0 ; +} + +static void io_accept_finish (struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); +struct io_kiocb *nxt =NULL ; + +if (io_req_cancelled (req)) +return ; +__io_accept (req, &nxt, false); +if (nxt) + *workptr = &nxt->work; +} +#endif + +static int io_accept (struct io_kiocb *req,struct io_kiocb **nxt, +bool force_nonblock) +{ +#if defined(CONFIG_NET ) +int ret; + + ret =io_accept_prep (req); +if (ret) +return ret; + + ret =__io_accept (req, nxt, force_nonblock); +if (ret == -EAGAIN && force_nonblock) { + req->work.func = io_accept_finish; + req->work.flags |=IO_WQ_WORK_NEEDS_FILES ; +io_put_req (req); +return -EAGAIN ; + } +return 0 ;#else return -EOPNOTSUPP ;#endif
@@ -2914,6 +3024,12 @@ static int io_req_defer_prep(struct io_kiocb *req)io_req_map_rw (req, ret, iovec, inline_vecs, &iter); ret =0 ;break ; +case IORING_OP_FSYNC : + ret =io_prep_fsync (req); +break ; +case IORING_OP_SYNC_FILE_RANGE : + ret =io_prep_sfr (req); +break ;case IORING_OP_SENDMSG : ret =io_sendmsg_prep (req, io);break ;
@@ -2929,6 +3045,9 @@ static int io_req_defer_prep(struct io_kiocb *req)case IORING_OP_LINK_TIMEOUT : ret =io_timeout_prep (req, io, true);break ; +case IORING_OP_ACCEPT : + ret =io_accept_prep (req); +break ;default : ret =0 ;break ;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 6/11 io_uring: make HARDLINK imply LINK
In reply to Jens AxboeFrom: Pavel Begunkov <[email protected]>
The rules are as follows, if IOSQE_IO_HARDLINK is specified, then it's a link and there is no need to set IOSQE_IO_LINK separately, though it could be there. Add proper check and ensure that IOSQE_IO_HARDLINK implies IOSQE_IO_LINK.
--- fs/io_uring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/io_uring.c +1 -1
@@ -3693,7 +3693,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,* 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) { +if (!(sqe_flags & (IOSQE_IO_LINK |IOSQE_IO_HARDLINK )) && link) {io_queue_link_head (link); link =NULL ; }
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 7/11 io_uring: make IORING_POLL_ADD and IORING_POLL_REMOVE deferrable
In reply to Jens AxboeIf we defer these commands as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the poll add/remove into the prep handling to make it safe for SQE reuse.
--- fs/io_uring.c | 68 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-)
fs/io_uring.c +54 -14
@@ -289,7 +289,10 @@ struct io_ring_ctx {*/ struct io_poll_iocb {struct file *file; -struct wait_queue_head *head; +union { +struct wait_queue_head *head; +u64 addr; + };__poll_t events;bool done;bool canceled;
@@ -2485,24 +2488,40 @@ static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr)return -ENOENT ; } +static int io_poll_remove_prep (struct io_kiocb *req) +{ +const struct io_uring_sqe *sqe = req->sqe; + +if (req->flags &REQ_F_PREPPED ) +return 0 ; +if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL )) +return -EINVAL ; +if (sqe->ioprio || sqe->off || sqe->len || sqe->buf_index || + sqe->poll_events) +return -EINVAL ; + + req->poll.addr =READ_ONCE (sqe->addr); + req->flags |=REQ_F_PREPPED ; +return 0 ; +} +/* * Find a running poll command that matches one specified in sqe->addr, * and remove it if found. */ static int io_poll_remove (struct io_kiocb *req) { -const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx; +u64 addr;int ret; -if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL )) -return -EINVAL ; -if (sqe->ioprio || sqe->off || sqe->len || sqe->buf_index || - sqe->poll_events) -return -EINVAL ; + ret =io_poll_remove_prep (req); +if (ret) +return ret; + addr = req->poll.addr;spin_lock_irq (&ctx->completion_lock); - ret =io_poll_cancel (ctx,READ_ONCE (sqe->addr)); + ret =io_poll_cancel (ctx, addr);spin_unlock_irq (&ctx->completion_lock);io_cqring_add_event (req, ret);
@@ -2637,16 +2656,14 @@ static void io_poll_req_insert(struct io_kiocb *req)hlist_add_head (&req->hash_node, list); } -static int io_poll_add (struct io_kiocb *req,struct io_kiocb **nxt) +static int io_poll_add_prep (struct io_kiocb *req) {const struct io_uring_sqe *sqe = req->sqe;struct io_poll_iocb *poll = &req->poll; -struct io_ring_ctx *ctx = req->ctx; -struct io_poll_table ipt; -bool cancel = false; -__poll_t mask;u16 events; +if (req->flags &REQ_F_PREPPED ) +return 0 ;if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL ))return -EINVAL ;if (sqe->addr || sqe->ioprio || sqe->off || sqe->len || sqe->buf_index)
@@ -2654,9 +2671,26 @@ static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt)if (!poll->file)return -EBADF ; -INIT_IO_WORK (&req->work, io_poll_complete_work); + req->flags |=REQ_F_PREPPED ; events =READ_ONCE (sqe->poll_events); poll->events =demangle_poll (events) |EPOLLERR |EPOLLHUP ; +return 0 ; +} + +static int io_poll_add (struct io_kiocb *req,struct io_kiocb **nxt) +{ +struct io_poll_iocb *poll = &req->poll; +struct io_ring_ctx *ctx = req->ctx; +struct io_poll_table ipt; +bool cancel = false; +__poll_t mask; +int ret; + + ret =io_poll_add_prep (req); +if (ret) +return ret; + +INIT_IO_WORK (&req->work, io_poll_complete_work);INIT_HLIST_NODE (&req->hash_node); poll->head =NULL ;
@@ -3024,6 +3058,12 @@ static int io_req_defer_prep(struct io_kiocb *req)io_req_map_rw (req, ret, iovec, inline_vecs, &iter); ret =0 ;break ; +case IORING_OP_POLL_ADD : + ret =io_poll_add_prep (req); +break ; +case IORING_OP_POLL_REMOVE : + ret =io_poll_remove_prep (req); +break ;case IORING_OP_FSYNC : ret =io_prep_fsync (req);break ;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 8/11 io_uring: make IORING_OP_CANCEL_ASYNC deferrable
In reply to Jens AxboeIf we defer this command as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the async cancel op into the prep handling to make it safe for SQE reuse.
--- fs/io_uring.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
fs/io_uring.c +28 -4
@@ -321,6 +321,11 @@ struct io_sync {int flags; }; +struct io_cancel { +struct file *file; +u64 addr; +}; +struct io_async_connect {struct sockaddr_storage address; };
@@ -363,6 +368,7 @@ struct io_kiocb {struct io_poll_iocb poll;struct io_accept accept;struct io_sync sync; +struct io_cancel cancel; };const struct io_uring_sqe *sqe;
@@ -3013,18 +3019,33 @@ static void io_async_find_and_cancel(struct io_ring_ctx *ctx,io_put_req_find_next (req, nxt); } -static int io_async_cancel (struct io_kiocb *req,struct io_kiocb **nxt) +static int io_async_cancel_prep (struct io_kiocb *req) {const struct io_uring_sqe *sqe = req->sqe; -struct io_ring_ctx *ctx = req->ctx; -if (unlikely (ctx->flags &IORING_SETUP_IOPOLL )) +if (req->flags &REQ_F_PREPPED ) +return 0 ; +if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL ))return -EINVAL ;if (sqe->flags || sqe->ioprio || sqe->off || sqe->len || sqe->cancel_flags)return -EINVAL ; -io_async_find_and_cancel (ctx, req,READ_ONCE (sqe->addr), nxt,0 ); + req->flags |=REQ_F_PREPPED ; + req->cancel.addr =READ_ONCE (sqe->addr); +return 0 ; +} + +static int io_async_cancel (struct io_kiocb *req,struct io_kiocb **nxt) +{ +struct io_ring_ctx *ctx = req->ctx; +int ret; + + ret =io_async_cancel_prep (req); +if (ret) +return ret; + +io_async_find_and_cancel (ctx, req, req->cancel.addr, nxt,0 );return 0 ; }
@@ -3082,6 +3103,9 @@ static int io_req_defer_prep(struct io_kiocb *req)case IORING_OP_TIMEOUT : ret =io_timeout_prep (req, io, false);break ; +case IORING_OP_ASYNC_CANCEL : + ret =io_async_cancel_prep (req); +break ;case IORING_OP_LINK_TIMEOUT : ret =io_timeout_prep (req, io, true);break ;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 9/11 io_uring: make IORING_OP_TIMEOUT_REMOVE deferrable
In reply to Jens AxboeIf we defer this command as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the timeout remove op into the prep handling to make it safe for SQE reuse.
--- fs/io_uring.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-)
fs/io_uring.c +34 -10
@@ -326,6 +326,12 @@ struct io_cancel {u64 addr; }; +struct io_timeout { +struct file *file; +u64 addr; +int flags; +}; +struct io_async_connect {struct sockaddr_storage address; };
@@ -369,6 +375,7 @@ struct io_kiocb {struct io_accept accept;struct io_sync sync;struct io_cancel cancel; +struct io_timeout timeout; };const struct io_uring_sqe *sqe;
@@ -2813,26 +2820,40 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data)return 0 ; } +static int io_timeout_remove_prep (struct io_kiocb *req) +{ +const struct io_uring_sqe *sqe = req->sqe; + +if (req->flags &REQ_F_PREPPED ) +return 0 ; +if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL )) +return -EINVAL ; +if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->len) +return -EINVAL ; + + req->timeout.addr =READ_ONCE (sqe->addr); + req->timeout.flags =READ_ONCE (sqe->timeout_flags); +if (req->timeout.flags) +return -EINVAL ; + + req->flags |=REQ_F_PREPPED ; +return 0 ; +} +/* * Remove or update an existing timeout command */ static int io_timeout_remove (struct io_kiocb *req) { -const struct io_uring_sqe *sqe = req->sqe;struct io_ring_ctx *ctx = req->ctx; -unsigned flags;int ret; -if (unlikely (ctx->flags &IORING_SETUP_IOPOLL )) -return -EINVAL ; -if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->len) -return -EINVAL ; - flags =READ_ONCE (sqe->timeout_flags); -if (flags) -return -EINVAL ; + ret =io_timeout_remove_prep (req); +if (ret) +return ret;spin_lock_irq (&ctx->completion_lock); - ret =io_timeout_cancel (ctx,READ_ONCE (sqe->addr)); + ret =io_timeout_cancel (ctx, req->timeout.addr);io_cqring_fill_event (req, ret);io_commit_cqring (ctx);
@@ -3103,6 +3124,9 @@ static int io_req_defer_prep(struct io_kiocb *req)case IORING_OP_TIMEOUT : ret =io_timeout_prep (req, io, false);break ; +case IORING_OP_TIMEOUT_REMOVE : + ret =io_timeout_remove_prep (req); +break ;case IORING_OP_ASYNC_CANCEL : ret =io_async_cancel_prep (req);break ;
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 10/11 io_uring: read opcode and user_data from SQE exactly once
In reply to Jens AxboeIf we defer a request, we can't be reading the opcode again. Ensure that the user_data and opcode fields are stable. For the user_data we already have a place for it, for the opcode we can fill a one byte hold and store that as well. For both of them, assign them when we originally read the SQE in io_get_sqring(). Any code that uses sqe->opcode or sqe->user_data is switched to req->opcode and req->user_data.
--- fs/io_uring.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-)
fs/io_uring.c +19 -24
@@ -385,6 +385,7 @@ struct io_kiocb {bool has_user;bool in_async;bool needs_fixed_file; +u8 opcode;struct io_ring_ctx *ctx;union {
@@ -598,12 +599,10 @@ static void __io_commit_cqring(struct io_ring_ctx *ctx) } } -static inline bool io_sqe_needs_user (const struct io_uring_sqe *sqe) +static inline bool io_req_needs_user (struct io_kiocb *req) { -u8 opcode =READ_ONCE (sqe->opcode); - -return !(opcode ==IORING_OP_READ_FIXED || - opcode ==IORING_OP_WRITE_FIXED ); +return !(req->opcode ==IORING_OP_READ_FIXED || + req->opcode ==IORING_OP_WRITE_FIXED ); } static inline bool io_prep_async_work(struct io_kiocb *req,
@@ -612,7 +611,7 @@ static inline bool io_prep_async_work(struct io_kiocb *req,bool do_hashed = false;if (req->sqe) { -switch (req->sqe->opcode) { +switch (req->opcode) {case IORING_OP_WRITEV :case IORING_OP_WRITE_FIXED :/* only regular files should be hashed for writes */
@@ -635,7 +634,7 @@ static inline bool io_prep_async_work(struct io_kiocb *req, req->work.flags |=IO_WQ_WORK_UNBOUND ;break ; } -if (io_sqe_needs_user (req->sqe)) +if (io_req_needs_user (req)) req->work.flags |=IO_WQ_WORK_NEEDS_USER ; }
@@ -1009,7 +1008,7 @@ static void io_fail_links(struct io_kiocb *req)trace_io_uring_fail_link (req, link);if ((req->flags &REQ_F_LINK_TIMEOUT ) && - link->sqe->opcode ==IORING_OP_LINK_TIMEOUT ) { + link->opcode ==IORING_OP_LINK_TIMEOUT ) {io_link_cancel_timeout (link); }else {io_cqring_fill_event (link, -ECANCELED );
@@ -1652,7 +1651,7 @@ static ssize_t io_import_iovec(int rw, struct io_kiocb *req,* for that purpose and instead let the caller pass in the read/write * flag. */ - opcode =READ_ONCE (sqe->opcode); + opcode = req->opcode;if (opcode ==IORING_OP_READ_FIXED || opcode ==IORING_OP_WRITE_FIXED ) { *iovec =NULL ;return io_import_fixed (req->ctx, rw, sqe, iter);
@@ -3176,11 +3175,10 @@ __attribute__((nonnull))static int io_issue_sqe (struct io_kiocb *req,struct io_kiocb **nxt,bool force_nonblock) { -int ret, opcode;struct io_ring_ctx *ctx = req->ctx; +int ret; - opcode =READ_ONCE (req->sqe->opcode); -switch (opcode) { +switch (req->opcode) {case IORING_OP_NOP : ret =io_nop (req);break ;
@@ -3317,11 +3315,9 @@ static bool io_req_op_valid(int op)return op >=IORING_OP_NOP && op <IORING_OP_LAST ; } -static int io_op_needs_file (const struct io_uring_sqe *sqe) +static int io_req_needs_file (struct io_kiocb *req) { -int op =READ_ONCE (sqe->opcode); - -switch (op) { +switch (req->opcode) {case IORING_OP_NOP :case IORING_OP_POLL_REMOVE :case IORING_OP_TIMEOUT :
@@ -3330,7 +3326,7 @@ static int io_op_needs_file(const struct io_uring_sqe *sqe)case IORING_OP_LINK_TIMEOUT :return 0 ;default : -if (io_req_op_valid (op)) +if (io_req_op_valid (req->opcode))return 1 ;return -EINVAL ; }
@@ -3357,7 +3353,7 @@ static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req)if (flags &IOSQE_IO_DRAIN ) req->flags |=REQ_F_IO_DRAIN ; - ret =io_op_needs_file (req->sqe); + ret =io_req_needs_file (req);if (ret <=0 )return ret;
@@ -3477,7 +3473,7 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req) nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb, link_list); -if (!nxt || nxt->sqe->opcode !=IORING_OP_LINK_TIMEOUT ) +if (!nxt || nxt->opcode !=IORING_OP_LINK_TIMEOUT )return NULL ; req->flags |=REQ_F_LINK_TIMEOUT ;
@@ -3579,8 +3575,6 @@ 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; -/* enforce forwards compatibility on users */ if (unlikely (req->sqe->flags & ~SQE_VALID_FLAGS )) { ret = -EINVAL ;
@@ -3712,6 +3706,8 @@ static bool io_get_sqring(struct io_ring_ctx *ctx, struct io_kiocb *req)*/ req->sequence = ctx->cached_sq_head; req->sqe = &ctx->sq_sqes[head]; + req->opcode =READ_ONCE (req->sqe->opcode); + req->user_data =READ_ONCE (req->sqe->user_data); ctx->cached_sq_head++;return true; }
@@ -3757,7 +3753,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,break ; } -if (io_sqe_needs_user (req->sqe) && !*mm) { +if (io_req_needs_user (req) && !*mm) { mm_fault = mm_fault || !mmget_not_zero (ctx->sqo_mm);if (!mm_fault) {use_mm (ctx->sqo_mm);
@@ -3773,8 +3769,7 @@ 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); +trace_io_uring_submit_sqe (ctx, req->user_data, true, async);if (!io_submit_sqe (req, statep, &link))break ;/*
Signature · 1 line
2.24.1
Jens Axboe
maintainerPATCH 11/11 io_uring: warn about unhandled opcode
In reply to Jens AxboeNow that we have all the opcodes handled in terms of command prep and SQE reuse, add a printk_once() to warn about any potentially new and unhandled ones.
--- fs/io_uring.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
fs/io_uring.c +6 -2
@@ -3074,9 +3074,11 @@ static int io_req_defer_prep(struct io_kiocb *req)struct iovec inline_vecs[UIO_FASTIOV ], *iovec = inline_vecs;struct io_async_ctx *io = req->io;struct iov_iter iter; -ssize_t ret; +ssize_t ret =0 ;switch (io->sqe.opcode) { +case IORING_OP_NOP : +break ;case IORING_OP_READV :case IORING_OP_READ_FIXED :/* ensure prep does right import */
@@ -3136,7 +3138,9 @@ static int io_req_defer_prep(struct io_kiocb *req) ret =io_accept_prep (req);break ;default : - ret =0 ; +printk_once (KERN_WARNING "io_uring: unhandled opcode %d\n" , + io->sqe.opcode); + ret = -EINVAL ;break ; }
Signature · 1 line
2.24.1