Merge tag 'io_uring-20190323' of git://git.kernel.dk/linux-block
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Mar 2019 17:25:12 +0000 (10:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Mar 2019 17:25:12 +0000 (10:25 -0700)
Pull io_uring fixes and improvements from Jens Axboe:
 "The first five in this series are heavily inspired by the work Al did
  on the aio side to fix the races there.

  The last two re-introduce a feature that was in io_uring before it got
  merged, but which I pulled since we didn't have a good way to have
  BVEC iters that already have a stable reference. These aren't
  necessarily related to block, it's just how io_uring pins fixed
  buffers"

* tag 'io_uring-20190323' of git://git.kernel.dk/linux-block:
  block: add BIO_NO_PAGE_REF flag
  iov_iter: add ITER_BVEC_FLAG_NO_REF flag
  io_uring: mark me as the maintainer
  io_uring: retry bulk slab allocs as single allocs
  io_uring: fix poll races
  io_uring: fix fget/fput handling
  io_uring: add prepped flag
  io_uring: make io_read/write return an integer
  io_uring: use regular request ref counts

1  2 
MAINTAINERS
fs/io_uring.c
include/linux/uio.h

diff --combined MAINTAINERS
index e17ebf70b5480ecc232ce1f62aedf95a03b5f403,a90137af48c2758ae232718fd8f53131e4a33204..3e5a5d263f2992b77c4bbc884969af279513ee19
@@@ -1059,8 -1059,6 +1059,8 @@@ L:      netdev@vger.kernel.or
  S:    Odd fixes
  F:    drivers/net/appletalk/
  F:    net/appletalk/
 +F:    include/linux/atalk.h
 +F:    include/uapi/linux/atalk.h
  
  APPLIED MICRO (APM) X-GENE DEVICE TREE SUPPORT
  M:    Khuong Dinh <khuong@os.amperecomputing.com>
@@@ -3204,7 -3202,6 +3204,7 @@@ F:      drivers/phy/broadcom/phy-brcm-usb
  BROADCOM GENET ETHERNET DRIVER
  M:    Doug Berger <opendmb@gmail.com>
  M:    Florian Fainelli <f.fainelli@gmail.com>
 +L:    bcm-kernel-feedback-list@broadcom.com
  L:    netdev@vger.kernel.org
  S:    Supported
  F:    drivers/net/ethernet/broadcom/genet/
@@@ -3312,7 -3309,6 +3312,7 @@@ F:      drivers/spi/spi-iproc-qspi.
  
  BROADCOM SYSTEMPORT ETHERNET DRIVER
  M:    Florian Fainelli <f.fainelli@gmail.com>
 +L:    bcm-kernel-feedback-list@broadcom.com
  L:    netdev@vger.kernel.org
  S:    Supported
  F:    drivers/net/ethernet/broadcom/bcmsysport.*
@@@ -3757,7 -3753,7 +3757,7 @@@ CHROME HARDWARE PLATFORM SUPPOR
  M:    Benson Leung <bleung@chromium.org>
  M:    Enric Balletbo i Serra <enric.balletbo@collabora.com>
  S:    Maintained
 -T:    git git://git.kernel.org/pub/scm/linux/kernel/git/bleung/chrome-platform.git
 +T:    git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git
  F:    drivers/platform/chrome/
  
  CHROMEOS EC SUBDRIVERS
@@@ -4645,11 -4641,10 +4645,11 @@@ S:   Maintaine
  F:    drivers/i2c/busses/i2c-diolan-u2c.c
  
  FILESYSTEM DIRECT ACCESS (DAX)
 -M:    Matthew Wilcox <willy@infradead.org>
 -M:    Ross Zwisler <zwisler@kernel.org>
 -M:    Jan Kara <jack@suse.cz>
 +M:    Dan Williams <dan.j.williams@intel.com>
 +R:    Matthew Wilcox <willy@infradead.org>
 +R:    Jan Kara <jack@suse.cz>
  L:    linux-fsdevel@vger.kernel.org
 +L:    linux-nvdimm@lists.01.org
  S:    Supported
  F:    fs/dax.c
  F:    include/linux/dax.h
@@@ -4657,9 -4652,9 +4657,9 @@@ F:      include/trace/events/fs_dax.
  
  DEVICE DIRECT ACCESS (DAX)
  M:    Dan Williams <dan.j.williams@intel.com>
 -M:    Dave Jiang <dave.jiang@intel.com>
 -M:    Ross Zwisler <zwisler@kernel.org>
  M:    Vishal Verma <vishal.l.verma@intel.com>
 +M:    Keith Busch <keith.busch@intel.com>
 +M:    Dave Jiang <dave.jiang@intel.com>
  L:    linux-nvdimm@lists.01.org
  S:    Supported
  F:    drivers/dax/
@@@ -5278,7 -5273,7 +5278,7 @@@ DRM DRIVERS FOR VIVANTE GPU I
  M:    Lucas Stach <l.stach@pengutronix.de>
  R:    Russell King <linux+etnaviv@armlinux.org.uk>
  R:    Christian Gmeiner <christian.gmeiner@gmail.com>
 -L:    etnaviv@lists.freedesktop.org
 +L:    etnaviv@lists.freedesktop.org (moderated for non-subscribers)
  L:    dri-devel@lists.freedesktop.org
  S:    Maintained
  F:    drivers/gpu/drm/etnaviv/
@@@ -8096,6 -8091,16 +8096,16 @@@ F:    include/linux/iommu.
  F:    include/linux/of_iommu.h
  F:    include/linux/iova.h
  
+ IO_URING
+ M:    Jens Axboe <axboe@kernel.dk>
+ L:    linux-block@vger.kernel.org
+ L:    linux-fsdevel@vger.kernel.org
+ T:    git git://git.kernel.dk/linux-block
+ T:    git git://git.kernel.dk/liburing
+ S:    Maintained
+ F:    fs/io_uring.c
+ F:    include/uapi/linux/io_uring.h
  IP MASQUERADING
  M:    Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar>
  S:    Maintained
@@@ -8461,7 -8466,6 +8471,7 @@@ F:      include/linux/kvm
  F:    include/kvm/iodev.h
  F:    virt/kvm/*
  F:    tools/kvm/
 +F:    tools/testing/selftests/kvm/
  
  KERNEL VIRTUAL MACHINE FOR AMD-V (KVM/amd)
  M:    Joerg Roedel <joro@8bytes.org>
@@@ -8471,25 -8475,29 +8481,25 @@@ S:   Maintaine
  F:    arch/x86/include/asm/svm.h
  F:    arch/x86/kvm/svm.c
  
 -KERNEL VIRTUAL MACHINE FOR ARM (KVM/arm)
 +KERNEL VIRTUAL MACHINE FOR ARM/ARM64 (KVM/arm, KVM/arm64)
  M:    Christoffer Dall <christoffer.dall@arm.com>
  M:    Marc Zyngier <marc.zyngier@arm.com>
 +R:    James Morse <james.morse@arm.com>
 +R:    Julien Thierry <julien.thierry@arm.com>
 +R:    Suzuki K Pouloze <suzuki.poulose@arm.com>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  L:    kvmarm@lists.cs.columbia.edu
  W:    http://systems.cs.columbia.edu/projects/kvm-arm
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git
 -S:    Supported
 +S:    Maintained
  F:    arch/arm/include/uapi/asm/kvm*
  F:    arch/arm/include/asm/kvm*
  F:    arch/arm/kvm/
 -F:    virt/kvm/arm/
 -F:    include/kvm/arm_*
 -
 -KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
 -M:    Christoffer Dall <christoffer.dall@arm.com>
 -M:    Marc Zyngier <marc.zyngier@arm.com>
 -L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 -L:    kvmarm@lists.cs.columbia.edu
 -S:    Maintained
  F:    arch/arm64/include/uapi/asm/kvm*
  F:    arch/arm64/include/asm/kvm*
  F:    arch/arm64/kvm/
 +F:    virt/kvm/arm/
 +F:    include/kvm/arm_*
  
  KERNEL VIRTUAL MACHINE FOR MIPS (KVM/mips)
  M:    James Hogan <jhogan@kernel.org>
@@@ -8812,6 -8820,7 +8822,6 @@@ S:      Maintaine
  F:    tools/lib/lockdep/
  
  LIBNVDIMM BLK: MMIO-APERTURE DRIVER
 -M:    Ross Zwisler <zwisler@kernel.org>
  M:    Dan Williams <dan.j.williams@intel.com>
  M:    Vishal Verma <vishal.l.verma@intel.com>
  M:    Dave Jiang <dave.jiang@intel.com>
@@@ -8824,6 -8833,7 +8834,6 @@@ F:      drivers/nvdimm/region_devs.
  LIBNVDIMM BTT: BLOCK TRANSLATION TABLE
  M:    Vishal Verma <vishal.l.verma@intel.com>
  M:    Dan Williams <dan.j.williams@intel.com>
 -M:    Ross Zwisler <zwisler@kernel.org>
  M:    Dave Jiang <dave.jiang@intel.com>
  L:    linux-nvdimm@lists.01.org
  Q:    https://patchwork.kernel.org/project/linux-nvdimm/list/
@@@ -8831,6 -8841,7 +8841,6 @@@ S:      Supporte
  F:    drivers/nvdimm/btt*
  
  LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER
 -M:    Ross Zwisler <zwisler@kernel.org>
  M:    Dan Williams <dan.j.williams@intel.com>
  M:    Vishal Verma <vishal.l.verma@intel.com>
  M:    Dave Jiang <dave.jiang@intel.com>
@@@ -8849,10 -8860,9 +8859,10 @@@ F:    Documentation/devicetree/bindings/pm
  
  LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM
  M:    Dan Williams <dan.j.williams@intel.com>
 -M:    Ross Zwisler <zwisler@kernel.org>
  M:    Vishal Verma <vishal.l.verma@intel.com>
  M:    Dave Jiang <dave.jiang@intel.com>
 +M:    Keith Busch <keith.busch@intel.com>
 +M:    Ira Weiny <ira.weiny@intel.com>
  L:    linux-nvdimm@lists.01.org
  Q:    https://patchwork.kernel.org/project/linux-nvdimm/list/
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git
diff --combined fs/io_uring.c
index c88088d92613595eecf6f1119a2c53f0b63bab27,c592a0933b0db917b7733a21d552b5f382c54053..6aaa30580a2b2057fca13404a44e0b3773450288
@@@ -189,17 -189,28 +189,28 @@@ struct sqe_submit 
        bool                            needs_fixed_file;
  };
  
+ /*
+  * First field must be the file pointer in all the
+  * iocb unions! See also 'struct kiocb' in <linux/fs.h>
+  */
  struct io_poll_iocb {
        struct file                     *file;
        struct wait_queue_head          *head;
        __poll_t                        events;
-       bool                            woken;
+       bool                            done;
        bool                            canceled;
        struct wait_queue_entry         wait;
  };
  
+ /*
+  * NOTE! Each of the iocb union members has the file pointer
+  * as the first entry in their struct definition. So you can
+  * access the file pointer through any of the sub-structs,
+  * or directly as just 'ki_filp' in this struct.
+  */
  struct io_kiocb {
        union {
+               struct file             *file;
                struct kiocb            rw;
                struct io_poll_iocb     poll;
        };
  #define REQ_F_IOPOLL_COMPLETED        2       /* polled IO has completed */
  #define REQ_F_FIXED_FILE      4       /* ctx owns file */
  #define REQ_F_SEQ_PREV                8       /* sequential with previous */
+ #define REQ_F_PREPPED         16      /* prep already done */
        u64                     user_data;
        u64                     error;
  
@@@ -355,20 -367,25 +367,25 @@@ static void io_cqring_fill_event(struc
        }
  }
  
- static void io_cqring_add_event(struct io_ring_ctx *ctx, u64 ki_user_data,
+ static void io_cqring_ev_posted(struct io_ring_ctx *ctx)
+ {
+       if (waitqueue_active(&ctx->wait))
+               wake_up(&ctx->wait);
+       if (waitqueue_active(&ctx->sqo_wait))
+               wake_up(&ctx->sqo_wait);
+ }
+ static void io_cqring_add_event(struct io_ring_ctx *ctx, u64 user_data,
                                long res, unsigned ev_flags)
  {
        unsigned long flags;
  
        spin_lock_irqsave(&ctx->completion_lock, flags);
-       io_cqring_fill_event(ctx, ki_user_data, res, ev_flags);
+       io_cqring_fill_event(ctx, user_data, res, ev_flags);
        io_commit_cqring(ctx);
        spin_unlock_irqrestore(&ctx->completion_lock, flags);
  
-       if (waitqueue_active(&ctx->wait))
-               wake_up(&ctx->wait);
-       if (waitqueue_active(&ctx->sqo_wait))
-               wake_up(&ctx->sqo_wait);
+       io_cqring_ev_posted(ctx);
  }
  
  static void io_ring_drop_ctx_refs(struct io_ring_ctx *ctx, unsigned refs)
  static struct io_kiocb *io_get_req(struct io_ring_ctx *ctx,
                                   struct io_submit_state *state)
  {
+       gfp_t gfp = GFP_KERNEL | __GFP_NOWARN;
        struct io_kiocb *req;
  
        if (!percpu_ref_tryget(&ctx->refs))
                return NULL;
  
        if (!state) {
-               req = kmem_cache_alloc(req_cachep, __GFP_NOWARN);
+               req = kmem_cache_alloc(req_cachep, gfp);
                if (unlikely(!req))
                        goto out;
        } else if (!state->free_reqs) {
                int ret;
  
                sz = min_t(size_t, state->ios_left, ARRAY_SIZE(state->reqs));
-               ret = kmem_cache_alloc_bulk(req_cachep, __GFP_NOWARN, sz,
-                                               state->reqs);
-               if (unlikely(ret <= 0))
-                       goto out;
+               ret = kmem_cache_alloc_bulk(req_cachep, gfp, sz, state->reqs);
+               /*
+                * Bulk alloc is all-or-nothing. If we fail to get a batch,
+                * retry single alloc to be on the safe side.
+                */
+               if (unlikely(ret <= 0)) {
+                       state->reqs[0] = kmem_cache_alloc(req_cachep, gfp);
+                       if (!state->reqs[0])
+                               goto out;
+                       ret = 1;
+               }
                state->free_reqs = ret - 1;
                state->cur_req = 1;
                req = state->reqs[0];
  
        req->ctx = ctx;
        req->flags = 0;
-       refcount_set(&req->refs, 0);
+       /* one is dropped after submission, the other at completion */
+       refcount_set(&req->refs, 2);
        return req;
  out:
        io_ring_drop_ctx_refs(ctx, 1);
@@@ -429,10 -456,16 +456,16 @@@ static void io_free_req_many(struct io_
  
  static void io_free_req(struct io_kiocb *req)
  {
-       if (!refcount_read(&req->refs) || refcount_dec_and_test(&req->refs)) {
-               io_ring_drop_ctx_refs(req->ctx, 1);
-               kmem_cache_free(req_cachep, req);
-       }
+       if (req->file && !(req->flags & REQ_F_FIXED_FILE))
+               fput(req->file);
+       io_ring_drop_ctx_refs(req->ctx, 1);
+       kmem_cache_free(req_cachep, req);
+ }
+ static void io_put_req(struct io_kiocb *req)
+ {
+       if (refcount_dec_and_test(&req->refs))
+               io_free_req(req);
  }
  
  /*
@@@ -442,44 -475,34 +475,34 @@@ static void io_iopoll_complete(struct i
                               struct list_head *done)
  {
        void *reqs[IO_IOPOLL_BATCH];
-       int file_count, to_free;
-       struct file *file = NULL;
        struct io_kiocb *req;
+       int to_free;
  
-       file_count = to_free = 0;
+       to_free = 0;
        while (!list_empty(done)) {
                req = list_first_entry(done, struct io_kiocb, list);
                list_del(&req->list);
  
                io_cqring_fill_event(ctx, req->user_data, req->error, 0);
-               reqs[to_free++] = req;
                (*nr_events)++;
  
-               /*
-                * Batched puts of the same file, to avoid dirtying the
-                * file usage count multiple times, if avoidable.
-                */
-               if (!(req->flags & REQ_F_FIXED_FILE)) {
-                       if (!file) {
-                               file = req->rw.ki_filp;
-                               file_count = 1;
-                       } else if (file == req->rw.ki_filp) {
-                               file_count++;
+               if (refcount_dec_and_test(&req->refs)) {
+                       /* If we're not using fixed files, we have to pair the
+                        * completion part with the file put. Use regular
+                        * completions for those, only batch free for fixed
+                        * file.
+                        */
+                       if (req->flags & REQ_F_FIXED_FILE) {
+                               reqs[to_free++] = req;
+                               if (to_free == ARRAY_SIZE(reqs))
+                                       io_free_req_many(ctx, reqs, &to_free);
                        } else {
-                               fput_many(file, file_count);
-                               file = req->rw.ki_filp;
-                               file_count = 1;
+                               io_free_req(req);
                        }
                }
-               if (to_free == ARRAY_SIZE(reqs))
-                       io_free_req_many(ctx, reqs, &to_free);
        }
-       io_commit_cqring(ctx);
  
-       if (file)
-               fput_many(file, file_count);
+       io_commit_cqring(ctx);
        io_free_req_many(ctx, reqs, &to_free);
  }
  
@@@ -602,21 -625,14 +625,14 @@@ static void kiocb_end_write(struct kioc
        }
  }
  
- static void io_fput(struct io_kiocb *req)
- {
-       if (!(req->flags & REQ_F_FIXED_FILE))
-               fput(req->rw.ki_filp);
- }
  static void io_complete_rw(struct kiocb *kiocb, long res, long res2)
  {
        struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw);
  
        kiocb_end_write(kiocb);
  
-       io_fput(req);
        io_cqring_add_event(req->ctx, req->user_data, res, 0);
-       io_free_req(req);
+       io_put_req(req);
  }
  
  static void io_complete_rw_iopoll(struct kiocb *kiocb, long res, long res2)
@@@ -731,31 -747,18 +747,18 @@@ static int io_prep_rw(struct io_kiocb *
        const struct io_uring_sqe *sqe = s->sqe;
        struct io_ring_ctx *ctx = req->ctx;
        struct kiocb *kiocb = &req->rw;
-       unsigned ioprio, flags;
-       int fd, ret;
+       unsigned ioprio;
+       int ret;
  
+       if (!req->file)
+               return -EBADF;
        /* For -EAGAIN retry, everything is already prepped */
-       if (kiocb->ki_filp)
+       if (req->flags & REQ_F_PREPPED)
                return 0;
  
-       flags = READ_ONCE(sqe->flags);
-       fd = READ_ONCE(sqe->fd);
+       if (force_nonblock && !io_file_supports_async(req->file))
+               force_nonblock = false;
  
-       if (flags & IOSQE_FIXED_FILE) {
-               if (unlikely(!ctx->user_files ||
-                   (unsigned) fd >= ctx->nr_user_files))
-                       return -EBADF;
-               kiocb->ki_filp = ctx->user_files[fd];
-               req->flags |= REQ_F_FIXED_FILE;
-       } else {
-               if (s->needs_fixed_file)
-                       return -EBADF;
-               kiocb->ki_filp = io_file_get(state, fd);
-               if (unlikely(!kiocb->ki_filp))
-                       return -EBADF;
-               if (force_nonblock && !io_file_supports_async(kiocb->ki_filp))
-                       force_nonblock = false;
-       }
        kiocb->ki_pos = READ_ONCE(sqe->off);
        kiocb->ki_flags = iocb_flags(kiocb->ki_filp);
        kiocb->ki_hint = ki_hint_validate(file_write_hint(kiocb->ki_filp));
        if (ioprio) {
                ret = ioprio_check_cap(ioprio);
                if (ret)
-                       goto out_fput;
+                       return ret;
  
                kiocb->ki_ioprio = ioprio;
        } else
  
        ret = kiocb_set_rw_flags(kiocb, READ_ONCE(sqe->rw_flags));
        if (unlikely(ret))
-               goto out_fput;
+               return ret;
        if (force_nonblock) {
                kiocb->ki_flags |= IOCB_NOWAIT;
                req->flags |= REQ_F_FORCE_NONBLOCK;
        }
        if (ctx->flags & IORING_SETUP_IOPOLL) {
-               ret = -EOPNOTSUPP;
                if (!(kiocb->ki_flags & IOCB_DIRECT) ||
                    !kiocb->ki_filp->f_op->iopoll)
-                       goto out_fput;
+                       return -EOPNOTSUPP;
  
                req->error = 0;
                kiocb->ki_flags |= IOCB_HIPRI;
                kiocb->ki_complete = io_complete_rw_iopoll;
        } else {
-               if (kiocb->ki_flags & IOCB_HIPRI) {
-                       ret = -EINVAL;
-                       goto out_fput;
-               }
+               if (kiocb->ki_flags & IOCB_HIPRI)
+                       return -EINVAL;
                kiocb->ki_complete = io_complete_rw;
        }
+       req->flags |= REQ_F_PREPPED;
        return 0;
- out_fput:
-       if (!(flags & IOSQE_FIXED_FILE)) {
-               /*
-                * in case of error, we didn't use this file reference. drop it.
-                */
-               if (state)
-                       state->used_refs--;
-               io_file_put(state, kiocb->ki_filp);
-       }
-       return ret;
  }
  
  static inline void io_rw_done(struct kiocb *kiocb, ssize_t ret)
@@@ -864,6 -855,9 +855,9 @@@ static int io_import_fixed(struct io_ri
        iov_iter_bvec(iter, rw, imu->bvec, imu->nr_bvecs, offset + len);
        if (offset)
                iov_iter_advance(iter, offset);
+       /* don't drop a reference to these pages */
+       iter->type |= ITER_BVEC_FLAG_NO_REF;
        return 0;
  }
  
@@@ -887,7 -881,7 +881,7 @@@ static int io_import_iovec(struct io_ri
        opcode = READ_ONCE(sqe->opcode);
        if (opcode == IORING_OP_READ_FIXED ||
            opcode == IORING_OP_WRITE_FIXED) {
-               ssize_t ret = io_import_fixed(ctx, rw, sqe, iter);
+               int ret = io_import_fixed(ctx, rw, sqe, iter);
                *iovec = NULL;
                return ret;
        }
@@@ -923,7 -917,7 +917,7 @@@ static void io_async_list_note(int rw, 
                /* Use 8x RA size as a decent limiter for both reads/writes */
                max_pages = filp->f_ra.ra_pages;
                if (!max_pages)
 -                      max_pages = VM_MAX_READAHEAD >> (PAGE_SHIFT - 10);
 +                      max_pages = VM_READAHEAD_PAGES;
                max_pages *= 8;
  
                /* If max pages are exceeded, reset the state */
        async_list->io_end = io_end;
  }
  
- static ssize_t io_read(struct io_kiocb *req, const struct sqe_submit *s,
-                      bool force_nonblock, struct io_submit_state *state)
+ static int io_read(struct io_kiocb *req, const struct sqe_submit *s,
+                  bool force_nonblock, struct io_submit_state *state)
  {
        struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
        struct kiocb *kiocb = &req->rw;
        struct iov_iter iter;
        struct file *file;
        size_t iov_count;
-       ssize_t ret;
+       int ret;
  
        ret = io_prep_rw(req, s, force_nonblock, state);
        if (ret)
                return ret;
        file = kiocb->ki_filp;
  
-       ret = -EBADF;
        if (unlikely(!(file->f_mode & FMODE_READ)))
-               goto out_fput;
-       ret = -EINVAL;
+               return -EBADF;
        if (unlikely(!file->f_op->read_iter))
-               goto out_fput;
+               return -EINVAL;
  
        ret = io_import_iovec(req->ctx, READ, s, &iovec, &iter);
        if (ret)
-               goto out_fput;
+               return ret;
  
        iov_count = iov_iter_count(&iter);
        ret = rw_verify_area(READ, file, &kiocb->ki_pos, iov_count);
                }
        }
        kfree(iovec);
- out_fput:
-       /* Hold on to the file for -EAGAIN */
-       if (unlikely(ret && ret != -EAGAIN))
-               io_fput(req);
        return ret;
  }
  
- static ssize_t io_write(struct io_kiocb *req, const struct sqe_submit *s,
-                       bool force_nonblock, struct io_submit_state *state)
+ static int io_write(struct io_kiocb *req, const struct sqe_submit *s,
+                   bool force_nonblock, struct io_submit_state *state)
  {
        struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
        struct kiocb *kiocb = &req->rw;
        struct iov_iter iter;
        struct file *file;
        size_t iov_count;
-       ssize_t ret;
+       int ret;
  
        ret = io_prep_rw(req, s, force_nonblock, state);
        if (ret)
                return ret;
  
-       ret = -EBADF;
        file = kiocb->ki_filp;
        if (unlikely(!(file->f_mode & FMODE_WRITE)))
-               goto out_fput;
-       ret = -EINVAL;
+               return -EBADF;
        if (unlikely(!file->f_op->write_iter))
-               goto out_fput;
+               return -EINVAL;
  
        ret = io_import_iovec(req->ctx, WRITE, s, &iovec, &iter);
        if (ret)
-               goto out_fput;
+               return ret;
  
        iov_count = iov_iter_count(&iter);
  
        }
  out_free:
        kfree(iovec);
- out_fput:
-       /* Hold on to the file for -EAGAIN */
-       if (unlikely(ret && ret != -EAGAIN))
-               io_fput(req);
        return ret;
  }
  
@@@ -1072,29 -1054,19 +1054,19 @@@ static int io_nop(struct io_kiocb *req
        if (unlikely(ctx->flags & IORING_SETUP_IOPOLL))
                return -EINVAL;
  
-       /*
-        * Twilight zone - it's possible that someone issued an opcode that
-        * has a file attached, then got -EAGAIN on submission, and changed
-        * the sqe before we retried it from async context. Avoid dropping
-        * a file reference for this malicious case, and flag the error.
-        */
-       if (req->rw.ki_filp) {
-               err = -EBADF;
-               io_fput(req);
-       }
        io_cqring_add_event(ctx, user_data, err, 0);
-       io_free_req(req);
+       io_put_req(req);
        return 0;
  }
  
  static int io_prep_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe)
  {
        struct io_ring_ctx *ctx = req->ctx;
-       unsigned flags;
-       int fd;
  
-       /* Prep already done */
-       if (req->rw.ki_filp)
+       if (!req->file)
+               return -EBADF;
+       /* Prep already done (EAGAIN retry) */
+       if (req->flags & REQ_F_PREPPED)
                return 0;
  
        if (unlikely(ctx->flags & IORING_SETUP_IOPOLL))
        if (unlikely(sqe->addr || sqe->ioprio || sqe->buf_index))
                return -EINVAL;
  
-       fd = READ_ONCE(sqe->fd);
-       flags = READ_ONCE(sqe->flags);
-       if (flags & IOSQE_FIXED_FILE) {
-               if (unlikely(!ctx->user_files || fd >= ctx->nr_user_files))
-                       return -EBADF;
-               req->rw.ki_filp = ctx->user_files[fd];
-               req->flags |= REQ_F_FIXED_FILE;
-       } else {
-               req->rw.ki_filp = fget(fd);
-               if (unlikely(!req->rw.ki_filp))
-                       return -EBADF;
-       }
+       req->flags |= REQ_F_PREPPED;
        return 0;
  }
  
@@@ -1144,9 -1103,8 +1103,8 @@@ static int io_fsync(struct io_kiocb *re
                                end > 0 ? end : LLONG_MAX,
                                fsync_flags & IORING_FSYNC_DATASYNC);
  
-       io_fput(req);
        io_cqring_add_event(req->ctx, sqe->user_data, ret, 0);
-       io_free_req(req);
+       io_put_req(req);
        return 0;
  }
  
@@@ -1204,15 -1162,16 +1162,16 @@@ static int io_poll_remove(struct io_kio
        spin_unlock_irq(&ctx->completion_lock);
  
        io_cqring_add_event(req->ctx, sqe->user_data, ret, 0);
-       io_free_req(req);
+       io_put_req(req);
        return 0;
  }
  
- static void io_poll_complete(struct io_kiocb *req, __poll_t mask)
+ static void io_poll_complete(struct io_ring_ctx *ctx, struct io_kiocb *req,
+                            __poll_t mask)
  {
-       io_cqring_add_event(req->ctx, req->user_data, mangle_poll(mask), 0);
-       io_fput(req);
-       io_free_req(req);
+       req->poll.done = true;
+       io_cqring_fill_event(ctx, req->user_data, mangle_poll(mask), 0);
+       io_commit_cqring(ctx);
  }
  
  static void io_poll_complete_work(struct work_struct *work)
                return;
        }
        list_del_init(&req->list);
+       io_poll_complete(ctx, req, mask);
        spin_unlock_irq(&ctx->completion_lock);
  
-       io_poll_complete(req, mask);
+       io_cqring_ev_posted(ctx);
+       io_put_req(req);
  }
  
  static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
        struct io_kiocb *req = container_of(poll, struct io_kiocb, poll);
        struct io_ring_ctx *ctx = req->ctx;
        __poll_t mask = key_to_poll(key);
-       poll->woken = true;
+       unsigned long flags;
  
        /* for instances that support it check for an event match first: */
-       if (mask) {
-               unsigned long flags;
+       if (mask && !(mask & poll->events))
+               return 0;
  
-               if (!(mask & poll->events))
-                       return 0;
+       list_del_init(&poll->wait.entry);
  
-               /* try to complete the iocb inline if we can: */
-               if (spin_trylock_irqsave(&ctx->completion_lock, flags)) {
-                       list_del(&req->list);
-                       spin_unlock_irqrestore(&ctx->completion_lock, flags);
+       if (mask && spin_trylock_irqsave(&ctx->completion_lock, flags)) {
+               list_del(&req->list);
+               io_poll_complete(ctx, req, mask);
+               spin_unlock_irqrestore(&ctx->completion_lock, flags);
  
-                       list_del_init(&poll->wait.entry);
-                       io_poll_complete(req, mask);
-                       return 1;
-               }
+               io_cqring_ev_posted(ctx);
+               io_put_req(req);
+       } else {
+               queue_work(ctx->sqo_wq, &req->work);
        }
  
-       list_del_init(&poll->wait.entry);
-       queue_work(ctx->sqo_wq, &req->work);
        return 1;
  }
  
@@@ -1305,36 -1262,23 +1262,23 @@@ static int io_poll_add(struct io_kiocb 
        struct io_poll_iocb *poll = &req->poll;
        struct io_ring_ctx *ctx = req->ctx;
        struct io_poll_table ipt;
-       unsigned flags;
+       bool cancel = false;
        __poll_t mask;
        u16 events;
-       int fd;
  
        if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
                return -EINVAL;
        if (sqe->addr || sqe->ioprio || sqe->off || sqe->len || sqe->buf_index)
                return -EINVAL;
+       if (!poll->file)
+               return -EBADF;
  
        INIT_WORK(&req->work, io_poll_complete_work);
        events = READ_ONCE(sqe->poll_events);
        poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP;
  
-       flags = READ_ONCE(sqe->flags);
-       fd = READ_ONCE(sqe->fd);
-       if (flags & IOSQE_FIXED_FILE) {
-               if (unlikely(!ctx->user_files || fd >= ctx->nr_user_files))
-                       return -EBADF;
-               poll->file = ctx->user_files[fd];
-               req->flags |= REQ_F_FIXED_FILE;
-       } else {
-               poll->file = fget(fd);
-       }
-       if (unlikely(!poll->file))
-               return -EBADF;
        poll->head = NULL;
-       poll->woken = false;
+       poll->done = false;
        poll->canceled = false;
  
        ipt.pt._qproc = io_poll_queue_proc;
        INIT_LIST_HEAD(&poll->wait.entry);
        init_waitqueue_func_entry(&poll->wait, io_poll_wake);
  
-       /* one for removal from waitqueue, one for this function */
-       refcount_set(&req->refs, 2);
        mask = vfs_poll(poll->file, &ipt.pt) & poll->events;
-       if (unlikely(!poll->head)) {
-               /* we did not manage to set up a waitqueue, done */
-               goto out;
-       }
  
        spin_lock_irq(&ctx->completion_lock);
-       spin_lock(&poll->head->lock);
-       if (poll->woken) {
-               /* wake_up context handles the rest */
-               mask = 0;
+       if (likely(poll->head)) {
+               spin_lock(&poll->head->lock);
+               if (unlikely(list_empty(&poll->wait.entry))) {
+                       if (ipt.error)
+                               cancel = true;
+                       ipt.error = 0;
+                       mask = 0;
+               }
+               if (mask || ipt.error)
+                       list_del_init(&poll->wait.entry);
+               else if (cancel)
+                       WRITE_ONCE(poll->canceled, true);
+               else if (!poll->done) /* actually waiting for an event */
+                       list_add_tail(&req->list, &ctx->cancel_list);
+               spin_unlock(&poll->head->lock);
+       }
+       if (mask) { /* no async, we'd stolen it */
+               req->error = mangle_poll(mask);
                ipt.error = 0;
-       } else if (mask || ipt.error) {
-               /* if we get an error or a mask we are done */
-               WARN_ON_ONCE(list_empty(&poll->wait.entry));
-               list_del_init(&poll->wait.entry);
-       } else {
-               /* actually waiting for an event */
-               list_add_tail(&req->list, &ctx->cancel_list);
+               io_poll_complete(ctx, req, mask);
        }
-       spin_unlock(&poll->head->lock);
        spin_unlock_irq(&ctx->completion_lock);
  
- out:
-       if (unlikely(ipt.error)) {
-               if (!(flags & IOSQE_FIXED_FILE))
-                       fput(poll->file);
-               /*
-                * Drop one of our refs to this req, __io_submit_sqe() will
-                * drop the other one since we're returning an error.
-                */
-               io_free_req(req);
-               return ipt.error;
+       if (mask) {
+               io_cqring_ev_posted(ctx);
+               io_put_req(req);
        }
-       if (mask)
-               io_poll_complete(req, mask);
-       io_free_req(req);
-       return 0;
+       return ipt.error;
  }
  
  static int __io_submit_sqe(struct io_ring_ctx *ctx, struct io_kiocb *req,
                           const struct sqe_submit *s, bool force_nonblock,
                           struct io_submit_state *state)
  {
-       ssize_t ret;
-       int opcode;
+       int ret, opcode;
  
        if (unlikely(s->index >= ctx->sq_entries))
                return -EINVAL;
@@@ -1524,10 -1456,13 +1456,13 @@@ restart
                                        break;
                                cond_resched();
                        } while (1);
+                       /* drop submission reference */
+                       io_put_req(req);
                }
                if (ret) {
                        io_cqring_add_event(ctx, sqe->user_data, ret, 0);
-                       io_free_req(req);
+                       io_put_req(req);
                }
  
                /* async context always use a copy of the sqe */
@@@ -1614,11 -1549,55 +1549,55 @@@ static bool io_add_to_prev_work(struct 
        return ret;
  }
  
+ static bool io_op_needs_file(const struct io_uring_sqe *sqe)
+ {
+       int op = READ_ONCE(sqe->opcode);
+       switch (op) {
+       case IORING_OP_NOP:
+       case IORING_OP_POLL_REMOVE:
+               return false;
+       default:
+               return true;
+       }
+ }
+ static int io_req_set_file(struct io_ring_ctx *ctx, const struct sqe_submit *s,
+                          struct io_submit_state *state, struct io_kiocb *req)
+ {
+       unsigned flags;
+       int fd;
+       flags = READ_ONCE(s->sqe->flags);
+       fd = READ_ONCE(s->sqe->fd);
+       if (!io_op_needs_file(s->sqe)) {
+               req->file = NULL;
+               return 0;
+       }
+       if (flags & IOSQE_FIXED_FILE) {
+               if (unlikely(!ctx->user_files ||
+                   (unsigned) fd >= ctx->nr_user_files))
+                       return -EBADF;
+               req->file = ctx->user_files[fd];
+               req->flags |= REQ_F_FIXED_FILE;
+       } else {
+               if (s->needs_fixed_file)
+                       return -EBADF;
+               req->file = io_file_get(state, fd);
+               if (unlikely(!req->file))
+                       return -EBADF;
+       }
+       return 0;
+ }
  static int io_submit_sqe(struct io_ring_ctx *ctx, struct sqe_submit *s,
                         struct io_submit_state *state)
  {
        struct io_kiocb *req;
-       ssize_t ret;
+       int ret;
  
        /* enforce forwards compatibility on users */
        if (unlikely(s->sqe->flags & ~IOSQE_FIXED_FILE))
        if (unlikely(!req))
                return -EAGAIN;
  
-       req->rw.ki_filp = NULL;
+       ret = io_req_set_file(ctx, s, state, req);
+       if (unlikely(ret))
+               goto out;
  
        ret = __io_submit_sqe(ctx, req, s, true, state);
        if (ret == -EAGAIN) {
                                INIT_WORK(&req->work, io_sq_wq_submit_work);
                                queue_work(ctx->sqo_wq, &req->work);
                        }
-                       ret = 0;
+                       /*
+                        * Queued up for async execution, worker will release
+                        * submit reference when the iocb is actually
+                        * submitted.
+                        */
+                       return 0;
                }
        }
+ out:
+       /* drop submission reference */
+       io_put_req(req);
+       /* and drop final reference, if we failed */
        if (ret)
-               io_free_req(req);
+               io_put_req(req);
  
        return ret;
  }
diff --combined include/linux/uio.h
index 87477e1640f9217223f7cbcde6b3fa416ef58ac5,4e926641fa801123dccef0b9eb07346ffbbdc09f..f184af1999a8e8c9f8216eb7aa64a689889c66a6
@@@ -23,14 -23,23 +23,23 @@@ struct kvec 
  };
  
  enum iter_type {
-       ITER_IOVEC = 0,
-       ITER_KVEC = 2,
-       ITER_BVEC = 4,
-       ITER_PIPE = 8,
-       ITER_DISCARD = 16,
+       /* set if ITER_BVEC doesn't hold a bv_page ref */
+       ITER_BVEC_FLAG_NO_REF = 2,
+       /* iter types */
+       ITER_IOVEC = 4,
+       ITER_KVEC = 8,
+       ITER_BVEC = 16,
+       ITER_PIPE = 32,
+       ITER_DISCARD = 64,
  };
  
  struct iov_iter {
+       /*
+        * Bit 0 is the read/write bit, set if we're writing.
+        * Bit 1 is the BVEC_FLAG_NO_REF bit, set if type is a bvec and
+        * the caller isn't expecting to drop a page reference when done.
+        */
        unsigned int type;
        size_t iov_offset;
        size_t count;
@@@ -84,6 -93,11 +93,11 @@@ static inline unsigned char iov_iter_rw
        return i->type & (READ | WRITE);
  }
  
+ static inline bool iov_iter_bvec_no_ref(const struct iov_iter *i)
+ {
+       return (i->type & ITER_BVEC_FLAG_NO_REF) != 0;
+ }
  /*
   * Total number of bytes covered by an iovec.
   *
@@@ -110,6 -124,14 +124,6 @@@ static inline struct iovec iov_iter_iov
        };
  }
  
 -#define iov_for_each(iov, iter, start)                                \
 -      if (iov_iter_type(start) == ITER_IOVEC ||               \
 -          iov_iter_type(start) == ITER_KVEC)                  \
 -      for (iter = (start);                                    \
 -           (iter).count &&                                    \
 -           ((iov = iov_iter_iovec(&(iter))), 1);              \
 -           iov_iter_advance(&(iter), (iov).iov_len))
 -
  size_t iov_iter_copy_from_user_atomic(struct page *page,
                struct iov_iter *i, unsigned long offset, size_t bytes);
  void iov_iter_advance(struct iov_iter *i, size_t bytes);