From: Stefan Roesch <shr@fb.com>
To: <io-uring@vger.kernel.org>, <linux-fsdevel@vger.kernel.org>,
<linux-block@vger.kernel.org>, <kernel-team@fb.com>
Cc: <shr@fb.com>
Subject: [PATCH v1 13/14] io_uring: support write throttling for async buffered writes
Date: Mon, 14 Feb 2022 09:44:02 -0800 [thread overview]
Message-ID: <20220214174403.4147994-14-shr@fb.com> (raw)
In-Reply-To: <20220214174403.4147994-1-shr@fb.com>
This adds the process-level throttling for the block layer for async
buffered writes to io-uring.In io_write the code now checks if the write
needs to be throttled. If this is required, it adds the request to the
list of pending io requests and starts a timer. After the timer expires,
it submits the list of pending writes.
- Add new list called pending_ios for delayed writes (throttled writes)
to struct io_uring_task. The list is protected by the task_lock spin
lock.
- Add new timer to struct io_uring_task.
Signed-off-by: Stefan Roesch <shr@fb.com>
---
fs/io_uring.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 91 insertions(+), 7 deletions(-)
diff --git a/fs/io_uring.c b/fs/io_uring.c
index 507f28b5b2bb..7bb77700ffac 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -461,6 +461,11 @@ struct io_ring_ctx {
};
};
+struct pending_list {
+ struct list_head list;
+ struct io_kiocb *req;
+};
+
struct io_uring_task {
/* submission side */
int cached_refs;
@@ -477,6 +482,9 @@ struct io_uring_task {
struct io_wq_work_list prior_task_list;
struct callback_head task_work;
bool task_running;
+
+ struct pending_list pending_ios;
+ struct timer_list timer;
};
/*
@@ -1134,13 +1142,14 @@ static void io_rsrc_put_work(struct work_struct *work);
static void io_req_task_queue(struct io_kiocb *req);
static void __io_submit_flush_completions(struct io_ring_ctx *ctx);
-static int io_req_prep_async(struct io_kiocb *req);
+static int io_req_prep_async(struct io_kiocb *req, bool force);
static int io_install_fixed_file(struct io_kiocb *req, struct file *file,
unsigned int issue_flags, u32 slot_index);
static int io_close_fixed(struct io_kiocb *req, unsigned int issue_flags);
static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer);
+static void delayed_write_fn(struct timer_list *tmr);
static struct kmem_cache *req_cachep;
@@ -2462,6 +2471,31 @@ static void io_req_task_queue_reissue(struct io_kiocb *req)
io_req_task_work_add(req, false);
}
+static int io_req_task_queue_reissue_delayed(struct io_kiocb *req)
+{
+ struct io_uring_task *tctx = req->task->io_uring;
+ struct pending_list *pending = kmalloc(sizeof(struct pending_list), GFP_KERNEL);
+ bool empty;
+
+ if (!pending)
+ return -ENOMEM;
+ pending->req = req;
+
+ spin_lock_irq(&tctx->task_lock);
+ empty = list_empty(&tctx->pending_ios.list);
+ list_add_tail(&pending->list, &tctx->pending_ios.list);
+
+ if (empty) {
+ timer_setup(&tctx->timer, delayed_write_fn, 0);
+
+ tctx->timer.expires = current->bdp_pause;
+ add_timer(&tctx->timer);
+ }
+ spin_unlock_irq(&tctx->task_lock);
+
+ return 0;
+}
+
static inline void io_queue_next(struct io_kiocb *req)
{
struct io_kiocb *nxt = io_req_find_next(req);
@@ -2770,7 +2804,7 @@ static bool io_resubmit_prep(struct io_kiocb *req)
struct io_async_rw *rw = req->async_data;
if (!req_has_async_data(req))
- return !io_req_prep_async(req);
+ return !io_req_prep_async(req, false);
iov_iter_restore(&rw->s.iter, &rw->s.iter_state);
return true;
}
@@ -3751,6 +3785,38 @@ static int io_write_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return io_prep_rw(req, sqe);
}
+static inline unsigned long write_delay(void)
+{
+ if (likely(current->bdp_nr_dirtied_pause == -1 ||
+ !time_before(jiffies, current->bdp_pause)))
+ return 0;
+
+ return current->bdp_pause;
+}
+
+static void delayed_write_fn(struct timer_list *tmr)
+{
+ struct io_uring_task *tctx = from_timer(tctx, tmr, timer);
+ struct list_head *curr;
+ struct list_head *next;
+ LIST_HEAD(pending_ios);
+
+ /* Move list to temporary list. */
+ spin_lock_irq(&tctx->task_lock);
+ list_splice_init(&tctx->pending_ios.list, &pending_ios);
+ spin_unlock_irq(&tctx->task_lock);
+
+ list_for_each_safe(curr, next, &pending_ios) {
+ struct pending_list *io;
+
+ io = list_entry(curr, struct pending_list, list);
+ io_req_task_queue_reissue(io->req);
+
+ list_del(curr);
+ kfree(io);
+ }
+}
+
static int io_write(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_rw_state __s, *s = &__s;
@@ -3759,6 +3825,18 @@ static int io_write(struct io_kiocb *req, unsigned int issue_flags)
bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
ssize_t ret, ret2;
+ /* Write throttling active? */
+ if (unlikely(write_delay()) && !(kiocb->ki_flags & IOCB_DIRECT)) {
+ int ret = io_req_prep_async(req, true);
+
+ if (unlikely(ret))
+ io_req_complete_failed(req, ret);
+ else
+ ret = io_req_task_queue_reissue_delayed(req);
+
+ return ret;
+ }
+
if (!req_has_async_data(req)) {
ret = io_import_iovec(WRITE, req, &iovec, s, issue_flags);
if (unlikely(ret < 0))
@@ -6597,9 +6675,9 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return -EINVAL;
}
-static int io_req_prep_async(struct io_kiocb *req)
+static int io_req_prep_async(struct io_kiocb *req, bool force)
{
- if (!io_op_defs[req->opcode].needs_async_setup)
+ if (!force && !io_op_defs[req->opcode].needs_async_setup)
return 0;
if (WARN_ON_ONCE(req_has_async_data(req)))
return -EFAULT;
@@ -6609,6 +6687,10 @@ static int io_req_prep_async(struct io_kiocb *req)
switch (req->opcode) {
case IORING_OP_READV:
return io_rw_prep_async(req, READ);
+ case IORING_OP_WRITE:
+ if (!force)
+ break;
+ fallthrough;
case IORING_OP_WRITEV:
return io_rw_prep_async(req, WRITE);
case IORING_OP_SENDMSG:
@@ -6618,6 +6700,7 @@ static int io_req_prep_async(struct io_kiocb *req)
case IORING_OP_CONNECT:
return io_connect_prep_async(req);
}
+
printk_once(KERN_WARNING "io_uring: prep_async() bad opcode %d\n",
req->opcode);
return -EFAULT;
@@ -6651,7 +6734,7 @@ static __cold void io_drain_req(struct io_kiocb *req)
}
spin_unlock(&ctx->completion_lock);
- ret = io_req_prep_async(req);
+ ret = io_req_prep_async(req, false);
if (ret) {
fail:
io_req_complete_failed(req, ret);
@@ -7146,7 +7229,7 @@ static void io_queue_sqe_fallback(struct io_kiocb *req)
} else if (unlikely(req->ctx->drain_active)) {
io_drain_req(req);
} else {
- int ret = io_req_prep_async(req);
+ int ret = io_req_prep_async(req, false);
if (unlikely(ret))
io_req_complete_failed(req, ret);
@@ -7345,7 +7428,7 @@ static int io_submit_sqe(struct io_ring_ctx *ctx, struct io_kiocb *req,
struct io_kiocb *head = link->head;
if (!(req->flags & REQ_F_FAIL)) {
- ret = io_req_prep_async(req);
+ ret = io_req_prep_async(req, false);
if (unlikely(ret)) {
req_fail_link_node(req, ret);
if (!(head->flags & REQ_F_FAIL))
@@ -8785,6 +8868,7 @@ static __cold int io_uring_alloc_task_context(struct task_struct *task,
INIT_WQ_LIST(&tctx->task_list);
INIT_WQ_LIST(&tctx->prior_task_list);
init_task_work(&tctx->task_work, tctx_task_work);
+ INIT_LIST_HEAD(&tctx->pending_ios.list);
return 0;
}
--
2.30.2
next prev parent reply other threads:[~2022-02-14 17:44 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-14 17:43 [PATCH v1 00/14] Support sync buffered writes for io-uring Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 01/14] fs: Add flags parameter to __block_write_begin_int Stefan Roesch
2022-02-14 19:02 ` Matthew Wilcox
2022-02-16 18:31 ` Stefan Roesch
2022-02-16 18:35 ` Matthew Wilcox
2022-02-14 17:43 ` [PATCH v1 02/14] mm: Introduce do_generic_perform_write Stefan Roesch
2022-02-14 19:06 ` Matthew Wilcox
2022-02-14 17:43 ` [PATCH v1 03/14] mm: add noio support in filemap_get_pages Stefan Roesch
2022-02-14 18:08 ` Matthew Wilcox
2022-02-16 18:27 ` Stefan Roesch
2022-02-14 19:33 ` Matthew Wilcox
2022-02-16 18:26 ` Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 04/14] mm: Add support for async buffered writes Stefan Roesch
2022-02-14 19:09 ` Matthew Wilcox
2022-02-14 17:43 ` [PATCH v1 05/14] fs: split off __alloc_page_buffers function Stefan Roesch
2022-02-14 22:46 ` kernel test robot
2022-02-14 22:46 ` kernel test robot
2022-02-14 23:27 ` kernel test robot
2022-02-14 23:27 ` kernel test robot
2022-02-15 2:40 ` [RFC PATCH] fs: __alloc_page_buffers() can be static kernel test robot
2022-02-15 2:40 ` kernel test robot
2022-02-15 2:41 ` [PATCH v1 05/14] fs: split off __alloc_page_buffers function kernel test robot
2022-02-15 2:41 ` kernel test robot
2022-02-14 17:43 ` [PATCH v1 06/14] fs: split off __create_empty_buffers function Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 07/14] fs: Add aop_flags parameter to create_page_buffers() Stefan Roesch
2022-02-14 18:14 ` Matthew Wilcox
2022-02-16 18:30 ` Stefan Roesch
2022-02-16 18:34 ` Matthew Wilcox
2022-02-16 18:35 ` Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 08/14] fs: add support for async buffered writes Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 09/14] io_uring: " Stefan Roesch
2022-02-14 17:43 ` [PATCH v1 10/14] io_uring: Add tracepoint for short writes Stefan Roesch
2022-02-14 17:44 ` [PATCH v1 11/14] sched: add new fields to task_struct Stefan Roesch
2022-02-14 17:44 ` [PATCH v1 12/14] mm: support write throttling for async buffered writes Stefan Roesch
2022-02-14 17:44 ` Stefan Roesch [this message]
2022-02-14 17:44 ` [PATCH v1 14/14] block: enable async buffered writes for block devices Stefan Roesch
2022-02-15 3:59 ` [PATCH v1 00/14] Support sync buffered writes for io-uring Hao Xu
2022-02-15 17:38 ` Stefan Roesch
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220214174403.4147994-14-shr@fb.com \
--to=shr@fb.com \
--cc=io-uring@vger.kernel.org \
--cc=kernel-team@fb.com \
--cc=linux-block@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.