summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
authorgjb <gjb@FreeBSD.org>2016-09-01 19:30:52 +0000
committergjb <gjb@FreeBSD.org>2016-09-01 19:30:52 +0000
commit26faa69f1b07ad683bb69e302b503771836f5267 (patch)
tree8efabae042929d1188ceaaeeb8140622c0e2552e /sys/boot/i386
parente5d9dd260d350c6aff2ab99aef49797ff0e2642e (diff)
downloadFreeBSD-src-26faa69f1b07ad683bb69e302b503771836f5267.zip
FreeBSD-src-26faa69f1b07ad683bb69e302b503771836f5267.tar.gz
MFC r304966 (peter):
The read-ahead code from r298230 made it likely the boot code would read beyond the end of disk. r298900 added code to prevent this. Some BIOSes cause significant delays if asked to read past end-of-disk. We never trusted the BIOS to accurately report the sectorsize of disks before and this set of changes. Unfortuately they interact badly with the infamous >2TB wraparound bugs. We have a number of relatively-recent machines in the FreeBSD.org cluster where the BIOS reports 3TB disks as 1TB. With pre-r298900 they work just fine. After r298900 they stop working if the boot environment attempts to access anything outside the first 1TB on the disk. 'ZFS: I/O error, all block copies unavailable' etc. It affects both UFS and ZFS if they try to boot from large volumes. This change replaces the blind trust of the BIOS end-of-disk reporting with a read-ahead clip to prevent reads crossing the of end-of-disk boundary. Since 2^32 (2TB) size reporting truncation is not uncommon, the clipping is done on 2TB aliases of the reported end-of-disk. ie: a 3TB disk reported as 1TB has readahead clipped at 1TB, 3TB, 5TB, ... as one of them is likely to be the real end-of-disk. This should make the loader on these broken machines behave the same as traditional pre-r298900 loader behavior, without disabling read-ahead. PR: 212139 Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/libi386/biosdisk.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c
index 209eaed..bbd4716 100644
--- a/sys/boot/i386/libi386/biosdisk.c
+++ b/sys/boot/i386/libi386/biosdisk.c
@@ -495,7 +495,7 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t offset, size_t size,
char *buf, size_t *rsize)
{
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
- int blks;
+ int blks, remaining;
#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
char fragbuf[BIOSDISK_SECSIZE];
size_t fragsize;
@@ -511,14 +511,15 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t offset, size_t size,
if (rsize)
*rsize = 0;
- if (dblk >= BD(dev).bd_sectors) {
- DEBUG("IO past disk end %llu", (unsigned long long)dblk);
- return (EIO);
- }
-
- if (dblk + blks > BD(dev).bd_sectors) {
- /* perform partial read */
- blks = BD(dev).bd_sectors - dblk;
+ /*
+ * Perform partial read to prevent read-ahead crossing
+ * the end of disk - or any 32 bit aliases of the end.
+ * Signed arithmetic is used to handle wrap-around cases
+ * like we do for TCP sequence numbers.
+ */
+ remaining = (int)(BD(dev).bd_sectors - dblk); /* truncate */
+ if (remaining > 0 && remaining < blks) {
+ blks = remaining;
size = blks * BD(dev).bd_sectorsize;
DEBUG("short read %d", blks);
}
OpenPOWER on IntegriCloud