Linux-Block Archive mirror
 help / color / mirror / Atom feed
* next try at interruptible BLKDISCARD
@ 2024-05-06  4:20 Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard Christoph Hellwig
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

Hi Jens,

this tries to resurrect Keith's series to allow fatal signals to
interrupt discards, after first refactoring the low-level code so
that calls from fs code do not get interrupted unexpectedly.

Unlike the original series it only handles discards.  Write zeroes
and secure discard could get the same treatement, but I'd rather
sort out the discard side first.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  2024-05-07 13:32   ` Jens Axboe
  2024-05-06  4:20 ` [PATCH 2/6] block: move discard checks into the ioctl handler Christoph Hellwig
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

We now set a default granularity in the queue limits API, so don't
bother with this extra check.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/blk-lib.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/block/blk-lib.c b/block/blk-lib.c
index a6954eafb8c8af..7ec3e170e7f629 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -46,13 +46,6 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
 	if (!bdev_max_discard_sectors(bdev))
 		return -EOPNOTSUPP;
 
-	/* In case the discard granularity isn't set by buggy device driver */
-	if (WARN_ON_ONCE(!bdev_discard_granularity(bdev))) {
-		pr_err_ratelimited("%pg: Error: discard_granularity is 0.\n",
-				   bdev);
-		return -EOPNOTSUPP;
-	}
-
 	bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
 	if ((sector | nr_sects) & bs_mask)
 		return -EINVAL;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/6] block: move discard checks into the ioctl handler
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 3/6] block: add a bio_chain_and_submit helper Christoph Hellwig
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

Most bio operations get basic sanity checking in submit_bio and anything
more complicated than that is done in the callers.  Discards are a bit
different from that in that a lot of checking is done in
__blkdev_issue_discard, and the specific errnos for that are returned
to userspace.  Move the checks that require specific errnos to the ioctl
handler instead, and just leave the basic sanity checking in submit_bio
for the other handlers.  This introduces two changes in behavior:

 1) the logical block size alignment check of the start and len is lost
    for non-ioctl callers.
    This matches what is done for other operations including reads and
    writes.  We should probably verify this for all bios, but for now
    make discards match the normal flow.
 2) for non-ioctl callers all errors are reported on I/O completion now
    instead of synchronously.  Callers in general mostly ignore or log
    errors so this will actually simplify the code once cleaned up

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/blk-lib.c | 13 -------------
 block/ioctl.c   |  7 +++++--
 2 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/block/blk-lib.c b/block/blk-lib.c
index 7ec3e170e7f629..6e54ef140bab12 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -39,19 +39,6 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
 		sector_t nr_sects, gfp_t gfp_mask, struct bio **biop)
 {
 	struct bio *bio = *biop;
-	sector_t bs_mask;
-
-	if (bdev_read_only(bdev))
-		return -EPERM;
-	if (!bdev_max_discard_sectors(bdev))
-		return -EOPNOTSUPP;
-
-	bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
-	if ((sector | nr_sects) & bs_mask)
-		return -EINVAL;
-
-	if (!nr_sects)
-		return -EINVAL;
 
 	while (nr_sects) {
 		sector_t req_sects =
diff --git a/block/ioctl.c b/block/ioctl.c
index 0c76137adcaaa5..03bcdf2783b508 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -95,6 +95,7 @@ static int compat_blkpg_ioctl(struct block_device *bdev,
 static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
 		unsigned long arg)
 {
+	unsigned int bs_mask = bdev_logical_block_size(bdev) - 1;
 	uint64_t range[2];
 	uint64_t start, len;
 	struct inode *inode = bdev->bd_inode;
@@ -105,6 +106,8 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
 
 	if (!bdev_max_discard_sectors(bdev))
 		return -EOPNOTSUPP;
+	if (bdev_read_only(bdev))
+		return -EPERM;
 
 	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
 		return -EFAULT;
@@ -112,9 +115,9 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
 	start = range[0];
 	len = range[1];
 
-	if (start & 511)
+	if (!len)
 		return -EINVAL;
-	if (len & 511)
+	if ((start | len) & bs_mask)
 		return -EINVAL;
 
 	if (start + len > bdev_nr_bytes(bdev))
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/6] block: add a bio_chain_and_submit helper
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 2/6] block: move discard checks into the ioctl handler Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 4/6] block: add a blk_alloc_discard_bio helper Christoph Hellwig
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

This is basically blk_next_bio just with the bio allocation moved
to the caller to allow for more flexible bio handling in the caller.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/bio.c         | 27 +++++++++++++++++++--------
 include/linux/bio.h |  1 +
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 38baedb39c6f2e..d82ef4fd545cb2 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -345,18 +345,29 @@ void bio_chain(struct bio *bio, struct bio *parent)
 }
 EXPORT_SYMBOL(bio_chain);
 
-struct bio *blk_next_bio(struct bio *bio, struct block_device *bdev,
-		unsigned int nr_pages, blk_opf_t opf, gfp_t gfp)
+/**
+ * bio_chain_and_submit - submit a bio after chaining it to another one
+ * @prev: bio to chain and submit
+ * @new: bio to chain to
+ *
+ * If @prev is non-NULL, chain it to @new and submit it.
+ *
+ * Return: @new.
+ */
+struct bio *bio_chain_and_submit(struct bio *prev, struct bio *new)
 {
-	struct bio *new = bio_alloc(bdev, nr_pages, opf, gfp);
-
-	if (bio) {
-		bio_chain(bio, new);
-		submit_bio(bio);
+	if (prev) {
+		bio_chain(prev, new);
+		submit_bio(prev);
 	}
-
 	return new;
 }
+
+struct bio *blk_next_bio(struct bio *bio, struct block_device *bdev,
+		unsigned int nr_pages, blk_opf_t opf, gfp_t gfp)
+{
+	return bio_chain_and_submit(bio, bio_alloc(bdev, nr_pages, opf, gfp));
+}
 EXPORT_SYMBOL_GPL(blk_next_bio);
 
 static void bio_alloc_rescue(struct work_struct *work)
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 9b8a369f44bc6b..283a0dcbd1de61 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -831,5 +831,6 @@ static inline void bio_clear_polled(struct bio *bio)
 
 struct bio *blk_next_bio(struct bio *bio, struct block_device *bdev,
 		unsigned int nr_pages, blk_opf_t opf, gfp_t gfp);
+struct bio *bio_chain_and_submit(struct bio *prev, struct bio *new);
 
 #endif /* __LINUX_BIO_H */
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/6] block: add a blk_alloc_discard_bio helper
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
                   ` (2 preceding siblings ...)
  2024-05-06  4:20 ` [PATCH 3/6] block: add a bio_chain_and_submit helper Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 5/6] block: add a bio_await_chain helper Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 6/6] blk-lib: check for kill signal in ioctl BLKDISCARD Christoph Hellwig
  5 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

Factor out a helper from __blkdev_issue_discard that chews off as much as
possible from a discard range and allocates a bio for it.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/blk-lib.c     | 50 ++++++++++++++++++++++++++-------------------
 include/linux/bio.h |  3 +++
 2 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/block/blk-lib.c b/block/blk-lib.c
index 6e54ef140bab12..442da9dad04213 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -35,31 +35,39 @@ static sector_t bio_discard_limit(struct block_device *bdev, sector_t sector)
 	return round_down(UINT_MAX, discard_granularity) >> SECTOR_SHIFT;
 }
 
-int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
-		sector_t nr_sects, gfp_t gfp_mask, struct bio **biop)
+struct bio *blk_alloc_discard_bio(struct block_device *bdev,
+		sector_t *sector, sector_t *nr_sects, gfp_t gfp_mask)
 {
-	struct bio *bio = *biop;
+	sector_t bio_sects = min(*nr_sects, bio_discard_limit(bdev, *sector));
+	struct bio *bio;
 
-	while (nr_sects) {
-		sector_t req_sects =
-			min(nr_sects, bio_discard_limit(bdev, sector));
+	if (!bio_sects)
+		return NULL;
 
-		bio = blk_next_bio(bio, bdev, 0, REQ_OP_DISCARD, gfp_mask);
-		bio->bi_iter.bi_sector = sector;
-		bio->bi_iter.bi_size = req_sects << 9;
-		sector += req_sects;
-		nr_sects -= req_sects;
-
-		/*
-		 * We can loop for a long time in here, if someone does
-		 * full device discards (like mkfs). Be nice and allow
-		 * us to schedule out to avoid softlocking if preempt
-		 * is disabled.
-		 */
-		cond_resched();
-	}
+	bio = bio_alloc(bdev, 0, REQ_OP_DISCARD, gfp_mask);
+	if (!bio)
+		return NULL;
+	bio->bi_iter.bi_sector = *sector;
+	bio->bi_iter.bi_size = bio_sects << SECTOR_SHIFT;
+	*sector += bio_sects;
+	*nr_sects -= bio_sects;
+	/*
+	 * We can loop for a long time in here if someone does full device
+	 * discards (like mkfs).  Be nice and allow us to schedule out to avoid
+	 * softlocking if preempt is disabled.
+	 */
+	cond_resched();
+	return bio;
+}
 
-	*biop = bio;
+int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
+		sector_t nr_sects, gfp_t gfp_mask, struct bio **biop)
+{
+	struct bio *bio;
+
+	while ((bio = blk_alloc_discard_bio(bdev, &sector, &nr_sects,
+			gfp_mask)))
+		*biop = bio_chain_and_submit(*biop, bio);
 	return 0;
 }
 EXPORT_SYMBOL(__blkdev_issue_discard);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 283a0dcbd1de61..d5379548d684e1 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -833,4 +833,7 @@ struct bio *blk_next_bio(struct bio *bio, struct block_device *bdev,
 		unsigned int nr_pages, blk_opf_t opf, gfp_t gfp);
 struct bio *bio_chain_and_submit(struct bio *prev, struct bio *new);
 
+struct bio *blk_alloc_discard_bio(struct block_device *bdev,
+		sector_t *sector, sector_t *nr_sects, gfp_t gfp_mask);
+
 #endif /* __LINUX_BIO_H */
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/6] block: add a bio_await_chain helper
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
                   ` (3 preceding siblings ...)
  2024-05-06  4:20 ` [PATCH 4/6] block: add a blk_alloc_discard_bio helper Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  2024-05-06  4:20 ` [PATCH 6/6] blk-lib: check for kill signal in ioctl BLKDISCARD Christoph Hellwig
  5 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

From: Keith Busch <kbusch@kernel.org>

Add a helper to wait for an entire chain of bios to complete.

Signed-off-by: Keith Busch <kbusch@kernel.org>
[hch: split from a larger patch, moved and changed the name now that it
 is non-static]
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/bio.c | 20 ++++++++++++++++++++
 block/blk.h |  1 +
 2 files changed, 21 insertions(+)

diff --git a/block/bio.c b/block/bio.c
index d82ef4fd545cb2..dce12a0efdead2 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1395,6 +1395,26 @@ int submit_bio_wait(struct bio *bio)
 }
 EXPORT_SYMBOL(submit_bio_wait);
 
+static void bio_wait_end_io(struct bio *bio)
+{
+	complete(bio->bi_private);
+	bio_put(bio);
+}
+
+/*
+ * bio_await_chain - ends @bio and waits for every chained bio to complete
+ */
+void bio_await_chain(struct bio *bio)
+{
+	DECLARE_COMPLETION_ONSTACK_MAP(done,
+			bio->bi_bdev->bd_disk->lockdep_map);
+
+	bio->bi_private = &done;
+	bio->bi_end_io = bio_wait_end_io;
+	bio_endio(bio);
+	blk_wait_io(&done);
+}
+
 void __bio_advance(struct bio *bio, unsigned bytes)
 {
 	if (bio_integrity(bio))
diff --git a/block/blk.h b/block/blk.h
index ee4f782d149662..d5107e65355e27 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -38,6 +38,7 @@ void __blk_mq_unfreeze_queue(struct request_queue *q, bool force_atomic);
 void blk_queue_start_drain(struct request_queue *q);
 int __bio_queue_enter(struct request_queue *q, struct bio *bio);
 void submit_bio_noacct_nocheck(struct bio *bio);
+void bio_await_chain(struct bio *bio);
 
 static inline bool blk_try_enter_queue(struct request_queue *q, bool pm)
 {
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/6] blk-lib: check for kill signal in ioctl BLKDISCARD
  2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
                   ` (4 preceding siblings ...)
  2024-05-06  4:20 ` [PATCH 5/6] block: add a bio_await_chain helper Christoph Hellwig
@ 2024-05-06  4:20 ` Christoph Hellwig
  5 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2024-05-06  4:20 UTC (permalink / raw
  To: Jens Axboe; +Cc: Keith Busch, Conrad Meyer, linux-block

Discards can access a significant capacity and take longer than the user
expected.  A user may change their mind about wanting to run that command
and attempt to kill the process and do something else with their device.
But since the task is uninterruptable, they have to wait for it to
finish, which could be many hours.

Open code blkdev_issue_discard in the BLKDISCARD ioctl handler and check
for a fatal signal at each iteration so the user doesn't have to wait
for their regretted operation to complete naturally.

Heavily based on an earlier patch from Keith Busch.

Reported-by: Conrad Meyer <conradmeyer@meta.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/ioctl.c | 33 ++++++++++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)

diff --git a/block/ioctl.c b/block/ioctl.c
index 03bcdf2783b508..003d134779db56 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -96,9 +96,11 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
 		unsigned long arg)
 {
 	unsigned int bs_mask = bdev_logical_block_size(bdev) - 1;
-	uint64_t range[2];
-	uint64_t start, len;
 	struct inode *inode = bdev->bd_inode;
+	uint64_t range[2], start, len;
+	struct bio *prev = NULL, *bio;
+	sector_t sector, nr_sects;
+	struct blk_plug plug;
 	int err;
 
 	if (!(mode & BLK_OPEN_WRITE))
@@ -127,7 +129,32 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
 	err = truncate_bdev_range(bdev, mode, start, start + len - 1);
 	if (err)
 		goto fail;
-	err = blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL);
+
+	sector = start >> SECTOR_SHIFT;
+	nr_sects = len >> SECTOR_SHIFT;
+
+	blk_start_plug(&plug);
+	while (1) {
+		if (fatal_signal_pending(current)) {
+			if (prev)
+				bio_await_chain(prev);
+			err = -EINTR;
+			goto out_unplug;
+		}
+		bio = blk_alloc_discard_bio(bdev, &sector, &nr_sects,
+				GFP_KERNEL);
+		if (!bio)
+			break;
+		prev = bio_chain_and_submit(prev, bio);
+	}
+	if (prev) {
+		err = submit_bio_wait(prev);
+		if (err == -EOPNOTSUPP)
+			err = 0;
+		bio_put(prev);
+	}
+out_unplug:
+	blk_finish_plug(&plug);
 fail:
 	filemap_invalidate_unlock(inode->i_mapping);
 	return err;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard
  2024-05-06  4:20 ` [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard Christoph Hellwig
@ 2024-05-07 13:32   ` Jens Axboe
  0 siblings, 0 replies; 8+ messages in thread
From: Jens Axboe @ 2024-05-07 13:32 UTC (permalink / raw
  To: Christoph Hellwig; +Cc: Keith Busch, Conrad Meyer, linux-block


On Mon, 06 May 2024 06:20:22 +0200, Christoph Hellwig wrote:
> We now set a default granularity in the queue limits API, so don't
> bother with this extra check.
> 
> 

Applied, thanks!

[1/6] block: remove the discard_granularity check in __blkdev_issue_discard
      commit: 0942592045782e76a9d52c409955c2dc313cbd30
[2/6] block: move discard checks into the ioctl handler
      commit: 30f1e724142242a453f92d90b33e030014900bf0
[3/6] block: add a bio_chain_and_submit helper
      commit: 81c2168c229bab0665e862937bb476f18cff056d
[4/6] block: add a blk_alloc_discard_bio helper
      commit: e8b4869bc78da1a71f2a2ab476caf50c1dcfeed0
[5/6] block: add a bio_await_chain helper
      commit: 0f8e9ecc4636e3abb4f3cf1ead14c94cce7dfde8
[6/6] blk-lib: check for kill signal in ioctl BLKDISCARD
      commit: 719c15a75ebf3bda3ca718fe8e0ce63d262ec7ae

Best regards,
-- 
Jens Axboe




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-05-07 13:32 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-06  4:20 next try at interruptible BLKDISCARD Christoph Hellwig
2024-05-06  4:20 ` [PATCH 1/6] block: remove the discard_granularity check in __blkdev_issue_discard Christoph Hellwig
2024-05-07 13:32   ` Jens Axboe
2024-05-06  4:20 ` [PATCH 2/6] block: move discard checks into the ioctl handler Christoph Hellwig
2024-05-06  4:20 ` [PATCH 3/6] block: add a bio_chain_and_submit helper Christoph Hellwig
2024-05-06  4:20 ` [PATCH 4/6] block: add a blk_alloc_discard_bio helper Christoph Hellwig
2024-05-06  4:20 ` [PATCH 5/6] block: add a bio_await_chain helper Christoph Hellwig
2024-05-06  4:20 ` [PATCH 6/6] blk-lib: check for kill signal in ioctl BLKDISCARD Christoph Hellwig

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 read-only IMAP folder(s) and NNTP newsgroup(s).