Index: sys/kern/kern_physio.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_physio.c,v retrieving revision 1.93 diff -p -u -r1.93 kern_physio.c --- sys/kern/kern_physio.c 21 Apr 2015 10:54:52 -0000 1.93 +++ sys/kern/kern_physio.c 23 Mar 2019 10:21:19 -0000 @@ -75,6 +75,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_physio. #include #include +#include #include #include #include @@ -100,6 +101,7 @@ struct physio_stat { int ps_error; int ps_failed; off_t ps_endoffset; + size_t ps_resid; buf_t *ps_orig_bp; kmutex_t ps_lock; kcondvar_t ps_cv; @@ -152,6 +154,8 @@ physio_done(struct work *wk, void *dummy ps->ps_error = bp->b_error; } ps->ps_failed++; + + ps->ps_resid += todo - done; } else { KASSERT(bp->b_error == 0); } @@ -220,6 +224,7 @@ physio(void (*strategy)(struct buf *), s struct buf *bp = NULL; struct physio_stat *ps; int concurrency = physio_concurrency - 1; + int isdisk; error = RUN_ONCE(&physio_initialized, physio_init); if (__predict_false(error != 0)) { @@ -237,9 +242,15 @@ physio(void (*strategy)(struct buf *), s /* ps->ps_failed = 0; */ ps->ps_orig_bp = obp; ps->ps_endoffset = -1; + ps->ps_resid = 0; mutex_init(&ps->ps_lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&ps->ps_cv, "physio"); + /* Allow concurrent I/O only for disks */ + isdisk = cdev_type(dev) == D_DISK; + if (!isdisk) + concurrency = 0; + /* Make sure we have a buffer, creating one if necessary. */ if (obp != NULL) { mutex_enter(&bufcache_lock); @@ -291,11 +302,30 @@ physio(void (*strategy)(struct buf *), s /* Set up the buffer for a maximum-sized transfer. */ bp->b_blkno = btodb(uio->uio_offset); - if (dbtob(bp->b_blkno) != uio->uio_offset) { - error = EINVAL; - goto done; + if (isdisk) { + /* + * For disks, check that offsets are at least block + * aligned, the block addresses are used to track + * errors of finished requests. + */ + if (dbtob(bp->b_blkno) != uio->uio_offset) { + error = EINVAL; + goto done; + } + /* + * Split request into MAXPHYS chunks + */ + bp->b_bcount = MIN(MAXPHYS, iovp->iov_len); + } else { + /* + * Verify that buffer can handle size + */ + if (iovp->iov_len > MAXBSIZE) { + error = EINVAL; + goto done; + } + bp->b_bcount = iovp->iov_len; } - bp->b_bcount = MIN(MAXPHYS, iovp->iov_len); bp->b_data = iovp->iov_base; /* @@ -364,16 +394,25 @@ done_locked: physio_wait(ps, 0); mutex_exit(&ps->ps_lock); - if (ps->ps_failed != 0) { - off_t delta; + KASSERT(ps->ps_failed || ps->ps_endoffset == -1); - delta = uio->uio_offset - ps->ps_endoffset; - KASSERT(delta > 0); - uio->uio_resid += delta; - /* uio->uio_offset = ps->ps_endoffset; */ + /* + * Compute residual, for disks adjust for the + * lowest numbered block that returned an error. + */ + if (isdisk) { + if (ps->ps_failed != 0) { + off_t delta; + + delta = uio->uio_offset - ps->ps_endoffset; + KASSERT(delta > 0); + uio->uio_resid += delta; + /* uio->uio_offset = ps->ps_endoffset; */ + } } else { - KASSERT(ps->ps_endoffset == -1); + uio->uio_resid += ps->ps_resid; } + if (bp != NULL && bp != obp) { putiobuf(bp); }