summary refs log tree commit diff
path: root/block
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2020-03-25 16:48:40 +0100
committerJens Axboe <axboe@kernel.dk>2020-03-25 09:50:08 -0600
commit29125ed624eeb3ac2eb7bca313a8de29c1c84dcd (patch)
tree1d728350d5aef28aa3ac2a343d8768292427d066 /block
parent1b4d4dbdaeb7087122a39d3fb9ae32487e001b6c (diff)
downloadlinux-29125ed624eeb3ac2eb7bca313a8de29c1c84dcd.tar.gz
block: move guard_bio_eod to bio.c
This is bio layer functionality and not related to buffer heads.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block')
-rw-r--r--block/bio.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/block/bio.c b/block/bio.c
index bc9152977bf0..11e6aac35092 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -589,6 +589,49 @@ void bio_truncate(struct bio *bio, unsigned new_size)
 }
 
 /**
+ * guard_bio_eod - truncate a BIO to fit the block device
+ * @bio:	bio to truncate
+ *
+ * This allows us to do IO even on the odd last sectors of a device, even if the
+ * block size is some multiple of the physical sector size.
+ *
+ * We'll just truncate the bio to the size of the device, and clear the end of
+ * the buffer head manually.  Truly out-of-range accesses will turn into actual
+ * I/O errors, this only handles the "we need to be able to do I/O at the final
+ * sector" case.
+ */
+void guard_bio_eod(struct bio *bio)
+{
+	sector_t maxsector;
+	struct hd_struct *part;
+
+	rcu_read_lock();
+	part = __disk_get_part(bio->bi_disk, bio->bi_partno);
+	if (part)
+		maxsector = part_nr_sects_read(part);
+	else
+		maxsector = get_capacity(bio->bi_disk);
+	rcu_read_unlock();
+
+	if (!maxsector)
+		return;
+
+	/*
+	 * If the *whole* IO is past the end of the device,
+	 * let it through, and the IO layer will turn it into
+	 * an EIO.
+	 */
+	if (unlikely(bio->bi_iter.bi_sector >= maxsector))
+		return;
+
+	maxsector -= bio->bi_iter.bi_sector;
+	if (likely((bio->bi_iter.bi_size >> 9) <= maxsector))
+		return;
+
+	bio_truncate(bio, maxsector << 9);
+}
+
+/**
  * bio_put - release a reference to a bio
  * @bio:   bio to release reference to
  *