aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-04 11:25:11 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-04 11:25:11 -0500
commit57302e0ddf8a210a66fd8a1a2fa50844863b5ded (patch)
tree1390147095c0b67f6e052d4f53377a369d3f22bf
parentb69f0859dc8e633c5d8c06845811588fe17e68b3 (diff)
vfs: avoid "attempt to access beyond end of device" warnings
The block device access simplification that avoided accessing the (racy) block size information (commit bbec0270bdd8: "blkdev_max_block: make private to fs/buffer.c") no longer checks the maximum block size in the block mapping path. That was _almost_ as simple as just removing the code entirely, because the readers and writers all check the size of the device anyway, so under normal circumstances it "just worked". However, the block size may be such that the end of the device may straddle one single buffer_head. At which point we may still want to access the end of the device, but the buffer we use to access it partially extends past the end. The 'bd_set_size()' function intentionally sets the block size to avoid this, but mounting the device - or setting the block size by hand to some other value - can modify that block size. So instead, teach 'submit_bh()' about the special case of the buffer head straddling the end of the device, and turning such an access into a smaller IO access, avoiding the problem. This, btw, also means that unlike before, we can now access the whole device regardless of device block size setting. So now, even if the device size is only 512-byte aligned, we can read and write even the last sector even when having a much bigger block size for accessing the rest of the device. So with this, we could now get rid of the 'bd_set_size()' block size code entirely - resulting in faster IO for the common case - but that would be a separate patch. Reported-and-tested-by: Romain Francoise <romain@orebokech.com> Reporeted-and-tested-by: Meelis Roos <mroos@linux.ee> Reported-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/buffer.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 3586fb05c8ce..c4e11390a44c 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2893,6 +2893,55 @@ static void end_bio_bh_io_sync(struct bio *bio, int err)
2893 bio_put(bio); 2893 bio_put(bio);
2894} 2894}
2895 2895
2896/*
2897 * This allows us to do IO even on the odd last sectors
2898 * of a device, even if the bh block size is some multiple
2899 * of the physical sector size.
2900 *
2901 * We'll just truncate the bio to the size of the device,
2902 * and clear the end of the buffer head manually.
2903 *
2904 * Truly out-of-range accesses will turn into actual IO
2905 * errors, this only handles the "we need to be able to
2906 * do IO at the final sector" case.
2907 */
2908static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh)
2909{
2910 sector_t maxsector;
2911 unsigned bytes;
2912
2913 maxsector = i_size_read(bio->bi_bdev->bd_inode) >> 9;
2914 if (!maxsector)
2915 return;
2916
2917 /*
2918 * If the *whole* IO is past the end of the device,
2919 * let it through, and the IO layer will turn it into
2920 * an EIO.
2921 */
2922 if (unlikely(bio->bi_sector >= maxsector))
2923 return;
2924
2925 maxsector -= bio->bi_sector;
2926 bytes = bio->bi_size;
2927 if (likely((bytes >> 9) <= maxsector))
2928 return;
2929
2930 /* Uhhuh. We've got a bh that straddles the device size! */
2931 bytes = maxsector << 9;
2932
2933 /* Truncate the bio.. */
2934 bio->bi_size = bytes;
2935 bio->bi_io_vec[0].bv_len = bytes;
2936
2937 /* ..and clear the end of the buffer for reads */
2938 if (rw & READ) {
2939 void *kaddr = kmap_atomic(bh->b_page);
2940 memset(kaddr + bh_offset(bh) + bytes, 0, bh->b_size - bytes);
2941 kunmap_atomic(kaddr);
2942 }
2943}
2944
2896int submit_bh(int rw, struct buffer_head * bh) 2945int submit_bh(int rw, struct buffer_head * bh)
2897{ 2946{
2898 struct bio *bio; 2947 struct bio *bio;
@@ -2929,6 +2978,9 @@ int submit_bh(int rw, struct buffer_head * bh)
2929 bio->bi_end_io = end_bio_bh_io_sync; 2978 bio->bi_end_io = end_bio_bh_io_sync;
2930 bio->bi_private = bh; 2979 bio->bi_private = bh;
2931 2980
2981 /* Take care of bh's that straddle the end of the device */
2982 guard_bh_eod(rw, bio, bh);
2983
2932 bio_get(bio); 2984 bio_get(bio);
2933 submit_bio(rw, bio); 2985 submit_bio(rw, bio);
2934 2986