aboutsummaryrefslogtreecommitdiff
path: root/sys/geom/uzip
diff options
context:
space:
mode:
authorMarcel Moolenaar <marcel@FreeBSD.org>2014-04-15 15:41:57 +0000
committerMarcel Moolenaar <marcel@FreeBSD.org>2014-04-15 15:41:57 +0000
commit855be5b2c135b9b5c4592a2c297db77cad6c1fa3 (patch)
tree1e9c882bba953e1eb7dda3a4b046c97d1044ab2b /sys/geom/uzip
parent135457127961b77388a893f999c3939e554d218d (diff)
downloadsrc-855be5b2c135b9b5c4592a2c297db77cad6c1fa3.tar.gz
src-855be5b2c135b9b5c4592a2c297db77cad6c1fa3.zip
Make sure not to do I/O for more than MAXPHYS bytes. Doing so can cause
problems in our providers, such as a KASSERT in md(4). We can initiate I/O for more than MAXPHYS bytes if we've been given a BIO for MAXPHYS bytes, the blocks from which we're reading couldn't be compressed and we had compression in preceeding blocks resulting in misalignment of the blocks we're trying to read relative to the sector. We're forced to round up the I/O length to make it an multiple of the sector size. When we detect the condition, we'll reduce the block count and perform a "short" read. In g_uzip_done() we need to consider the original I/O length and stop early if we're about to deflate a block that we didn't read. By using bio_completed in the cloned BIO and not bio_length to check for this, we automatically and gracefully handle short reads that our providers may be doing on top of the short reads we may initiate ourselves. Obtained from: Juniper Networks, Inc.
Notes
Notes: svn path=/head/; revision=264504
Diffstat (limited to 'sys/geom/uzip')
-rw-r--r--sys/geom/uzip/g_uzip.c27
1 files changed, 22 insertions, 5 deletions
diff --git a/sys/geom/uzip/g_uzip.c b/sys/geom/uzip/g_uzip.c
index 94ad87bb06c0..7ba347712ab1 100644
--- a/sys/geom/uzip/g_uzip.c
+++ b/sys/geom/uzip/g_uzip.c
@@ -125,7 +125,7 @@ g_uzip_done(struct bio *bp)
struct g_consumer *cp;
struct g_geom *gp;
struct g_uzip_softc *sc;
- off_t pos, upos;
+ off_t iolen, pos, upos;
uint32_t start_blk, i;
size_t bsize;
@@ -153,11 +153,13 @@ g_uzip_done(struct bio *bp)
}
start_blk = bp2->bio_offset / sc->blksz;
bsize = pp2->sectorsize;
+ iolen = bp->bio_completed;
pos = sc->offsets[start_blk] % bsize;
upos = 0;
- DPRINTF(("%s: done: start_blk %d, pos %jd, upos %jd (%jd, %d, %zd)\n",
+ DPRINTF(("%s: done: start_blk %d, pos %jd, upos %jd, iolen %jd "
+ "(%jd, %d, %zd)\n",
gp->name, start_blk, (intmax_t)pos, (intmax_t)upos,
- (intmax_t)bp2->bio_offset, sc->blksz, bsize));
+ (intmax_t)iolen, (intmax_t)bp2->bio_offset, sc->blksz, bsize));
for (i = start_blk; upos < bp2->bio_length; i++) {
off_t len, ulen, uoff;
@@ -172,6 +174,12 @@ g_uzip_done(struct bio *bp)
bp2->bio_completed += ulen;
continue;
}
+ if (len > iolen) {
+ DPRINTF(("%s: done: early termination: len (%jd) > "
+ "iolen (%jd)\n",
+ gp->name, (intmax_t)len, (intmax_t)iolen));
+ break;
+ }
zs.next_in = bp->bio_data + pos;
zs.avail_in = len;
zs.next_out = sc->last_buf;
@@ -196,6 +204,7 @@ g_uzip_done(struct bio *bp)
mtx_unlock(&sc->last_mtx);
pos += len;
+ iolen -= len;
upos += ulen;
bp2->bio_completed += ulen;
err = inflateReset(&zs);
@@ -290,8 +299,16 @@ g_uzip_start(struct bio *bp)
pp2->name, pp2->sectorsize, (intmax_t)pp2->mediasize));
bsize = pp2->sectorsize;
bp2->bio_offset = sc->offsets[start_blk] - sc->offsets[start_blk] % bsize;
- bp2->bio_length = sc->offsets[end_blk] - bp2->bio_offset;
- bp2->bio_length = (bp2->bio_length + bsize - 1) / bsize * bsize;
+ while (1) {
+ bp2->bio_length = sc->offsets[end_blk] - bp2->bio_offset;
+ bp2->bio_length = (bp2->bio_length + bsize - 1) / bsize * bsize;
+ if (bp2->bio_length < MAXPHYS)
+ break;
+
+ end_blk--;
+ DPRINTF(("%s: bio_length (%jd) > MAXPHYS: lowering end_blk "
+ "to %u\n", gp->name, (intmax_t)bp2->bio_length, end_blk));
+ }
DPRINTF(("%s: start %jd + %jd -> %ju + %ju -> %jd + %jd\n",
gp->name,
(intmax_t)bp->bio_offset, (intmax_t)bp->bio_length,