]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.4
authorSasha Levin <sashal@kernel.org>
Thu, 2 Jul 2020 01:39:07 +0000 (21:39 -0400)
committerSasha Levin <sashal@kernel.org>
Thu, 2 Jul 2020 01:39:07 +0000 (21:39 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.4/io_uring-make-sure-async-workqueue-is-canceled-on-ex.patch [new file with mode: 0644]
queue-5.4/series [new file with mode: 0644]

diff --git a/queue-5.4/io_uring-make-sure-async-workqueue-is-canceled-on-ex.patch b/queue-5.4/io_uring-make-sure-async-workqueue-is-canceled-on-ex.patch
new file mode 100644 (file)
index 0000000..e956206
--- /dev/null
@@ -0,0 +1,193 @@
+From c2a3d09b76c634456af0073e038e28475386af0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jul 2020 21:30:14 -0400
+Subject: io_uring: make sure async workqueue is canceled on exit
+
+From: Jens Axboe <axboe@kernel.dk>
+
+Track async work items that we queue, so we can safely cancel them
+if the ring is closed or the process exits. Newer kernels handle
+this automatically with io-wq, but the old workqueue based setup needs
+a bit of special help to get there.
+
+There's no upstream variant of this, as that would require backporting
+all the io-wq changes from 5.5 and on. Hence I made a one-off that
+ensures that we don't leak memory if we have async work items that
+need active cancelation (like socket IO).
+
+Reported-by: Agarwal, Anchal <anchalag@amazon.com>
+Tested-by: Agarwal, Anchal <anchalag@amazon.com>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/io_uring.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 63 insertions(+)
+
+diff --git a/fs/io_uring.c b/fs/io_uring.c
+index 7fa3cd3fff4d2..e0200406765c3 100644
+--- a/fs/io_uring.c
++++ b/fs/io_uring.c
+@@ -267,6 +267,9 @@ struct io_ring_ctx {
+ #if defined(CONFIG_UNIX)
+       struct socket           *ring_sock;
+ #endif
++
++      struct list_head        task_list;
++      spinlock_t              task_lock;
+ };
+ struct sqe_submit {
+@@ -331,14 +334,18 @@ struct io_kiocb {
+ #define REQ_F_ISREG           2048    /* regular file */
+ #define REQ_F_MUST_PUNT               4096    /* must be punted even for NONBLOCK */
+ #define REQ_F_TIMEOUT_NOSEQ   8192    /* no timeout sequence */
++#define REQ_F_CANCEL          16384   /* cancel request */
+       unsigned long           fsize;
+       u64                     user_data;
+       u32                     result;
+       u32                     sequence;
++      struct task_struct      *task;
+       struct fs_struct        *fs;
+       struct work_struct      work;
++      struct task_struct      *work_task;
++      struct list_head        task_list;
+ };
+ #define IO_PLUG_THRESHOLD             2
+@@ -425,6 +432,8 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
+       INIT_LIST_HEAD(&ctx->cancel_list);
+       INIT_LIST_HEAD(&ctx->defer_list);
+       INIT_LIST_HEAD(&ctx->timeout_list);
++      INIT_LIST_HEAD(&ctx->task_list);
++      spin_lock_init(&ctx->task_lock);
+       return ctx;
+ }
+@@ -492,6 +501,7 @@ static void __io_commit_cqring(struct io_ring_ctx *ctx)
+ static inline void io_queue_async_work(struct io_ring_ctx *ctx,
+                                      struct io_kiocb *req)
+ {
++      unsigned long flags;
+       int rw = 0;
+       if (req->submit.sqe) {
+@@ -503,6 +513,13 @@ static inline void io_queue_async_work(struct io_ring_ctx *ctx,
+               }
+       }
++      req->task = current;
++
++      spin_lock_irqsave(&ctx->task_lock, flags);
++      list_add(&req->task_list, &ctx->task_list);
++      req->work_task = NULL;
++      spin_unlock_irqrestore(&ctx->task_lock, flags);
++
+       queue_work(ctx->sqo_wq[rw], &req->work);
+ }
+@@ -2201,6 +2218,8 @@ static void io_sq_wq_submit_work(struct work_struct *work)
+       old_cred = override_creds(ctx->creds);
+       async_list = io_async_list_from_sqe(ctx, req->submit.sqe);
++
++      allow_kernel_signal(SIGINT);
+ restart:
+       do {
+               struct sqe_submit *s = &req->submit;
+@@ -2232,6 +2251,12 @@ static void io_sq_wq_submit_work(struct work_struct *work)
+               }
+               if (!ret) {
++                      req->work_task = current;
++                      if (req->flags & REQ_F_CANCEL) {
++                              ret = -ECANCELED;
++                              goto end_req;
++                      }
++
+                       s->has_user = cur_mm != NULL;
+                       s->needs_lock = true;
+                       do {
+@@ -2246,6 +2271,12 @@ static void io_sq_wq_submit_work(struct work_struct *work)
+                                       break;
+                               cond_resched();
+                       } while (1);
++end_req:
++                      if (!list_empty(&req->task_list)) {
++                              spin_lock_irq(&ctx->task_lock);
++                              list_del_init(&req->task_list);
++                              spin_unlock_irq(&ctx->task_lock);
++                      }
+               }
+               /* drop submission reference */
+@@ -2311,6 +2342,7 @@ static void io_sq_wq_submit_work(struct work_struct *work)
+       }
+ out:
++      disallow_signal(SIGINT);
+       if (cur_mm) {
+               set_fs(old_fs);
+               unuse_mm(cur_mm);
+@@ -3675,12 +3707,32 @@ static int io_uring_fasync(int fd, struct file *file, int on)
+       return fasync_helper(fd, file, on, &ctx->cq_fasync);
+ }
++static void io_cancel_async_work(struct io_ring_ctx *ctx,
++                               struct task_struct *task)
++{
++      if (list_empty(&ctx->task_list))
++              return;
++
++      spin_lock_irq(&ctx->task_lock);
++      while (!list_empty(&ctx->task_list)) {
++              struct io_kiocb *req;
++
++              req = list_first_entry(&ctx->task_list, struct io_kiocb, task_list);
++              list_del_init(&req->task_list);
++              req->flags |= REQ_F_CANCEL;
++              if (req->work_task && (!task || req->task == task))
++                      send_sig(SIGINT, req->work_task, 1);
++      }
++      spin_unlock_irq(&ctx->task_lock);
++}
++
+ static void io_ring_ctx_wait_and_kill(struct io_ring_ctx *ctx)
+ {
+       mutex_lock(&ctx->uring_lock);
+       percpu_ref_kill(&ctx->refs);
+       mutex_unlock(&ctx->uring_lock);
++      io_cancel_async_work(ctx, NULL);
+       io_kill_timeouts(ctx);
+       io_poll_remove_all(ctx);
+       io_iopoll_reap_events(ctx);
+@@ -3688,6 +3740,16 @@ static void io_ring_ctx_wait_and_kill(struct io_ring_ctx *ctx)
+       io_ring_ctx_free(ctx);
+ }
++static int io_uring_flush(struct file *file, void *data)
++{
++      struct io_ring_ctx *ctx = file->private_data;
++
++      if (fatal_signal_pending(current) || (current->flags & PF_EXITING))
++              io_cancel_async_work(ctx, current);
++
++      return 0;
++}
++
+ static int io_uring_release(struct inode *inode, struct file *file)
+ {
+       struct io_ring_ctx *ctx = file->private_data;
+@@ -3792,6 +3854,7 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit,
+ static const struct file_operations io_uring_fops = {
+       .release        = io_uring_release,
++      .flush          = io_uring_flush,
+       .mmap           = io_uring_mmap,
+       .poll           = io_uring_poll,
+       .fasync         = io_uring_fasync,
+-- 
+2.25.1
+
diff --git a/queue-5.4/series b/queue-5.4/series
new file mode 100644 (file)
index 0000000..39598df
--- /dev/null
@@ -0,0 +1 @@
+io_uring-make-sure-async-workqueue-is-canceled-on-ex.patch