From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
To: linux-block@vger.kernel.org, linux-fsdevel@vger.kernel.org
Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
Subject: [PATCH 1/2] block: Add submit_bio_killable
Date: Fri, 16 Oct 2020 17:14:25 +0100 [thread overview]
Message-ID: <20201016161426.21715-2-willy@infradead.org> (raw)
In-Reply-To: <20201016161426.21715-1-willy@infradead.org>
This new function allows the user to interrupt the I/O wait with a fatal
signal, as long as the caller provides an alternative function to clean
up once the I/O does complete.
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
block/bio.c | 87 +++++++++++++++++++++++++++++---------
include/linux/bio.h | 1 +
include/linux/completion.h | 1 +
kernel/sched/completion.c | 9 ++--
4 files changed, 75 insertions(+), 23 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index a9931f23d933..83e1d22c053e 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1129,39 +1129,88 @@ static void submit_bio_wait_endio(struct bio *bio)
complete(bio->bi_private);
}
+static
+int __submit_bio_wait(struct bio *bio, struct completion *done, int state)
+{
+ long timeout;
+ long ret = 0;
+
+ bio->bi_private = done;
+ bio->bi_end_io = submit_bio_wait_endio;
+ bio->bi_opf |= REQ_SYNC;
+ submit_bio(bio);
+
+ /* Prevent hang_check timer from firing at us during very long I/O */
+ timeout = sysctl_hung_task_timeout_secs * (HZ / 2);
+ if (!timeout)
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ while (ret == 0)
+ ret = __wait_for_completion_io(done, timeout, state);
+
+ if (ret < 0)
+ return ret;
+
+ return blk_status_to_errno(bio->bi_status);
+}
+
/**
- * submit_bio_wait - submit a bio, and wait until it completes
+ * submit_bio_wait - Submit a bio, and wait for it to complete.
* @bio: The &struct bio which describes the I/O
*
* Simple wrapper around submit_bio(). Returns 0 on success, or the error from
* bio_endio() on failure.
*
- * WARNING: Unlike to how submit_bio() is usually used, this function does not
- * result in bio reference to be consumed. The caller must drop the reference
- * on his own.
+ * WARNING: Unlike how submit_bio() is usually used, this function does
+ * not result in a bio reference being consumed. The caller must drop
+ * the reference.
*/
int submit_bio_wait(struct bio *bio)
{
DECLARE_COMPLETION_ONSTACK_MAP(done, bio->bi_disk->lockdep_map);
- unsigned long hang_check;
+ return __submit_bio_wait(bio, &done, TASK_UNINTERRUPTIBLE);
+}
+EXPORT_SYMBOL(submit_bio_wait);
- bio->bi_private = &done;
- bio->bi_end_io = submit_bio_wait_endio;
- bio->bi_opf |= REQ_SYNC;
- submit_bio(bio);
+/**
+ * submit_bio_killable - Submit a bio, and wait for it to complete.
+ * @bio: The &struct bio which describes the I/O
+ * @async_end_io: Callback for interrupted waits
+ *
+ * Submits the BIO to the block device and waits for it to complete.
+ * If the wait is interrupted by a fatal signal, the @async_end_io
+ * will be called instead. Unlike submit_bio_wait(), the bio will
+ * have its reference count consumed by this call (unless we get a
+ * fatal signal; in which case the reference count should be
+ * consumed by @async_end_io).
+ *
+ * Return: 0 if the bio completed successfully, -ERESTARTSYS if we received
+ * a fatal signal, or a different errno if the bio could not complete.
+ */
+int submit_bio_killable(struct bio *bio, bio_end_io_t async_end_io)
+{
+ DECLARE_COMPLETION_ONSTACK_MAP(cmpl, bio->bi_disk->lockdep_map);
+ int err = __submit_bio_wait(bio, &cmpl, TASK_KILLABLE);
- /* Prevent hang_check timer from firing at us during very long I/O */
- hang_check = sysctl_hung_task_timeout_secs;
- if (hang_check)
- while (!wait_for_completion_io_timeout(&done,
- hang_check * (HZ/2)))
- ;
- else
- wait_for_completion_io(&done);
+ if (likely(err != -ERESTARTSYS))
+ goto completed;
- return blk_status_to_errno(bio->bi_status);
+ bio->bi_end_io = async_end_io;
+ synchronize_rcu();
+ /*
+ * Nobody can touch the completion now, but it may have been
+ * completed while we waited. It doesn't really matter what
+ * error we return since the task is about to die, but we need
+ * to not leak the bio.
+ */
+ if (!cmpl.done)
+ return err;
+
+ err = blk_status_to_errno(bio->bi_status);
+completed:
+ bio_put(bio);
+ return err;
}
-EXPORT_SYMBOL(submit_bio_wait);
+EXPORT_SYMBOL(submit_bio_killable);
/**
* bio_advance - increment/complete a bio by some number of bytes
diff --git a/include/linux/bio.h b/include/linux/bio.h
index c6d765382926..f254bc79bb3a 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -431,6 +431,7 @@ static inline void bio_wouldblock_error(struct bio *bio)
struct request_queue;
extern int submit_bio_wait(struct bio *bio);
+extern int submit_bio_killable(struct bio *bio, bio_end_io_t async_end_io);
extern void bio_advance(struct bio *, unsigned);
extern void bio_init(struct bio *bio, struct bio_vec *table,
diff --git a/include/linux/completion.h b/include/linux/completion.h
index bf8e77001f18..c7d557d5639c 100644
--- a/include/linux/completion.h
+++ b/include/linux/completion.h
@@ -108,6 +108,7 @@ extern unsigned long wait_for_completion_timeout(struct completion *x,
unsigned long timeout);
extern unsigned long wait_for_completion_io_timeout(struct completion *x,
unsigned long timeout);
+long __wait_for_completion_io(struct completion *x, long timeout, int state);
extern long wait_for_completion_interruptible_timeout(
struct completion *x, unsigned long timeout);
extern long wait_for_completion_killable_timeout(
diff --git a/kernel/sched/completion.c b/kernel/sched/completion.c
index a778554f9dad..3cb97b754b44 100644
--- a/kernel/sched/completion.c
+++ b/kernel/sched/completion.c
@@ -117,11 +117,12 @@ wait_for_common(struct completion *x, long timeout, int state)
return __wait_for_common(x, schedule_timeout, timeout, state);
}
-static long __sched
-wait_for_common_io(struct completion *x, long timeout, int state)
+long __sched
+__wait_for_completion_io(struct completion *x, long timeout, int state)
{
return __wait_for_common(x, io_schedule_timeout, timeout, state);
}
+EXPORT_SYMBOL_GPL(__wait_for_completion_io);
/**
* wait_for_completion: - waits for completion of a task
@@ -168,7 +169,7 @@ EXPORT_SYMBOL(wait_for_completion_timeout);
*/
void __sched wait_for_completion_io(struct completion *x)
{
- wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
+ __wait_for_completion_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion_io);
@@ -188,7 +189,7 @@ EXPORT_SYMBOL(wait_for_completion_io);
unsigned long __sched
wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
{
- return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE);
+ return __wait_for_completion_io(x, timeout, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion_io_timeout);
--
2.28.0
next prev parent reply other threads:[~2020-10-16 16:14 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-16 16:14 [PATCH 0/2] Killable synchronous BIO submission Matthew Wilcox (Oracle)
2020-10-16 16:14 ` Matthew Wilcox (Oracle) [this message]
2020-10-16 16:14 ` [PATCH 2/2] fs: Make mpage_readpage synchronous Matthew Wilcox (Oracle)
2020-10-16 16:56 ` Keith Busch
2020-10-16 16:59 ` Matthew Wilcox
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=20201016161426.21715-2-willy@infradead.org \
--to=willy@infradead.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).