]> Git Repo - linux.git/commitdiff
Merge tag 'iov_iter.3-5.15-2021-09-17' of git://git.kernel.dk/linux-block
authorLinus Torvalds <[email protected]>
Fri, 17 Sep 2021 16:23:44 +0000 (09:23 -0700)
committerLinus Torvalds <[email protected]>
Fri, 17 Sep 2021 16:23:44 +0000 (09:23 -0700)
Pull io_uring iov_iter retry fixes from Jens Axboe:
 "This adds a helper to save/restore iov_iter state, and modifies
  io_uring to use it.

  After that is done, we can now kill the iter->truncated addition that
  we added for this release. The io_uring change is being overly
  cautious with the save/restore/advance, but better safe than sorry and
  we can always improve that and reduce the overhead if it proves to be
  of concern. The only case to be worried about in this regard is huge
  IO, where iteration can take a while to iterate segments.

  I spent some time writing test cases, and expanded the coverage quite
  a bit from the last posting of this. liburing carries this regression
  test case now:

      https://git.kernel.dk/cgit/liburing/tree/test/file-verify.c

  which exercises all of this. It now also supports provided buffers,
  and explicitly tests for end-of-file/device truncation as well.

  On top of that, Pavel sanitized the IOPOLL retry path to follow the
  exact same pattern as normal IO"

* tag 'iov_iter.3-5.15-2021-09-17' of git://git.kernel.dk/linux-block:
  io_uring: move iopoll reissue into regular IO path
  Revert "iov_iter: track truncated size"
  io_uring: use iov_iter state save/restore helpers
  iov_iter: add helper to save iov_iter state

1  2 
fs/io_uring.c

diff --combined fs/io_uring.c
index 3077f85a2638142f2518a3f200b75cfc386e706c,81b0c2723c724c2e83140c85b6d42efaf477077f..e372d5b9f6dc04ce31b39d4499602d54815867f6
@@@ -712,6 -712,7 +712,7 @@@ struct io_async_rw 
        struct iovec                    fast_iov[UIO_FASTIOV];
        const struct iovec              *free_iovec;
        struct iov_iter                 iter;
+       struct iov_iter_state           iter_state;
        size_t                          bytes_done;
        struct wait_page_queue          wpq;
  };
@@@ -735,7 -736,6 +736,6 @@@ enum 
        REQ_F_BUFFER_SELECTED_BIT,
        REQ_F_COMPLETE_INLINE_BIT,
        REQ_F_REISSUE_BIT,
-       REQ_F_DONT_REISSUE_BIT,
        REQ_F_CREDS_BIT,
        REQ_F_REFCOUNT_BIT,
        REQ_F_ARM_LTIMEOUT_BIT,
@@@ -782,8 -782,6 +782,6 @@@ enum 
        REQ_F_COMPLETE_INLINE   = BIT(REQ_F_COMPLETE_INLINE_BIT),
        /* caller should reissue async */
        REQ_F_REISSUE           = BIT(REQ_F_REISSUE_BIT),
-       /* don't attempt request reissue, see io_rw_reissue() */
-       REQ_F_DONT_REISSUE      = BIT(REQ_F_DONT_REISSUE_BIT),
        /* supports async reads */
        REQ_F_NOWAIT_READ       = BIT(REQ_F_NOWAIT_READ_BIT),
        /* supports async writes */
@@@ -1482,8 -1480,6 +1480,8 @@@ static void io_kill_timeout(struct io_k
        struct io_timeout_data *io = req->async_data;
  
        if (hrtimer_try_to_cancel(&io->timer) != -1) {
 +              if (status)
 +                      req_set_fail(req);
                atomic_set(&req->ctx->cq_timeouts,
                        atomic_read(&req->ctx->cq_timeouts) + 1);
                list_del_init(&req->timeout.list);
@@@ -1621,11 -1617,8 +1619,11 @@@ static void io_cqring_ev_posted(struct 
  
  static void io_cqring_ev_posted_iopoll(struct io_ring_ctx *ctx)
  {
 +      /* see waitqueue_active() comment */
 +      smp_mb();
 +
        if (ctx->flags & IORING_SETUP_SQPOLL) {
 -              if (wq_has_sleeper(&ctx->cq_wait))
 +              if (waitqueue_active(&ctx->cq_wait))
                        wake_up_all(&ctx->cq_wait);
        }
        if (io_should_trigger_evfd(ctx))
@@@ -2444,13 -2437,6 +2442,6 @@@ static void io_iopoll_complete(struct i
                req = list_first_entry(done, struct io_kiocb, inflight_entry);
                list_del(&req->inflight_entry);
  
-               if (READ_ONCE(req->result) == -EAGAIN &&
-                   !(req->flags & REQ_F_DONT_REISSUE)) {
-                       req->iopoll_completed = 0;
-                       io_req_task_queue_reissue(req);
-                       continue;
-               }
                __io_cqring_fill_event(ctx, req->user_data, req->result,
                                        io_put_rw_kbuf(req));
                (*nr_events)++;
@@@ -2613,8 -2599,7 +2604,7 @@@ static bool io_resubmit_prep(struct io_
  
        if (!rw)
                return !io_req_prep_async(req);
-       /* may have left rw->iter inconsistent on -EIOCBQUEUED */
-       iov_iter_revert(&rw->iter, req->result - iov_iter_count(&rw->iter));
+       iov_iter_restore(&rw->iter, &rw->iter_state);
        return true;
  }
  
@@@ -2714,10 -2699,9 +2704,9 @@@ static void io_complete_rw_iopoll(struc
        if (kiocb->ki_flags & IOCB_WRITE)
                kiocb_end_write(req);
        if (unlikely(res != req->result)) {
-               if (!(res == -EAGAIN && io_rw_should_reissue(req) &&
-                   io_resubmit_prep(req))) {
-                       req_set_fail(req);
-                       req->flags |= REQ_F_DONT_REISSUE;
+               if (res == -EAGAIN && io_rw_should_reissue(req)) {
+                       req->flags |= REQ_F_REISSUE;
+                       return;
                }
        }
  
@@@ -2843,8 -2827,7 +2832,8 @@@ static bool io_file_supports_nowait(str
        return __io_file_supports_nowait(req->file, rw);
  }
  
 -static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 +static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
 +                    int rw)
  {
        struct io_ring_ctx *ctx = req->ctx;
        struct kiocb *kiocb = &req->rw.kiocb;
        if (unlikely(ret))
                return ret;
  
 -      /* don't allow async punt for O_NONBLOCK or RWF_NOWAIT */
 -      if ((kiocb->ki_flags & IOCB_NOWAIT) || (file->f_flags & O_NONBLOCK))
 +      /*
 +       * If the file is marked O_NONBLOCK, still allow retry for it if it
 +       * supports async. Otherwise it's impossible to use O_NONBLOCK files
 +       * reliably. If not, or it IOCB_NOWAIT is set, don't retry.
 +       */
 +      if ((kiocb->ki_flags & IOCB_NOWAIT) ||
 +          ((file->f_flags & O_NONBLOCK) && !io_file_supports_nowait(req, rw)))
                req->flags |= REQ_F_NOWAIT;
  
        ioprio = READ_ONCE(sqe->ioprio);
@@@ -2937,7 -2915,6 +2926,6 @@@ static void kiocb_done(struct kiocb *ki
  {
        struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb);
        struct io_async_rw *io = req->async_data;
-       bool check_reissue = kiocb->ki_complete == io_complete_rw;
  
        /* add previously done IO, if any */
        if (io && io->bytes_done > 0) {
  
        if (req->flags & REQ_F_CUR_POS)
                req->file->f_pos = kiocb->ki_pos;
-       if (ret >= 0 && check_reissue)
+       if (ret >= 0 && (kiocb->ki_complete == io_complete_rw))
                __io_complete_rw(req, ret, 0, issue_flags);
        else
                io_rw_done(kiocb, ret);
  
-       if (check_reissue && (req->flags & REQ_F_REISSUE)) {
+       if (req->flags & REQ_F_REISSUE) {
                req->flags &= ~REQ_F_REISSUE;
                if (io_resubmit_prep(req)) {
                        io_req_task_queue_reissue(req);
                } else {
+                       unsigned int cflags = io_put_rw_kbuf(req);
+                       struct io_ring_ctx *ctx = req->ctx;
                        req_set_fail(req);
-                       __io_req_complete(req, issue_flags, ret,
-                                         io_put_rw_kbuf(req));
+                       if (issue_flags & IO_URING_F_NONBLOCK) {
+                               mutex_lock(&ctx->uring_lock);
+                               __io_req_complete(req, issue_flags, ret, cflags);
+                               mutex_unlock(&ctx->uring_lock);
+                       } else {
+                               __io_req_complete(req, issue_flags, ret, cflags);
+                       }
                }
        }
  }
@@@ -3269,15 -3254,12 +3265,15 @@@ static ssize_t loop_rw_iter(int rw, str
                                ret = nr;
                        break;
                }
 +              if (!iov_iter_is_bvec(iter)) {
 +                      iov_iter_advance(iter, nr);
 +              } else {
 +                      req->rw.len -= nr;
 +                      req->rw.addr += nr;
 +              }
                ret += nr;
                if (nr != iovec.iov_len)
                        break;
 -              req->rw.len -= nr;
 -              req->rw.addr += nr;
 -              iov_iter_advance(iter, nr);
        }
  
        return ret;
@@@ -3324,12 -3306,17 +3320,17 @@@ static int io_setup_async_rw(struct io_
        if (!force && !io_op_defs[req->opcode].needs_async_setup)
                return 0;
        if (!req->async_data) {
+               struct io_async_rw *iorw;
                if (io_alloc_async_data(req)) {
                        kfree(iovec);
                        return -ENOMEM;
                }
  
                io_req_map_rw(req, iovec, fast_iov, iter);
+               iorw = req->async_data;
+               /* we've copied and mapped the iter, ensure state is saved */
+               iov_iter_save_state(&iorw->iter, &iorw->iter_state);
        }
        return 0;
  }
@@@ -3348,6 -3335,7 +3349,7 @@@ static inline int io_rw_prep_async(stru
        iorw->free_iovec = iov;
        if (iov)
                req->flags |= REQ_F_NEED_CLEANUP;
+       iov_iter_save_state(&iorw->iter, &iorw->iter_state);
        return 0;
  }
  
@@@ -3355,7 -3343,7 +3357,7 @@@ static int io_read_prep(struct io_kioc
  {
        if (unlikely(!(req->file->f_mode & FMODE_READ)))
                return -EBADF;
 -      return io_prep_rw(req, sqe);
 +      return io_prep_rw(req, sqe, READ);
  }
  
  /*
@@@ -3451,19 -3439,28 +3453,28 @@@ static int io_read(struct io_kiocb *req
        struct kiocb *kiocb = &req->rw.kiocb;
        struct iov_iter __iter, *iter = &__iter;
        struct io_async_rw *rw = req->async_data;
-       ssize_t io_size, ret, ret2;
        bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+       struct iov_iter_state __state, *state;
+       ssize_t ret, ret2;
  
        if (rw) {
                iter = &rw->iter;
+               state = &rw->iter_state;
+               /*
+                * We come here from an earlier attempt, restore our state to
+                * match in case it doesn't. It's cheap enough that we don't
+                * need to make this conditional.
+                */
+               iov_iter_restore(iter, state);
                iovec = NULL;
        } else {
                ret = io_import_iovec(READ, req, &iovec, iter, !force_nonblock);
                if (ret < 0)
                        return ret;
+               state = &__state;
+               iov_iter_save_state(iter, state);
        }
-       io_size = iov_iter_count(iter);
-       req->result = io_size;
+       req->result = iov_iter_count(iter);
  
        /* Ensure we clear previously set non-block flag */
        if (!force_nonblock)
                return ret ?: -EAGAIN;
        }
  
-       ret = rw_verify_area(READ, req->file, io_kiocb_ppos(kiocb), io_size);
+       ret = rw_verify_area(READ, req->file, io_kiocb_ppos(kiocb), req->result);
        if (unlikely(ret)) {
                kfree(iovec);
                return ret;
                /* no retry on NONBLOCK nor RWF_NOWAIT */
                if (req->flags & REQ_F_NOWAIT)
                        goto done;
-               /* some cases will consume bytes even on error returns */
-               iov_iter_reexpand(iter, iter->count + iter->truncated);
-               iov_iter_revert(iter, io_size - iov_iter_count(iter));
                ret = 0;
        } else if (ret == -EIOCBQUEUED) {
                goto out_free;
-       } else if (ret <= 0 || ret == io_size || !force_nonblock ||
+       } else if (ret <= 0 || ret == req->result || !force_nonblock ||
                   (req->flags & REQ_F_NOWAIT) || !need_read_all(req)) {
                /* read all, failed, already did sync or don't want to retry */
                goto done;
        }
  
+       /*
+        * Don't depend on the iter state matching what was consumed, or being
+        * untouched in case of error. Restore it and we'll advance it
+        * manually if we need to.
+        */
+       iov_iter_restore(iter, state);
        ret2 = io_setup_async_rw(req, iovec, inline_vecs, iter, true);
        if (ret2)
                return ret2;
  
        iovec = NULL;
        rw = req->async_data;
-       /* now use our persistent iterator, if we aren't already */
-       iter = &rw->iter;
+       /*
+        * Now use our persistent iterator and state, if we aren't already.
+        * We've restored and mapped the iter to match.
+        */
+       if (iter != &rw->iter) {
+               iter = &rw->iter;
+               state = &rw->iter_state;
+       }
  
        do {
-               io_size -= ret;
+               /*
+                * We end up here because of a partial read, either from
+                * above or inside this loop. Advance the iter by the bytes
+                * that were consumed.
+                */
+               iov_iter_advance(iter, ret);
+               if (!iov_iter_count(iter))
+                       break;
                rw->bytes_done += ret;
+               iov_iter_save_state(iter, state);
                /* if we can retry, do so with the callbacks armed */
                if (!io_rw_should_retry(req)) {
                        kiocb->ki_flags &= ~IOCB_WAITQ;
                        return 0;
                /* we got some bytes, but not all. retry. */
                kiocb->ki_flags &= ~IOCB_WAITQ;
-       } while (ret > 0 && ret < io_size);
+               iov_iter_restore(iter, state);
+       } while (ret > 0);
  done:
        kiocb_done(kiocb, ret, issue_flags);
  out_free:
@@@ -3548,7 -3565,7 +3579,7 @@@ static int io_write_prep(struct io_kioc
  {
        if (unlikely(!(req->file->f_mode & FMODE_WRITE)))
                return -EBADF;
 -      return io_prep_rw(req, sqe);
 +      return io_prep_rw(req, sqe, WRITE);
  }
  
  static int io_write(struct io_kiocb *req, unsigned int issue_flags)
        struct kiocb *kiocb = &req->rw.kiocb;
        struct iov_iter __iter, *iter = &__iter;
        struct io_async_rw *rw = req->async_data;
-       ssize_t ret, ret2, io_size;
        bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+       struct iov_iter_state __state, *state;
+       ssize_t ret, ret2;
  
        if (rw) {
                iter = &rw->iter;
+               state = &rw->iter_state;
+               iov_iter_restore(iter, state);
                iovec = NULL;
        } else {
                ret = io_import_iovec(WRITE, req, &iovec, iter, !force_nonblock);
                if (ret < 0)
                        return ret;
+               state = &__state;
+               iov_iter_save_state(iter, state);
        }
-       io_size = iov_iter_count(iter);
-       req->result = io_size;
+       req->result = iov_iter_count(iter);
+       ret2 = 0;
  
        /* Ensure we clear previously set non-block flag */
        if (!force_nonblock)
            (req->flags & REQ_F_ISREG))
                goto copy_iov;
  
-       ret = rw_verify_area(WRITE, req->file, io_kiocb_ppos(kiocb), io_size);
+       ret = rw_verify_area(WRITE, req->file, io_kiocb_ppos(kiocb), req->result);
        if (unlikely(ret))
                goto out_free;
  
@@@ -3633,9 -3655,9 +3669,9 @@@ done
                kiocb_done(kiocb, ret2, issue_flags);
        } else {
  copy_iov:
-               /* some cases will consume bytes even on error returns */
-               iov_iter_reexpand(iter, iter->count + iter->truncated);
-               iov_iter_revert(iter, io_size - iov_iter_count(iter));
+               iov_iter_restore(iter, state);
+               if (ret2 > 0)
+                       iov_iter_advance(iter, ret2);
                ret = io_setup_async_rw(req, iovec, inline_vecs, iter, false);
                return ret ?: -EAGAIN;
        }
@@@ -7524,14 -7546,6 +7560,14 @@@ static int io_cqring_wait(struct io_rin
                        break;
        } while (1);
  
 +      if (uts) {
 +              struct timespec64 ts;
 +
 +              if (get_timespec64(&ts, uts))
 +                      return -EFAULT;
 +              timeout = timespec64_to_jiffies(&ts);
 +      }
 +
        if (sig) {
  #ifdef CONFIG_COMPAT
                if (in_compat_syscall())
                        return ret;
        }
  
 -      if (uts) {
 -              struct timespec64 ts;
 -
 -              if (get_timespec64(&ts, uts))
 -                      return -EFAULT;
 -              timeout = timespec64_to_jiffies(&ts);
 -      }
 -
        init_waitqueue_func_entry(&iowq.wq, io_wake_function);
        iowq.wq.private = current;
        INIT_LIST_HEAD(&iowq.wq.entry);
@@@ -8293,27 -8315,11 +8329,27 @@@ static int io_sqe_file_register(struct 
  #endif
  }
  
 +static int io_queue_rsrc_removal(struct io_rsrc_data *data, unsigned idx,
 +                               struct io_rsrc_node *node, void *rsrc)
 +{
 +      struct io_rsrc_put *prsrc;
 +
 +      prsrc = kzalloc(sizeof(*prsrc), GFP_KERNEL);
 +      if (!prsrc)
 +              return -ENOMEM;
 +
 +      prsrc->tag = *io_get_tag_slot(data, idx);
 +      prsrc->rsrc = rsrc;
 +      list_add(&prsrc->list, &node->rsrc_list);
 +      return 0;
 +}
 +
  static int io_install_fixed_file(struct io_kiocb *req, struct file *file,
                                 unsigned int issue_flags, u32 slot_index)
  {
        struct io_ring_ctx *ctx = req->ctx;
        bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
 +      bool needs_switch = false;
        struct io_fixed_file *file_slot;
        int ret = -EBADF;
  
  
        slot_index = array_index_nospec(slot_index, ctx->nr_user_files);
        file_slot = io_fixed_file_slot(&ctx->file_table, slot_index);
 -      ret = -EBADF;
 -      if (file_slot->file_ptr)
 -              goto err;
 +
 +      if (file_slot->file_ptr) {
 +              struct file *old_file;
 +
 +              ret = io_rsrc_node_switch_start(ctx);
 +              if (ret)
 +                      goto err;
 +
 +              old_file = (struct file *)(file_slot->file_ptr & FFS_MASK);
 +              ret = io_queue_rsrc_removal(ctx->file_data, slot_index,
 +                                          ctx->rsrc_node, old_file);
 +              if (ret)
 +                      goto err;
 +              file_slot->file_ptr = 0;
 +              needs_switch = true;
 +      }
  
        *io_get_tag_slot(ctx->file_data, slot_index) = 0;
        io_fixed_file_set(file_slot, file);
  
        ret = 0;
  err:
 +      if (needs_switch)
 +              io_rsrc_node_switch(ctx, ctx->file_data);
        io_ring_submit_unlock(ctx, !force_nonblock);
        if (ret)
                fput(file);
        return ret;
  }
  
 -static int io_queue_rsrc_removal(struct io_rsrc_data *data, unsigned idx,
 -                               struct io_rsrc_node *node, void *rsrc)
 -{
 -      struct io_rsrc_put *prsrc;
 -
 -      prsrc = kzalloc(sizeof(*prsrc), GFP_KERNEL);
 -      if (!prsrc)
 -              return -ENOMEM;
 -
 -      prsrc->tag = *io_get_tag_slot(data, idx);
 -      prsrc->rsrc = rsrc;
 -      list_add(&prsrc->list, &node->rsrc_list);
 -      return 0;
 -}
 -
  static int __io_sqe_files_update(struct io_ring_ctx *ctx,
                                 struct io_uring_rsrc_update2 *up,
                                 unsigned nr_args)
@@@ -10580,17 -10586,8 +10616,17 @@@ static int io_register_iowq_max_workers
        if (ctx->flags & IORING_SETUP_SQPOLL) {
                sqd = ctx->sq_data;
                if (sqd) {
 +                      /*
 +                       * Observe the correct sqd->lock -> ctx->uring_lock
 +                       * ordering. Fine to drop uring_lock here, we hold
 +                       * a ref to the ctx.
 +                       */
 +                      refcount_inc(&sqd->refs);
 +                      mutex_unlock(&ctx->uring_lock);
                        mutex_lock(&sqd->lock);
 -                      tctx = sqd->thread->io_uring;
 +                      mutex_lock(&ctx->uring_lock);
 +                      if (sqd->thread)
 +                              tctx = sqd->thread->io_uring;
                }
        } else {
                tctx = current->io_uring;
        if (ret)
                goto err;
  
 -      if (sqd)
 +      if (sqd) {
                mutex_unlock(&sqd->lock);
 +              io_put_sq_data(sqd);
 +      }
  
        if (copy_to_user(arg, new_count, sizeof(new_count)))
                return -EFAULT;
  
        return 0;
  err:
 -      if (sqd)
 +      if (sqd) {
                mutex_unlock(&sqd->lock);
 +              io_put_sq_data(sqd);
 +      }
        return ret;
  }
  
@@@ -10896,7 -10889,7 +10932,7 @@@ static int __init io_uring_init(void
        BUILD_BUG_ON(SQE_VALID_FLAGS >= (1 << 8));
  
        BUILD_BUG_ON(ARRAY_SIZE(io_op_defs) != IORING_OP_LAST);
 -      BUILD_BUG_ON(__REQ_F_LAST_BIT >= 8 * sizeof(int));
 +      BUILD_BUG_ON(__REQ_F_LAST_BIT > 8 * sizeof(int));
  
        req_cachep = KMEM_CACHE(io_kiocb, SLAB_HWCACHE_ALIGN | SLAB_PANIC |
                                SLAB_ACCOUNT);
This page took 0.106443 seconds and 4 git commands to generate.