diff options
author | Renato Botelho <renato@netgate.com> | 2015-12-17 11:13:55 -0200 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-12-17 11:13:55 -0200 |
commit | 02eb529700650bb0a6247479d6c83d206e0a5985 (patch) | |
tree | f2dd3e19d51cd578d33f2c231100e5248a937ad2 /sys/geom | |
parent | 4e66c4599e6123e69ca54036271685a3dde9a929 (diff) | |
parent | 5952006acb1f0577732ac15f481976227292807f (diff) | |
download | FreeBSD-src-02eb529700650bb0a6247479d6c83d206e0a5985.zip FreeBSD-src-02eb529700650bb0a6247479d6c83d206e0a5985.tar.gz |
Merge remote-tracking branch 'origin/stable/10' into devel
Diffstat (limited to 'sys/geom')
-rw-r--r-- | sys/geom/geom_disk.c | 188 | ||||
-rw-r--r-- | sys/geom/geom_io.c | 9 |
2 files changed, 159 insertions, 38 deletions
diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c index 9319b97..1a879f7 100644 --- a/sys/geom/geom_disk.c +++ b/sys/geom/geom_disk.c @@ -58,6 +58,8 @@ __FBSDID("$FreeBSD$"); #include <dev/led/led.h> +#include <machine/bus.h> + struct g_disk_softc { struct mtx done_mtx; struct disk *dp; @@ -273,6 +275,145 @@ g_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, int fflag, struct t return (error); } +static off_t +g_disk_maxsize(struct disk *dp, struct bio *bp) +{ + if (bp->bio_cmd == BIO_DELETE) + return (dp->d_delmaxsize); + return (dp->d_maxsize); +} + +static int +g_disk_maxsegs(struct disk *dp, struct bio *bp) +{ + return ((g_disk_maxsize(dp, bp) / PAGE_SIZE) + 1); +} + +static void +g_disk_advance(struct disk *dp, struct bio *bp, off_t off) +{ + + bp->bio_offset += off; + bp->bio_length -= off; + + if ((bp->bio_flags & BIO_VLIST) != 0) { + bus_dma_segment_t *seg, *end; + + seg = (bus_dma_segment_t *)bp->bio_data; + end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n; + off += bp->bio_ma_offset; + while (off >= seg->ds_len) { + KASSERT((seg != end), + ("vlist request runs off the end")); + off -= seg->ds_len; + seg++; + } + bp->bio_ma_offset = off; + bp->bio_ma_n = end - seg; + bp->bio_data = (void *)seg; + } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { + bp->bio_ma += off / PAGE_SIZE; + bp->bio_ma_offset += off; + bp->bio_ma_offset %= PAGE_SIZE; + bp->bio_ma_n -= off / PAGE_SIZE; + } else { + bp->bio_data += off; + } +} + +static void +g_disk_seg_limit(bus_dma_segment_t *seg, off_t *poffset, + off_t *plength, int *ppages) +{ + uintptr_t seg_page_base; + uintptr_t seg_page_end; + off_t offset; + off_t length; + int seg_pages; + + offset = *poffset; + length = *plength; + + if (length > seg->ds_len - offset) + length = seg->ds_len - offset; + + seg_page_base = trunc_page(seg->ds_addr + offset); + seg_page_end = round_page(seg->ds_addr + offset + length); + seg_pages = (seg_page_end - seg_page_base) >> PAGE_SHIFT; + + if (seg_pages > *ppages) { + seg_pages = *ppages; + length = (seg_page_base + (seg_pages << PAGE_SHIFT)) - + (seg->ds_addr + offset); + } + + *poffset = 0; + *plength -= length; + *ppages -= seg_pages; +} + +static off_t +g_disk_vlist_limit(struct disk *dp, struct bio *bp, bus_dma_segment_t **pendseg) +{ + bus_dma_segment_t *seg, *end; + off_t residual; + off_t offset; + int pages; + + seg = (bus_dma_segment_t *)bp->bio_data; + end = (bus_dma_segment_t *)bp->bio_data + bp->bio_ma_n; + residual = bp->bio_length; + offset = bp->bio_ma_offset; + pages = g_disk_maxsegs(dp, bp); + while (residual != 0 && pages != 0) { + KASSERT((seg != end), + ("vlist limit runs off the end")); + g_disk_seg_limit(seg, &offset, &residual, &pages); + seg++; + } + if (pendseg != NULL) + *pendseg = seg; + return (residual); +} + +static bool +g_disk_limit(struct disk *dp, struct bio *bp) +{ + bool limited = false; + off_t maxsz; + + maxsz = g_disk_maxsize(dp, bp); + + /* + * XXX: If we have a stripesize we should really use it here. + * Care should be taken in the delete case if this is done + * as deletes can be very sensitive to size given how they + * are processed. + */ + if (bp->bio_length > maxsz) { + bp->bio_length = maxsz; + limited = true; + } + + if ((bp->bio_flags & BIO_VLIST) != 0) { + bus_dma_segment_t *firstseg, *endseg; + off_t residual; + + firstseg = (bus_dma_segment_t*)bp->bio_data; + residual = g_disk_vlist_limit(dp, bp, &endseg); + if (residual != 0) { + bp->bio_ma_n = endseg - firstseg; + bp->bio_length -= residual; + limited = true; + } + } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { + bp->bio_ma_n = + howmany(bp->bio_ma_offset + bp->bio_length, PAGE_SIZE); + } + + return (limited); +} + static void g_disk_start(struct bio *bp) { @@ -297,6 +438,9 @@ g_disk_start(struct bio *bp) /* fall-through */ case BIO_READ: case BIO_WRITE: + KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO) != 0 || + (bp->bio_flags & BIO_UNMAPPED) == 0, + ("unmapped bio not supported by disk %s", dp->d_name)); off = 0; bp3 = NULL; bp2 = g_clone_bio(bp); @@ -304,39 +448,10 @@ g_disk_start(struct bio *bp) error = ENOMEM; break; } - do { - off_t d_maxsize; - - d_maxsize = (bp->bio_cmd == BIO_DELETE) ? - dp->d_delmaxsize : dp->d_maxsize; - bp2->bio_offset += off; - bp2->bio_length -= off; - if ((bp->bio_flags & BIO_UNMAPPED) == 0) { - bp2->bio_data += off; - } else { - KASSERT((dp->d_flags & DISKFLAG_UNMAPPED_BIO) - != 0, - ("unmapped bio not supported by disk %s", - dp->d_name)); - bp2->bio_ma += off / PAGE_SIZE; - bp2->bio_ma_offset += off; - bp2->bio_ma_offset %= PAGE_SIZE; - bp2->bio_ma_n -= off / PAGE_SIZE; - } - if (bp2->bio_length > d_maxsize) { - /* - * XXX: If we have a stripesize we should really - * use it here. Care should be taken in the delete - * case if this is done as deletes can be very - * sensitive to size given how they are processed. - */ - bp2->bio_length = d_maxsize; - if ((bp->bio_flags & BIO_UNMAPPED) != 0) { - bp2->bio_ma_n = howmany( - bp2->bio_ma_offset + - bp2->bio_length, PAGE_SIZE); - } - off += d_maxsize; + for (;;) { + if (g_disk_limit(dp, bp2)) { + off += bp2->bio_length; + /* * To avoid a race, we need to grab the next bio * before we schedule this one. See "notes". @@ -355,9 +470,14 @@ g_disk_start(struct bio *bp) g_disk_lock_giant(dp); dp->d_strategy(bp2); g_disk_unlock_giant(dp); + + if (bp3 == NULL) + break; + bp2 = bp3; bp3 = NULL; - } while (bp2 != NULL); + g_disk_advance(dp, bp2, off); + } break; case BIO_GETATTR: /* Give the driver a chance to override */ diff --git a/sys/geom/geom_io.c b/sys/geom/geom_io.c index f1edc70..9dff151 100644 --- a/sys/geom/geom_io.c +++ b/sys/geom/geom_io.c @@ -205,11 +205,12 @@ g_clone_bio(struct bio *bp) /* * BIO_ORDERED flag may be used by disk drivers to enforce * ordering restrictions, so this flag needs to be cloned. - * BIO_UNMAPPED should be inherited, to properly indicate - * which way the buffer is passed. + * BIO_UNMAPPED and BIO_VLIST should be inherited, to properly + * indicate which way the buffer is passed. * Other bio flags are not suitable for cloning. */ - bp2->bio_flags = bp->bio_flags & (BIO_ORDERED | BIO_UNMAPPED); + bp2->bio_flags = bp->bio_flags & + (BIO_ORDERED | BIO_UNMAPPED | BIO_VLIST); bp2->bio_length = bp->bio_length; bp2->bio_offset = bp->bio_offset; bp2->bio_data = bp->bio_data; @@ -240,7 +241,7 @@ g_duplicate_bio(struct bio *bp) struct bio *bp2; bp2 = uma_zalloc(biozone, M_WAITOK | M_ZERO); - bp2->bio_flags = bp->bio_flags & BIO_UNMAPPED; + bp2->bio_flags = bp->bio_flags & (BIO_UNMAPPED | BIO_VLIST); bp2->bio_parent = bp; bp2->bio_cmd = bp->bio_cmd; bp2->bio_length = bp->bio_length; |