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 <sys/param.h>
 #include <sys/systm.h>
+#include <sys/conf.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/once.h>
@@ -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);
 	}
