|
@@ -2893,6 +2893,55 @@ static void end_bio_bh_io_sync(struct bio *bio, int err)
|
|
|
bio_put(bio);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This allows us to do IO even on the odd last sectors
|
|
|
+ * of a device, even if the bh 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 IO
|
|
|
+ * errors, this only handles the "we need to be able to
|
|
|
+ * do IO at the final sector" case.
|
|
|
+ */
|
|
|
+static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh)
|
|
|
+{
|
|
|
+ sector_t maxsector;
|
|
|
+ unsigned bytes;
|
|
|
+
|
|
|
+ maxsector = i_size_read(bio->bi_bdev->bd_inode) >> 9;
|
|
|
+ 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_sector >= maxsector))
|
|
|
+ return;
|
|
|
+
|
|
|
+ maxsector -= bio->bi_sector;
|
|
|
+ bytes = bio->bi_size;
|
|
|
+ if (likely((bytes >> 9) <= maxsector))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Uhhuh. We've got a bh that straddles the device size! */
|
|
|
+ bytes = maxsector << 9;
|
|
|
+
|
|
|
+ /* Truncate the bio.. */
|
|
|
+ bio->bi_size = bytes;
|
|
|
+ bio->bi_io_vec[0].bv_len = bytes;
|
|
|
+
|
|
|
+ /* ..and clear the end of the buffer for reads */
|
|
|
+ if (rw & READ) {
|
|
|
+ void *kaddr = kmap_atomic(bh->b_page);
|
|
|
+ memset(kaddr + bh_offset(bh) + bytes, 0, bh->b_size - bytes);
|
|
|
+ kunmap_atomic(kaddr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int submit_bh(int rw, struct buffer_head * bh)
|
|
|
{
|
|
|
struct bio *bio;
|
|
@@ -2929,6 +2978,9 @@ int submit_bh(int rw, struct buffer_head * bh)
|
|
|
bio->bi_end_io = end_bio_bh_io_sync;
|
|
|
bio->bi_private = bh;
|
|
|
|
|
|
+ /* Take care of bh's that straddle the end of the device */
|
|
|
+ guard_bh_eod(rw, bio, bh);
|
|
|
+
|
|
|
bio_get(bio);
|
|
|
submit_bio(rw, bio);
|
|
|
|