Module Name:    src
Committed By:   jdolecek
Date:           Sun Sep 10 19:31:15 UTC 2017

Modified Files:
        src/sys/dev/ata [jdolecek-ncq]: TODO.ncq ata.c ata_wdc.c atavar.h
        src/sys/dev/ic [jdolecek-ncq]: ahcisata_core.c mvsata.c siisata.c wdc.c
        src/sys/dev/scsipi [jdolecek-ncq]: atapi_wdc.c

Log Message:
refactor code so that xfer c_start() hook is called with channel mutex held,
and hence the controller submit code no longer relies on spl

tested all the affected drivers - wdc (via piixide), ahci, mvsata, siisata,
both disk and atapi I/O


To generate a diff of this commit:
cvs rdiff -u -r1.1.2.37 -r1.1.2.38 src/sys/dev/ata/TODO.ncq
cvs rdiff -u -r1.132.8.30 -r1.132.8.31 src/sys/dev/ata/ata.c
cvs rdiff -u -r1.105.6.8 -r1.105.6.9 src/sys/dev/ata/ata_wdc.c
cvs rdiff -u -r1.92.8.24 -r1.92.8.25 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.57.6.26 -r1.57.6.27 src/sys/dev/ic/ahcisata_core.c
cvs rdiff -u -r1.35.6.24 -r1.35.6.25 src/sys/dev/ic/mvsata.c
cvs rdiff -u -r1.30.4.36 -r1.30.4.37 src/sys/dev/ic/siisata.c
cvs rdiff -u -r1.283.2.13 -r1.283.2.14 src/sys/dev/ic/wdc.c
cvs rdiff -u -r1.123.4.12 -r1.123.4.13 src/sys/dev/scsipi/atapi_wdc.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ata/TODO.ncq
diff -u src/sys/dev/ata/TODO.ncq:1.1.2.37 src/sys/dev/ata/TODO.ncq:1.1.2.38
--- src/sys/dev/ata/TODO.ncq:1.1.2.37	Tue Aug 29 13:38:38 2017
+++ src/sys/dev/ata/TODO.ncq	Sun Sep 10 19:31:15 2017
@@ -2,11 +2,13 @@ Bugs
 ----
 test wd* at umass?, confirm the ata_channel kludge works
 
-c_start() needs to be called on splbio to avoid spurious irq during reset,
-is not e.g. in ata thread and may not in atastart() neither
+revise calls to atastart() - now called alsoafter ATASTART_ABORT(), call
+only from intr routine
 - wdc.c never calls atastart() (start always false)
 - ata_wdc.c calls atastart() regardless if error
 
+reconsider freeze/thaw in error recovery - can it screw up with thread?
+
 Other random notes (do outside the NCQ branch):
 -----------------------------------------------------
 do biodone() in wddone() starting the dump to not leak bufs when dumping from

Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.132.8.30 src/sys/dev/ata/ata.c:1.132.8.31
--- src/sys/dev/ata/ata.c:1.132.8.30	Sun Sep 10 19:22:56 2017
+++ src/sys/dev/ata/ata.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata.c,v 1.132.8.30 2017/09/10 19:22:56 jdolecek Exp $	*/
+/*	$NetBSD: ata.c,v 1.132.8.31 2017/09/10 19:31:15 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.30 2017/09/10 19:22:56 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.31 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -130,8 +130,12 @@ static bool atabus_suspend(device_t, con
 static void atabusconfig_thread(void *);
 
 static void ata_channel_idle(struct ata_channel *);
+static void ata_channel_thaw_locked(struct ata_channel *);
 static void ata_activate_xfer_locked(struct ata_channel *, struct ata_xfer *);
 static void ata_channel_freeze_locked(struct ata_channel *);
+static struct ata_xfer *ata_queue_get_active_xfer_locked(struct ata_channel *);
+static void ata_thread_wake_locked(struct ata_channel *);
+
 /*
  * atabus_init:
  *
@@ -200,7 +204,7 @@ ata_queue_hwslot_to_xfer(struct ata_chan
 	struct ata_queue *chq = chp->ch_queue;
 	struct ata_xfer *xfer = NULL;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	KASSERTMSG(hwslot < chq->queue_openings, "hwslot %d > openings %d",
 	    hwslot, chq->queue_openings);
@@ -212,7 +216,7 @@ ata_queue_hwslot_to_xfer(struct ata_chan
 			break;
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	KASSERTMSG((xfer != NULL),
 	    "%s: xfer with slot %d not found (active %x)", __func__,
@@ -221,6 +225,13 @@ ata_queue_hwslot_to_xfer(struct ata_chan
 	return xfer;
 }
 
+static struct ata_xfer *
+ata_queue_get_active_xfer_locked(struct ata_channel *chp)
+{
+	KASSERT(mutex_owned(&chp->ch_lock));
+	return TAILQ_FIRST(&chp->ch_queue->active_xfers);
+}
+
 /*
  * This interface is supposed only to be used when there is exactly
  * one outstanding command, when there is no information about the slot,
@@ -232,12 +243,12 @@ ata_queue_get_active_xfer(struct ata_cha
 {
 	struct ata_xfer *xfer = NULL;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	KASSERT(chp->ch_queue->queue_active <= 1);
-	xfer = TAILQ_FIRST(&chp->ch_queue->active_xfers);
+	xfer = ata_queue_get_active_xfer_locked(chp);
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	return xfer;
 }
@@ -247,7 +258,7 @@ ata_queue_drive_active_xfer(struct ata_c
 {
 	struct ata_xfer *xfer = NULL;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	TAILQ_FOREACH(xfer, &chp->ch_queue->active_xfers, c_activechain) {
 		if (xfer->c_drive == drive)
@@ -255,7 +266,7 @@ ata_queue_drive_active_xfer(struct ata_c
 	}
 	KASSERT(xfer != NULL);
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	return xfer;
 }
@@ -374,9 +385,9 @@ atabusconfig(struct atabus_softc *atabus
 	int i, error;
 
 	/* we are in the atabus's thread context */
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	chp->ch_flags |= ATACH_TH_RUN;
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	/*
 	 * Probe for the drives attached to controller, unless a PMP
@@ -393,9 +404,9 @@ atabusconfig(struct atabus_softc *atabus
 	}
 
 	/* next operations will occurs in a separate thread */
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	chp->ch_flags &= ~ATACH_TH_RUN;
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	/* Make sure the devices probe in atabus order to avoid jitter. */
 	mutex_enter(&atabus_qlock);
@@ -407,7 +418,7 @@ atabusconfig(struct atabus_softc *atabus
 	}
 	mutex_exit(&atabus_qlock);
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	/* If no drives, abort here */
 	if (chp->ch_drive == NULL)
@@ -423,7 +434,7 @@ atabusconfig(struct atabus_softc *atabus
 	if (chp->ch_flags & ATACH_SHUTDOWN)
 		goto out;
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	if ((error = kthread_create(PRI_NONE, 0, NULL, atabusconfig_thread,
 	    atabus_sc, &atabus_cfg_lwp,
@@ -433,7 +444,7 @@ atabusconfig(struct atabus_softc *atabus
 	return;
 
  out:
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	mutex_enter(&atabus_qlock);
 	TAILQ_REMOVE(&atabus_initq_head, atabus_initq, atabus_initq);
@@ -586,9 +597,9 @@ atabus_thread(void *arg)
 	struct ata_channel *chp = sc->sc_chan;
 	struct ata_queue *chq = chp->ch_queue;
 	struct ata_xfer *xfer;
-	int i, s;
+	int i, rv, s;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	chp->ch_flags |= ATACH_TH_RUN;
 
 	/*
@@ -602,11 +613,11 @@ atabus_thread(void *arg)
 		chp->ch_drive[i].drive_flags = 0;
 		chp->ch_drive[i].drive_type = ATA_DRIVET_NONE;
 	}
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	atabusconfig(sc);
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	for (;;) {
 		if ((chp->ch_flags & (ATACH_TH_RESET | ATACH_SHUTDOWN)) == 0 &&
 		    (chq->queue_active == 0 || chq->queue_freeze == 0)) {
@@ -619,21 +630,21 @@ atabus_thread(void *arg)
 		}
 		if (chp->ch_flags & ATACH_TH_RESCAN) {
 			chp->ch_flags &= ~ATACH_TH_RESCAN;
-			mutex_exit(&chp->ch_lock);
+			ata_channel_unlock(chp);
 			atabusconfig(sc);
-			mutex_enter(&chp->ch_lock);
+			ata_channel_lock(chp);
 		}
 		if (chp->ch_flags & ATACH_TH_RESET) {
 			/*
 			 * ata_reset_channel() will freeze 2 times, so
 			 * unfreeze one time. Not a problem as we're at splbio
 			 */
-			mutex_exit(&chp->ch_lock);
-			ata_channel_thaw(chp);
+			ata_channel_thaw_locked(chp);
+			ata_channel_unlock(chp);
 			s = splbio();
 			ata_reset_channel(chp, AT_WAIT | chp->ch_reset_flags);
 			splx(s);
-			mutex_enter(&chp->ch_lock);
+			ata_channel_lock(chp);
 		} else if (chq->queue_active > 0 && chq->queue_freeze == 1) {
 			/*
 			 * Caller has bumped queue_freeze, decrease it. This
@@ -641,31 +652,39 @@ atabus_thread(void *arg)
 			 */
 			KASSERT((chp->ch_flags & ATACH_NCQ) == 0);
 			KASSERT(chq->queue_active == 1);
-			mutex_exit(&chp->ch_lock);
 
-			ata_channel_thaw(chp);
-			xfer = ata_queue_get_active_xfer(chp);
+			ata_channel_thaw_locked(chp);
+			xfer = ata_queue_get_active_xfer_locked(chp);
+
 			KASSERT(xfer != NULL);
-			s = splbio();
-			(*xfer->c_start)(xfer->c_chp, xfer);
-			splx(s);
-			mutex_enter(&chp->ch_lock);
+			KASSERT((xfer->c_flags & C_POLL) == 0);
+
+			switch ((rv = ata_xfer_start(xfer))) {
+			case ATASTART_STARTED:
+			case ATASTART_POLL:
+			case ATASTART_ABORT:
+				break;
+			case ATASTART_TH:
+			default:
+				panic("%s: ata_xfer_start() unexpected rv %d",
+				    __func__, rv);
+				/* NOTREACHED */
+			}
 		} else if (chq->queue_freeze > 1)
 			panic("%s: queue_freeze", __func__);
 	}
 	chp->ch_thread = NULL;
 	cv_signal(&chp->ch_thr_idle);
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 	kthread_exit(0);
 }
 
-void
-ata_thread_wake(struct ata_channel *chp)
+static void
+ata_thread_wake_locked(struct ata_channel *chp)
 {
-	mutex_enter(&chp->ch_lock);
+	KASSERT(mutex_owned(&chp->ch_lock));
 	ata_channel_freeze_locked(chp);
 	cv_signal(&chp->ch_thr_idle);
-	mutex_exit(&chp->ch_lock);
 }
 
 /*
@@ -743,13 +762,13 @@ atabus_detach(device_t self, int flags)
 	int i, error = 0;
 
 	/* Shutdown the channel. */
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	chp->ch_flags |= ATACH_SHUTDOWN;
 	while (chp->ch_thread != NULL) {
 		cv_signal(&chp->ch_thr_idle);
 		cv_wait(&chp->ch_thr_idle, &chp->ch_lock);
 	}
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	/*
 	 * Detach atapibus and its children.
@@ -1199,7 +1218,7 @@ ata_exec_xfer(struct ata_channel *chp, s
 	/* complete xfer setup */
 	xfer->c_chp = chp;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	/*
 	 * Standard commands are added to the end of command list, but
@@ -1235,7 +1254,7 @@ ata_exec_xfer(struct ata_channel *chp, s
 		}
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	atastart(chp);
 }
@@ -1268,9 +1287,9 @@ atastart(struct ata_channel *chp)
 	splx(spl1);
 #endif /* ATA_DEBUG */
 
-again:
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
+again:
 	KASSERT(chq->queue_active <= chq->queue_openings);
 	if (chq->queue_active == chq->queue_openings) {
 		goto out; /* channel completely busy */
@@ -1340,22 +1359,57 @@ again:
 	if (atac->atac_cap & ATAC_CAP_NOIRQ)
 		KASSERT(xfer->c_flags & C_POLL);
 
-	mutex_exit(&chp->ch_lock);
-
-	/*
-	 * XXX MPSAFE can't keep the lock, xfer->c_start() might call the done
-	 * routine for polled commands.
-	 */
-	xfer->c_start(chp, xfer);
+	switch (ata_xfer_start(xfer)) {
+	case ATASTART_TH:
+	case ATASTART_ABORT:
+		/* don't start any further commands in this case */
+		goto out;
+	default:
+		/* nothing to do */
+		break;
+	}
 
 	/* Queue more commands if possible, but not during recovery */
 	if (!recovery && chq->queue_active < chq->queue_openings)
 		goto again;
 
-	return;
-
 out:
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
+}
+
+int
+ata_xfer_start(struct ata_xfer *xfer)
+{
+	struct ata_channel *chp = xfer->c_chp;
+	int rv;
+
+	KASSERT(mutex_owned(&chp->ch_lock));
+
+	rv = xfer->c_start(chp, xfer);
+	switch (rv) {
+	case ATASTART_STARTED:
+		/* nothing to do */
+		break;
+	case ATASTART_TH:
+		/* postpone xfer to thread */
+		ata_thread_wake_locked(chp);
+		break;
+	case ATASTART_POLL:
+		/* can happen even in thread context for some ATAPI devices */
+		ata_channel_unlock(chp);
+		KASSERT(xfer->c_poll != NULL);
+		xfer->c_poll(chp, xfer);
+		ata_channel_lock(chp);
+		break;
+	case ATASTART_ABORT:
+		ata_channel_unlock(chp);
+		KASSERT(xfer->c_abort != NULL);
+		xfer->c_abort(chp, xfer);
+		ata_channel_lock(chp);
+		break;
+	}
+
+	return rv;
 }
 
 /*
@@ -1376,7 +1430,7 @@ ata_get_xfer_ext(struct ata_channel *chp
 	    __func__, chp->ch_channel, flags, openings),
 	    DEBUG_XFERS);
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	/*
 	 * When openings is just 1, can't reserve anything for
@@ -1430,7 +1484,7 @@ retry:
 	    sizeof(struct ata_xfer) - offsetof(struct ata_xfer, c_startzero));
 
 out:
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 	return xfer;
 }
 
@@ -1442,7 +1496,7 @@ ata_free_xfer(struct ata_channel *chp, s
 {
 	struct ata_queue *chq = chp->ch_queue;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	if (xfer->c_flags & (C_WAITACT|C_WAITTIMO)) {
 		/* Someone is waiting for this xfer, so we can't free now */
@@ -1472,7 +1526,7 @@ out:
 		cv_broadcast(&chq->queue_busy);
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 }
 
 static void
@@ -1496,7 +1550,7 @@ ata_deactivate_xfer(struct ata_channel *
 {
 	struct ata_queue * const chq = chp->ch_queue;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	KASSERT(chq->queue_active > 0);
 	KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) != 0);
@@ -1510,7 +1564,7 @@ ata_deactivate_xfer(struct ata_channel *
 	chq->active_xfers_used &= ~__BIT(xfer->c_slot);
 	chq->queue_active--;
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 }
 
 /*
@@ -1528,20 +1582,20 @@ ata_waitdrain_xfer_check(struct ata_chan
 	int drive = xfer->c_drive;
 	bool draining = false;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	if (chp->ch_drive[drive].drive_flags & ATA_DRIVE_WAITDRAIN) {
-		mutex_exit(&chp->ch_lock);
+		ata_channel_unlock(chp);
 
 		(*xfer->c_kill_xfer)(chp, xfer, KILL_GONE);
 
-		mutex_enter(&chp->ch_lock);
+		ata_channel_lock(chp);
 		chp->ch_drive[drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN;
 		cv_signal(&chp->ch_queue->queue_drain);
 		draining = true;
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	return draining;
 }
@@ -1555,7 +1609,7 @@ ata_timo_xfer_check(struct ata_xfer *xfe
 	struct ata_channel *chp = xfer->c_chp;
 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	callout_ack(&xfer->c_timo_callout);
 
@@ -1565,7 +1619,7 @@ ata_timo_xfer_check(struct ata_xfer *xfe
 		/* Handle race vs. ata_free_xfer() */
 		if (xfer->c_flags & C_FREE) {
 			xfer->c_flags &= ~C_FREE;
-			mutex_exit(&chp->ch_lock);
+			ata_channel_unlock(chp);
 
 	    		aprint_normal_dev(drvp->drv_softc,
 			    "xfer %d freed while invoking timeout\n",
@@ -1577,7 +1631,7 @@ ata_timo_xfer_check(struct ata_xfer *xfe
 
 		/* Handle race vs. callout_stop() in ata_deactivate_xfer() */
 		if (!callout_expired(&xfer->c_timo_callout)) {
-			mutex_exit(&chp->ch_lock);
+			ata_channel_unlock(chp);
 
 	    		aprint_normal_dev(drvp->drv_softc,
 			    "xfer %d deactivated while invoking timeout\n",
@@ -1586,7 +1640,7 @@ ata_timo_xfer_check(struct ata_xfer *xfe
 		}
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	/* No race, proceed with timeout handling */
 	return false;
@@ -1645,7 +1699,7 @@ ata_kill_pending(struct ata_drive_datas 
 	struct ata_queue * const chq = chp->ch_queue;
 	struct ata_xfer *xfer, *xfernext;
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 
 	/* Kill all pending transfers */
 	TAILQ_FOREACH_SAFE(xfer, &chq->queue_xfer, c_xferchain, xfernext) {
@@ -1687,7 +1741,7 @@ ata_kill_pending(struct ata_drive_datas 
 		cv_wait(&chq->queue_drain, &chp->ch_lock);
 	}
 
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 }
 
 static void
@@ -1699,17 +1753,25 @@ ata_channel_freeze_locked(struct ata_cha
 void
 ata_channel_freeze(struct ata_channel *chp)
 {
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	ata_channel_freeze_locked(chp);
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
+}
+
+static void
+ata_channel_thaw_locked(struct ata_channel *chp)
+{
+	KASSERT(mutex_owned(&chp->ch_lock));
+
+	chp->ch_queue->queue_freeze--;
 }
 
 void
 ata_channel_thaw(struct ata_channel *chp)
 {
-	mutex_enter(&chp->ch_lock);
-	chp->ch_queue->queue_freeze--;
-	mutex_exit(&chp->ch_lock);
+	ata_channel_lock(chp);
+	ata_channel_thaw_locked(chp);
+	ata_channel_unlock(chp);
 }
 
 /*
@@ -1752,23 +1814,23 @@ ata_reset_channel(struct ata_channel *ch
 			ata_channel_thaw(chp);
 			return;
 		}
-		mutex_enter(&chp->ch_lock);
+		ata_channel_lock(chp);
 		chp->ch_flags |= ATACH_TH_RESET;
 		chp->ch_reset_flags = flags & AT_RST_EMERG;
 		cv_signal(&chp->ch_thr_idle);
-		mutex_exit(&chp->ch_lock);
+		ata_channel_unlock(chp);
 		return;
 	}
 
 	(*atac->atac_bustype_ata->ata_reset_channel)(chp, flags);
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	KASSERT(chp->ch_ndrives == 0 || chp->ch_drive != NULL);
 	for (drive = 0; drive < chp->ch_ndrives; drive++)
 		chp->ch_drive[drive].state = 0;
 
 	chp->ch_flags &= ~ATACH_TH_RESET;
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	if (flags & AT_RST_EMERG) {
 		/* make sure that we can use polled commands */
@@ -2350,13 +2412,13 @@ atabus_resume(device_t dv, const pmf_qua
 	 * XXX joerg: with wdc, the first channel unfreezes the controler.
 	 * Move this the reset and queue idling into wdc.
 	 */
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	if (chp->ch_queue->queue_freeze == 0) {
-		mutex_exit(&chp->ch_lock);
+		ata_channel_unlock(chp);
 		goto out;
 	}
 	KASSERT(chp->ch_queue->queue_freeze > 0);
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	/* unfreeze the queue and reset drives */
 	ata_channel_thaw(chp);
@@ -2401,10 +2463,10 @@ atabus_rescan(device_t self, const char 
 	mutex_exit(&atabus_qlock);
 	config_pending_incr(sc->sc_dev);
 
-	mutex_enter(&chp->ch_lock);
+	ata_channel_lock(chp);
 	chp->ch_flags |= ATACH_TH_RESCAN;
 	cv_signal(&chp->ch_thr_idle);
-	mutex_exit(&chp->ch_lock);
+	ata_channel_unlock(chp);
 
 	return 0;
 }
@@ -2500,3 +2562,21 @@ ata_channel_start(struct ata_channel *ch
 	splx(s);
 #undef ATA_DRIVE_START
 }
+
+void
+ata_channel_lock(struct ata_channel *chp)
+{
+	mutex_enter(&chp->ch_lock);
+}
+
+void
+ata_channel_unlock(struct ata_channel *chp)
+{
+	mutex_exit(&chp->ch_lock);
+}
+
+void
+ata_channel_lock_owned(struct ata_channel *chp)
+{
+	KASSERT(mutex_owned(&chp->ch_lock));
+}

Index: src/sys/dev/ata/ata_wdc.c
diff -u src/sys/dev/ata/ata_wdc.c:1.105.6.8 src/sys/dev/ata/ata_wdc.c:1.105.6.9
--- src/sys/dev/ata/ata_wdc.c:1.105.6.8	Sat Aug 12 14:41:54 2017
+++ src/sys/dev/ata/ata_wdc.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata_wdc.c,v 1.105.6.8 2017/08/12 14:41:54 jdolecek Exp $	*/
+/*	$NetBSD: ata_wdc.c,v 1.105.6.9 2017/09/10 19:31:15 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001, 2003 Manuel Bouyer.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.105.6.8 2017/08/12 14:41:54 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.105.6.9 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include "opt_ata.h"
 #include "opt_wdc.h"
@@ -104,8 +104,9 @@ extern int wdcdebug_wd_mask; /* inited i
 #define ATA_DELAY 10000 /* 10s for a drive I/O */
 
 static int	wdc_ata_bio(struct ata_drive_datas*, struct ata_xfer *);
-static void	wdc_ata_bio_start(struct ata_channel *,struct ata_xfer *);
-static void	_wdc_ata_bio_start(struct ata_channel *,struct ata_xfer *);
+static int	wdc_ata_bio_start(struct ata_channel *,struct ata_xfer *);
+static int	_wdc_ata_bio_start(struct ata_channel *,struct ata_xfer *);
+static void	wdc_ata_bio_poll(struct ata_channel *,struct ata_xfer *);
 static int	wdc_ata_bio_intr(struct ata_channel *, struct ata_xfer *,
 				 int);
 static void	wdc_ata_bio_kill_xfer(struct ata_channel *,
@@ -161,13 +162,15 @@ wdc_ata_bio(struct ata_drive_datas *drvp
 	xfer->c_databuf = ata_bio->databuf;
 	xfer->c_bcount = ata_bio->bcount;
 	xfer->c_start = wdc_ata_bio_start;
+	xfer->c_poll = wdc_ata_bio_poll;
+	xfer->c_abort = wdc_ata_bio_done;
 	xfer->c_intr = wdc_ata_bio_intr;
 	xfer->c_kill_xfer = wdc_ata_bio_kill_xfer;
 	ata_exec_xfer(chp, xfer);
 	return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
 }
 
-static void
+static int
 wdc_ata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct atac_softc *atac = chp->ch_atac;
@@ -189,6 +192,8 @@ wdc_ata_bio_start(struct ata_channel *ch
 	    drvp->state, drvp->drive_flags, xfer->c_flags, chp->ch_flags),
 	    DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
+
 	/* Do control operations specially. */
 	if (__predict_false(drvp->state < READY)) {
 		/*
@@ -199,8 +204,7 @@ wdc_ata_bio_start(struct ata_channel *ch
 		/* If it's not a polled command, we need the kernel thread */
 		if ((xfer->c_flags & C_POLL) == 0 &&
 		    (chp->ch_flags & ATACH_TH_RUN) == 0) {
-			ata_thread_wake(chp);
-			return;
+			return ATASTART_TH;
 		}
 		/*
 		 * disable interrupts, all commands here should be quick
@@ -297,8 +301,7 @@ ready:
 		delay(10); /* some drives need a little delay here */
 	}
 
-	_wdc_ata_bio_start(chp, xfer);
-	return;
+	return _wdc_ata_bio_start(chp, xfer);
 ctrltimeout:
 	printf("%s:%d:%d: %s timed out\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
@@ -319,15 +322,14 @@ ctrlerror:
 	}
 ctrldone:
 	drvp->state = 0;
-	wdc_ata_bio_done(chp, xfer);
 
 	if (! (wdc->cap & WDC_CAPABILITY_NO_AUXCTL))
 		bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, 
 		    WDCTL_4BIT);
-	return;
+	return ATASTART_ABORT;
 }
 
-static void
+static int
 _wdc_ata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct atac_softc *atac = chp->ch_atac;
@@ -358,7 +360,6 @@ _wdc_ata_bio_start(struct ata_channel *c
 			dma_flags |= WDC_DMA_LBA48;
 	}
 #endif
-again:
 	/*
 	 *
 	 * When starting a multi-sector transfer, or doing single-sector
@@ -438,8 +439,7 @@ again:
 				}
 				ata_bio->error = ERR_DMA;
 				ata_bio->r_error = 0;
-				wdc_ata_bio_done(chp, xfer);
-				return;
+				return ATASTART_ABORT;
 			}
 			/* Initiate command */
 			if (wdc->select)
@@ -453,7 +453,7 @@ again:
 			case WDCWAIT_TOUT:
 				goto timeout;
 			case WDCWAIT_THR:
-				return;
+				return ATASTART_TH;
 			}
 			if (ata_bio->flags & ATA_LBA48) {
 			    uint8_t device = WDSD_LBA;
@@ -505,8 +505,7 @@ again:
 					} else {
 						ata_bio->error = ERR_DMA;
 						ata_bio->r_error = 0;
-						wdc_ata_bio_done(chp, xfer);
-						return;
+						return ATASTART_ABORT;
 					}
 				}
 			}
@@ -533,7 +532,7 @@ again:
 		case WDCWAIT_TOUT:
 			goto timeout;
 		case WDCWAIT_THR:
-			return;
+			return ATASTART_TH;
 		}
 		if (ata_bio->flags & ATA_LBA48) {
 		    wdccommandext(chp, xfer->c_drive, atacmd_to48(cmd),
@@ -570,12 +569,10 @@ again:
 			    ATACH_ST(tfd), ATACH_ERR(tfd));
 			if (wdc_ata_err(drvp, ata_bio, tfd) != WDC_ATA_ERR)
 				ata_bio->error = TIMEOUT;
-			wdc_ata_bio_done(chp, xfer);
-			return;
+			return ATASTART_ABORT;
 		}
 		if (wdc_ata_err(drvp, ata_bio, tfd) == WDC_ATA_ERR) {
-			wdc_ata_bio_done(chp, xfer);
-			return;
+			return ATASTART_ABORT;
 		}
 #if NATA_PIOBM
 		if (xfer->c_flags & C_PIOBM) {
@@ -595,28 +592,29 @@ again:
 intr:
 #endif
 	/* Wait for IRQ (either real or polled) */
-	if ((ata_bio->flags & ATA_POLL) != 0) {
-		/* Wait for at last 400ns for status bit to be valid */
-		delay(1);
-#if NATA_DMA
-		if (chp->ch_flags & ATACH_DMA_WAIT) {
-			wdc_dmawait(chp, xfer, ATA_DELAY);
-			chp->ch_flags &= ~ATACH_DMA_WAIT;
-		}
-#endif
-		wdc_ata_bio_intr(chp, xfer, 0);
-		if ((ata_bio->flags & ATA_ITSDONE) == 0)
-			goto again;
-	}
-	return;
+	return (ata_bio->flags & ATA_POLL) ? ATASTART_POLL : ATASTART_STARTED;
+
 timeout:
 	printf("%s:%d:%d: not ready, st=0x%02x, err=0x%02x\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
 	    ATACH_ST(tfd), ATACH_ERR(tfd));
 	if (wdc_ata_err(drvp, ata_bio, tfd) != WDC_ATA_ERR)
 		ata_bio->error = TIMEOUT;
-	wdc_ata_bio_done(chp, xfer);
-	return;
+	return ATASTART_ABORT;
+}
+
+static void
+wdc_ata_bio_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	/* Wait for at last 400ns for status bit to be valid */
+	delay(1);
+#if NATA_DMA
+	if (chp->ch_flags & ATACH_DMA_WAIT) {
+		wdc_dmawait(chp, xfer, ATA_DELAY);
+		chp->ch_flags &= ~ATACH_DMA_WAIT;
+	}
+#endif
+	wdc_ata_bio_intr(chp, xfer, 0);
 }
 
 static int
@@ -633,6 +631,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive),
 	    DEBUG_INTR | DEBUG_XFERS);
 
+	ata_channel_lock(chp);
 
 	/* Is it not a transfer, but a control operation? */
 	if (drvp->state < READY) {
@@ -648,6 +647,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 	 */
 	if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
 		ata_bio->error = TIMEOUT;
+		ata_channel_unlock(chp);
 		wdc_ata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -668,6 +668,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 		    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
 		    xfer->c_bcount, xfer->c_skip);
 		ata_bio->error = TIMEOUT;
+		ata_channel_unlock(chp);
 		wdc_ata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -722,6 +723,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 
 	/* if we had an error, end */
 	if (drv_err == WDC_ATA_ERR) {
+		ata_channel_unlock(chp);
 		wdc_ata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -733,6 +735,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 			    device_xname(atac->atac_dev), chp->ch_channel,
 			    xfer->c_drive);
 			ata_bio->error = TIMEOUT;
+			ata_channel_unlock(chp);
 			wdc_ata_bio_done(chp, xfer);
 			return 1;
 		}
@@ -744,6 +747,7 @@ wdc_ata_bio_intr(struct ata_channel *chp
 			    xfer->c_skip, ata_bio->nbytes,
 			    WDC_PIOBM_XFER_IRQ);
 			chp->ch_flags |= ATACH_DMA_WAIT | ATACH_PIOBM_WAIT;
+			ata_channel_unlock(chp);
 			return 1;
 		} else
 #endif
@@ -758,17 +762,19 @@ end:
 	ata_bio->blkdone += ata_bio->nblks;
 	xfer->c_skip += ata_bio->nbytes;
 	xfer->c_bcount -= ata_bio->nbytes;
+
 	/* See if this transfer is complete. */
 	if (xfer->c_bcount > 0) {
 		if ((ata_bio->flags & ATA_POLL) == 0) {
 			/* Start the next operation */
-			_wdc_ata_bio_start(chp, xfer);
+			ata_xfer_start(xfer);
 		} else {
 			/* Let _wdc_ata_bio_start do the loop */
-			return 1;
 		}
+		ata_channel_unlock(chp);
 	} else { /* Done with this transfer */
 		ata_bio->error = NOERROR;
+		ata_channel_unlock(chp);
 		wdc_ata_bio_done(chp, xfer);
 	}
 	return 1;

Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.92.8.24 src/sys/dev/ata/atavar.h:1.92.8.25
--- src/sys/dev/ata/atavar.h:1.92.8.24	Sat Aug 12 22:12:04 2017
+++ src/sys/dev/ata/atavar.h	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: atavar.h,v 1.92.8.24 2017/08/12 22:12:04 jdolecek Exp $	*/
+/*	$NetBSD: atavar.h,v 1.92.8.25 2017/09/10 19:31:15 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -166,7 +166,13 @@ struct ata_xfer {
 	TAILQ_ENTRY(ata_xfer) c_activechain;
 
 	/* Low-level protocol handlers. */
-	void	(*c_start)(struct ata_channel *, struct ata_xfer *);
+	int	(*c_start)(struct ata_channel *, struct ata_xfer *);
+#define ATASTART_STARTED	0	/* xfer started, waiting for intr */
+#define ATASTART_TH		1	/* xfer needs to be run in thread */
+#define ATASTART_POLL		2	/* xfer needs to be polled */
+#define ATASTART_ABORT		3	/* error occurred, abort xfer */
+	void	(*c_poll)(struct ata_channel *, struct ata_xfer *);
+	void	(*c_abort)(struct ata_channel *, struct ata_xfer *);
 	int	(*c_intr)(struct ata_channel *, struct ata_xfer *, int);
 	void	(*c_kill_xfer)(struct ata_channel *, struct ata_xfer *, int);
 };
@@ -508,6 +514,7 @@ struct ata_xfer *ata_get_xfer_ext(struct
 void	ata_free_xfer(struct ata_channel *, struct ata_xfer *);
 void	ata_deactivate_xfer(struct ata_channel *, struct ata_xfer *);
 void	ata_exec_xfer(struct ata_channel *, struct ata_xfer *);
+int	ata_xfer_start(struct ata_xfer *xfer);
 
 void	ata_timeout(void *);
 bool	ata_timo_xfer_check(struct ata_xfer *);
@@ -517,7 +524,9 @@ void	ata_reset_channel(struct ata_channe
 void	ata_channel_freeze(struct ata_channel *);
 void	ata_channel_thaw(struct ata_channel *);
 void	ata_channel_start(struct ata_channel *, int);
-void	ata_thread_wake(struct ata_channel *);
+void	ata_channel_lock(struct ata_channel *);
+void	ata_channel_unlock(struct ata_channel *);
+void	ata_channel_lock_owned(struct ata_channel *);
 
 int	ata_addref(struct ata_channel *);
 void	ata_delref(struct ata_channel *);

Index: src/sys/dev/ic/ahcisata_core.c
diff -u src/sys/dev/ic/ahcisata_core.c:1.57.6.26 src/sys/dev/ic/ahcisata_core.c:1.57.6.27
--- src/sys/dev/ic/ahcisata_core.c:1.57.6.26	Sat Aug 12 22:12:04 2017
+++ src/sys/dev/ic/ahcisata_core.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ahcisata_core.c,v 1.57.6.26 2017/08/12 22:12:04 jdolecek Exp $	*/
+/*	$NetBSD: ahcisata_core.c,v 1.57.6.27 2017/09/10 19:31:15 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 2006 Manuel Bouyer.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.57.6.26 2017/08/12 22:12:04 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.57.6.27 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <sys/malloc.h>
@@ -66,12 +66,16 @@ static int  ahci_ata_addref(struct ata_d
 static void ahci_ata_delref(struct ata_drive_datas *);
 static void ahci_killpending(struct ata_drive_datas *);
 
-static void ahci_cmd_start(struct ata_channel *, struct ata_xfer *);
+static int ahci_cmd_start(struct ata_channel *, struct ata_xfer *);
 static int  ahci_cmd_complete(struct ata_channel *, struct ata_xfer *, int);
-static void ahci_cmd_done(struct ata_channel *, struct ata_xfer *, int);
+static void ahci_cmd_poll(struct ata_channel *, struct ata_xfer *);
+static void ahci_cmd_abort(struct ata_channel *, struct ata_xfer *);
+static void ahci_cmd_done(struct ata_channel *, struct ata_xfer *);
 static void ahci_cmd_done_end(struct ata_channel *, struct ata_xfer *);
 static void ahci_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
-static void ahci_bio_start(struct ata_channel *, struct ata_xfer *);
+static int ahci_bio_start(struct ata_channel *, struct ata_xfer *);
+static void ahci_bio_poll(struct ata_channel *, struct ata_xfer *);
+static void ahci_bio_abort(struct ata_channel *, struct ata_xfer *);
 static int  ahci_bio_complete(struct ata_channel *, struct ata_xfer *, int);
 static void ahci_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int) ;
 static void ahci_channel_stop(struct ahci_softc *, struct ata_channel *, int);
@@ -86,7 +90,9 @@ static void ahci_atapi_kill_pending(stru
 static void ahci_atapi_minphys(struct buf *);
 static void ahci_atapi_scsipi_request(struct scsipi_channel *,
     scsipi_adapter_req_t, void *);
-static void ahci_atapi_start(struct ata_channel *, struct ata_xfer *);
+static int ahci_atapi_start(struct ata_channel *, struct ata_xfer *);
+static void ahci_atapi_poll(struct ata_channel *, struct ata_xfer *);
+static void ahci_atapi_abort(struct ata_channel *, struct ata_xfer *);
 static int  ahci_atapi_complete(struct ata_channel *, struct ata_xfer *, int);
 static void ahci_atapi_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
 static void ahci_atapi_probe_device(struct atapibus_softc *, int);
@@ -984,6 +990,8 @@ ahci_exec_command(struct ata_drive_datas
 	xfer->c_databuf = ata_c->data;
 	xfer->c_bcount = ata_c->bcount;
 	xfer->c_start = ahci_cmd_start;
+	xfer->c_poll = ahci_cmd_poll;
+	xfer->c_abort = ahci_cmd_abort;
 	xfer->c_intr = ahci_cmd_complete;
 	xfer->c_kill_xfer = ahci_cmd_kill_xfer;
 	s = splbio();
@@ -1009,7 +1017,7 @@ ahci_exec_command(struct ata_drive_datas
 	return ret;
 }
 
-static void
+static int
 ahci_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct ahci_softc *sc = AHCI_CH2SC(chp);
@@ -1018,13 +1026,13 @@ ahci_cmd_start(struct ata_channel *chp, 
 	int slot = xfer->c_slot;
 	struct ahci_cmd_tbl *cmd_tbl;
 	struct ahci_cmd_header *cmd_h;
-	int i;
 
 	AHCIDEBUG_PRINT(("ahci_cmd_start CI 0x%x timo %d\n slot %d",
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel)),
 	    ata_c->timeout, slot),
 	    DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
 	KASSERT((achp->ahcic_cmds_active & (1 << slot)) == 0);
 
 	cmd_tbl = achp->ahcic_cmd_tbl[slot];
@@ -1043,8 +1051,7 @@ ahci_cmd_start(struct ata_channel *chp, 
 	    ata_c->bcount,
 	    (ata_c->flags & AT_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)) {
 		ata_c->flags |= AT_DF;
-		xfer->c_intr(chp, xfer, 0);
-		return;
+		return ATASTART_ABORT;
 	}
 	cmd_h->cmdh_flags = htole16(
 	    ((ata_c->flags & AT_WRITE) ? AHCI_CMDH_F_WR : 0) |
@@ -1066,16 +1073,25 @@ ahci_cmd_start(struct ata_channel *chp, 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(ata_c->timeout),
 		    ata_timeout, xfer);
-		return;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+static void
+ahci_cmd_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct ahci_softc *sc = AHCI_CH2SC(chp);
+	struct ahci_channel *achp = (struct ahci_channel *)chp;
+
 	/*
 	 * Polled command. 
 	 */
-	for (i = 0; i < ata_c->timeout / 10; i++) {
-		if (ata_c->flags & AT_DONE)
+	for (int i = 0; i < xfer->c_ata_c.timeout / 10; i++) {
+		if (xfer->c_ata_c.flags & AT_DONE)
 			break;
 		ahci_intr_port(sc, achp);
-		ata_delay(10, "ahcipl", ata_c->flags);
+		ata_delay(10, "ahcipl", xfer->c_ata_c.flags);
 	}
 	AHCIDEBUG_PRINT(("%s port %d poll end GHC 0x%x IS 0x%x list 0x%x%x fis 0x%x%x CMD 0x%x CI 0x%x\n", AHCINAME(sc), chp->ch_channel, 
 	    AHCI_READ(sc, AHCI_GHC), AHCI_READ(sc, AHCI_IS),
@@ -1086,8 +1102,8 @@ ahci_cmd_start(struct ata_channel *chp, 
 	    AHCI_READ(sc, AHCI_P_CMD(chp->ch_channel)),
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))),
 	    DEBUG_XFERS);
-	if ((ata_c->flags & AT_DONE) == 0) {
-		ata_c->flags |= AT_TIMEOU;
+	if ((xfer->c_ata_c.flags & AT_DONE) == 0) {
+		xfer->c_ata_c.flags |= AT_TIMEOU;
 		xfer->c_intr(chp, xfer, 0);
 	}
 	/* reenable interrupts */
@@ -1095,6 +1111,12 @@ ahci_cmd_start(struct ata_channel *chp, 
 }
 
 static void
+ahci_cmd_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	ahci_cmd_complete(chp, xfer, 0);
+}
+
+static void
 ahci_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason)
 {
 	struct ahci_channel *achp = (struct ahci_channel *)chp;
@@ -1164,17 +1186,18 @@ ahci_cmd_complete(struct ata_channel *ch
 	if (ata_c->flags & AT_READREG)
 		satafis_rdh_cmd_readreg(ata_c, achp->ahcic_rfis->rfis_rfis);
 
-	ahci_cmd_done(chp, xfer, tfd);
+	ahci_cmd_done(chp, xfer);
 	return 0;
 }
 
 static void
-ahci_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer, int tfd)
+ahci_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
 	struct ahci_channel *achp = (struct ahci_channel *)chp;
 	struct ata_command *ata_c = &xfer->c_ata_c;
 	uint16_t *idwordbuf;
+	int flags = ata_c->flags;
 	int i;
 
 	AHCIDEBUG_PRINT(("ahci_cmd_done channel %d flags %#x/%#x\n",
@@ -1203,7 +1226,7 @@ ahci_cmd_done(struct ata_channel *chp, s
 	if (achp->ahcic_cmdh[xfer->c_slot].cmdh_prdbc)
 		ata_c->flags |= AT_XFDONE;
 	ahci_cmd_done_end(chp, xfer);
-	if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0)
+	if ((flags & (AT_TIMEOU|AT_ERROR)) == 0)
 		atastart(chp);
 }
 
@@ -1235,13 +1258,15 @@ ahci_ata_bio(struct ata_drive_datas *drv
 	xfer->c_databuf = ata_bio->databuf;
 	xfer->c_bcount = ata_bio->bcount;
 	xfer->c_start = ahci_bio_start;
+	xfer->c_poll = ahci_bio_poll;
+	xfer->c_abort = ahci_bio_abort;
 	xfer->c_intr = ahci_bio_complete;
 	xfer->c_kill_xfer = ahci_bio_kill_xfer;
 	ata_exec_xfer(chp, xfer);
 	return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
 }
 
-static void
+static int
 ahci_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
@@ -1249,11 +1274,12 @@ ahci_bio_start(struct ata_channel *chp, 
 	struct ata_bio *ata_bio = &xfer->c_bio;
 	struct ahci_cmd_tbl *cmd_tbl;
 	struct ahci_cmd_header *cmd_h;
-	int i;
 
 	AHCIDEBUG_PRINT(("ahci_bio_start CI 0x%x\n",
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))), DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
+
 	cmd_tbl = achp->ahcic_cmd_tbl[xfer->c_slot];
 	AHCIDEBUG_PRINT(("%s port %d tbl %p\n", AHCINAME(sc), chp->ch_channel,
 	      cmd_tbl), DEBUG_XFERS);
@@ -1268,8 +1294,7 @@ ahci_bio_start(struct ata_channel *chp, 
 	    (ata_bio->flags & ATA_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)) {
 		ata_bio->error = ERR_DMA;
 		ata_bio->r_error = 0;
-		xfer->c_intr(chp, xfer, 0);
-		return;
+		return ATASTART_ABORT;
 	}
 	cmd_h->cmdh_flags = htole16(
 	    ((ata_bio->flags & ATA_READ) ? 0 :  AHCI_CMDH_F_WR) |
@@ -1293,13 +1318,22 @@ ahci_bio_start(struct ata_channel *chp, 
 	if ((xfer->c_flags & C_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(ATA_DELAY),
 		    ata_timeout, xfer);
-		return;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+static void
+ahci_bio_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
+	struct ahci_channel *achp = (struct ahci_channel *)chp;
+
 	/*
 	 * Polled command. 
 	 */
-	for (i = 0; i < ATA_DELAY * 10; i++) {
-		if (ata_bio->flags & ATA_ITSDONE)
+	for (int i = 0; i < ATA_DELAY * 10; i++) {
+		if (xfer->c_bio.flags & ATA_ITSDONE)
 			break;
 		ahci_intr_port(sc, achp);
 		delay(100);
@@ -1313,8 +1347,8 @@ ahci_bio_start(struct ata_channel *chp, 
 	    AHCI_READ(sc, AHCI_P_CMD(chp->ch_channel)),
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))),
 	    DEBUG_XFERS);
-	if ((ata_bio->flags & ATA_ITSDONE) == 0) {
-		ata_bio->error = TIMEOUT;
+	if ((xfer->c_bio.flags & ATA_ITSDONE) == 0) {
+		xfer->c_bio.error = TIMEOUT;
 		xfer->c_intr(chp, xfer, 0);
 	}
 	/* reenable interrupts */
@@ -1322,6 +1356,12 @@ ahci_bio_start(struct ata_channel *chp, 
 }
 
 static void
+ahci_bio_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	ahci_bio_complete(chp, xfer, 0);
+}
+
+static void
 ahci_bio_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason)
 {
 	int drive = xfer->c_drive;
@@ -1787,6 +1827,8 @@ ahci_atapi_scsipi_request(struct scsipi_
 		xfer->c_databuf = sc_xfer->data;
 		xfer->c_bcount = sc_xfer->datalen;
 		xfer->c_start = ahci_atapi_start;
+		xfer->c_poll = ahci_atapi_poll;
+		xfer->c_abort = ahci_atapi_abort;
 		xfer->c_intr = ahci_atapi_complete;
 		xfer->c_kill_xfer = ahci_atapi_kill_xfer;
 		xfer->c_dscpoll = 0;
@@ -1806,7 +1848,7 @@ ahci_atapi_scsipi_request(struct scsipi_
 	}
 }
 
-static void
+static int
 ahci_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
@@ -1814,11 +1856,12 @@ ahci_atapi_start(struct ata_channel *chp
 	struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
 	struct ahci_cmd_tbl *cmd_tbl;
 	struct ahci_cmd_header *cmd_h;
-	int i;
 
 	AHCIDEBUG_PRINT(("ahci_atapi_start CI 0x%x\n",
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))), DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
+
 	cmd_tbl = achp->ahcic_cmd_tbl[xfer->c_slot];
 	AHCIDEBUG_PRINT(("%s port %d tbl %p\n", AHCINAME(sc), chp->ch_channel,
 	      cmd_tbl), DEBUG_XFERS);
@@ -1837,8 +1880,7 @@ ahci_atapi_start(struct ata_channel *chp
 	    (sc_xfer->xs_control & XS_CTL_DATA_IN) ?
 	    BUS_DMA_READ : BUS_DMA_WRITE)) {
 		sc_xfer->error = XS_DRIVER_STUFFUP;
-		xfer->c_intr(chp, xfer, 0);
-		return;
+		return ATASTART_ABORT;
 	}
 	cmd_h->cmdh_flags = htole16(
 	    ((sc_xfer->xs_control & XS_CTL_DATA_OUT) ? AHCI_CMDH_F_WR : 0) |
@@ -1861,13 +1903,22 @@ ahci_atapi_start(struct ata_channel *chp
 	if ((xfer->c_flags & C_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
 		    ata_timeout, xfer);
-		return;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+static void
+ahci_atapi_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac;
+	struct ahci_channel *achp = (struct ahci_channel *)chp;
+
 	/*
 	 * Polled command. 
 	 */
-	for (i = 0; i < ATA_DELAY / 10; i++) {
-		if (sc_xfer->xs_status & XS_STS_DONE)
+	for (int i = 0; i < ATA_DELAY / 10; i++) {
+		if (xfer->c_scsipi->xs_status & XS_STS_DONE)
 			break;
 		ahci_intr_port(sc, achp);
 		delay(10000);
@@ -1881,14 +1932,20 @@ ahci_atapi_start(struct ata_channel *chp
 	    AHCI_READ(sc, AHCI_P_CMD(chp->ch_channel)),
 	    AHCI_READ(sc, AHCI_P_CI(chp->ch_channel))),
 	    DEBUG_XFERS);
-	if ((sc_xfer->xs_status & XS_STS_DONE) == 0) {
-		sc_xfer->error = XS_TIMEOUT;
+	if ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) {
+		xfer->c_scsipi->error = XS_TIMEOUT;
 		xfer->c_intr(chp, xfer, 0);
 	}
 	/* reenable interrupts */
 	AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE);
 }
 
+static void
+ahci_atapi_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	ahci_atapi_complete(chp, xfer, 0);
+}
+
 static int
 ahci_atapi_complete(struct ata_channel *chp, struct ata_xfer *xfer, int tfd)
 {
@@ -1908,8 +1965,6 @@ ahci_atapi_complete(struct ata_channel *
 
 	if (xfer->c_flags & C_TIMEOU) {
 		sc_xfer->error = XS_TIMEOUT;
-	} else if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0) {
-		sc_xfer->error = XS_NOERROR;
 	}
 
 	if (xfer->c_bcount > 0) {

Index: src/sys/dev/ic/mvsata.c
diff -u src/sys/dev/ic/mvsata.c:1.35.6.24 src/sys/dev/ic/mvsata.c:1.35.6.25
--- src/sys/dev/ic/mvsata.c:1.35.6.24	Sun Sep 10 18:37:21 2017
+++ src/sys/dev/ic/mvsata.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: mvsata.c,v 1.35.6.24 2017/09/10 18:37:21 jdolecek Exp $	*/
+/*	$NetBSD: mvsata.c,v 1.35.6.25 2017/09/10 19:31:15 jdolecek Exp $	*/
 /*
  * Copyright (c) 2008 KIYOHARA Takashi
  * All rights reserved.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.24 2017/09/10 18:37:21 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.35.6.25 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include "opt_mvsata.h"
 
@@ -129,21 +129,24 @@ static void mvsata_atapi_kill_pending(st
 static void mvsata_setup_channel(struct ata_channel *);
 
 #ifndef MVSATA_WITHOUTDMA
-static void mvsata_bio_start(struct ata_channel *, struct ata_xfer *);
+static int mvsata_bio_start(struct ata_channel *, struct ata_xfer *);
 static int mvsata_bio_intr(struct ata_channel *, struct ata_xfer *, int);
+static void mvsata_bio_poll(struct ata_channel *, struct ata_xfer *);
 static void mvsata_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
 static void mvsata_bio_done(struct ata_channel *, struct ata_xfer *);
 static int mvsata_bio_ready(struct mvsata_port *, struct ata_bio *, int,
 			    int);
-static void mvsata_wdc_cmd_start(struct ata_channel *, struct ata_xfer *);
+static int mvsata_wdc_cmd_start(struct ata_channel *, struct ata_xfer *);
 static int mvsata_wdc_cmd_intr(struct ata_channel *, struct ata_xfer *, int);
+static void mvsata_wdc_cmd_poll(struct ata_channel *, struct ata_xfer *);
 static void mvsata_wdc_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *,
 				     int);
-static void mvsata_wdc_cmd_done(struct ata_channel *, struct ata_xfer *, int);
+static void mvsata_wdc_cmd_done(struct ata_channel *, struct ata_xfer *);
 static void mvsata_wdc_cmd_done_end(struct ata_channel *, struct ata_xfer *);
 #if NATAPIBUS > 0
-static void mvsata_atapi_start(struct ata_channel *, struct ata_xfer *);
+static int mvsata_atapi_start(struct ata_channel *, struct ata_xfer *);
 static int mvsata_atapi_intr(struct ata_channel *, struct ata_xfer *, int);
+static void mvsata_atapi_poll(struct ata_channel *, struct ata_xfer *);
 static void mvsata_atapi_kill_xfer(struct ata_channel *, struct ata_xfer *,
 				   int);
 static void mvsata_atapi_reset(struct ata_channel *, struct ata_xfer *);
@@ -1088,12 +1091,14 @@ mvsata_bio(struct ata_drive_datas *drvp,
 	xfer->c_bcount = ata_bio->bcount;
 	xfer->c_start = mvsata_bio_start;
 	xfer->c_intr = mvsata_bio_intr;
+	xfer->c_poll = mvsata_bio_poll;
+	xfer->c_abort = mvsata_bio_done;
 	xfer->c_kill_xfer = mvsata_bio_kill_xfer;
 	ata_exec_xfer(chp, xfer);
 	return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
 }
 
-static void
+static int
 mvsata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct mvsata_port *mvport = (struct mvsata_port *)chp;
@@ -1110,6 +1115,8 @@ mvsata_bio_start(struct ata_channel *chp
 	DPRINTF(DEBUG_FUNCS|DEBUG_XFERS, ("%s:%d: mvsata_bio_start: drive=%d\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
 
+	ata_channel_lock_owned(chp);
+
 	if (xfer->c_flags & C_DMA)
 		if (drvp->n_xfers <= NXFER)
 			drvp->n_xfers++;
@@ -1185,8 +1192,7 @@ mvsata_bio_start(struct ata_channel *chp
 				}
 				ata_bio->error = ERR_DMA;
 				ata_bio->r_error = 0;
-				mvsata_bio_done(chp, xfer);
-				return;
+				return ATASTART_ABORT;
 			}
 			chp->ch_flags |= ATACH_DMA_WAIT;
 			/* start timeout machinery */
@@ -1248,13 +1254,11 @@ do_pio:
 			 */
 			if ((xfer->c_flags & C_POLL) == 0 &&
 			    (chp->ch_flags & ATACH_TH_RUN) == 0) {
-				ata_thread_wake(chp);
-				return;
+				return ATASTART_TH;
 			}
 			if (mvsata_bio_ready(mvport, ata_bio, xfer->c_drive,
 			    (xfer->c_flags & C_POLL) ? AT_POLL : 0) != 0) {
-				mvsata_bio_done(chp, xfer);
-				return;
+				return ATASTART_ABORT;
 			}
 		}
 
@@ -1266,7 +1270,7 @@ do_pio:
 		case WDCWAIT_TOUT:
 			goto timeout;
 		case WDCWAIT_THR:
-			return;
+			return ATASTART_TH;
 		}
 		if (ata_bio->flags & ATA_LBA48)
 			wdccommandext(chp, 0, atacmd_to48(cmd),
@@ -1302,14 +1306,13 @@ do_pio:
 			    chp->ch_channel, xfer->c_drive, ATACH_ST(tfd),
 			    ATACH_ERR(tfd));
 			ata_bio->error = TIMEOUT;
-			mvsata_bio_done(chp, xfer);
-			return;
+			return ATASTART_ABORT;
 		}
 		if (ATACH_ST(tfd) & WDCS_ERR) {
 			ata_bio->error = ERROR;
 			ata_bio->r_error = ATACH_ERR(tfd);
 			mvsata_bio_done(chp, xfer);
-			return;
+			return ATASTART_ABORT;
 		}
 
 		wdc->dataout_pio(chp, drvp->drive_flags,
@@ -1318,26 +1321,35 @@ do_pio:
 
 intr:
 	/* Wait for IRQ (either real or polled) */
-	if ((ata_bio->flags & ATA_POLL) != 0) {
-		/* Wait for at last 400ns for status bit to be valid */
-		delay(1);
-		if (chp->ch_flags & ATACH_DMA_WAIT) {
-			mvsata_edma_wait(mvport, xfer, ATA_DELAY);
-			sc->sc_enable_intr(mvport, 1 /*on*/);
-			chp->ch_flags &= ~ATACH_DMA_WAIT;
-		}
-		if ((ata_bio->flags & ATA_ITSDONE) == 0)
-			mvsata_bio_intr(chp, xfer, 0);
-	}
-	return;
+	if ((ata_bio->flags & ATA_POLL) != 0)
+		return ATASTART_POLL;
+	else
+		return ATASTART_STARTED;
 
 timeout:
 	aprint_error_dev(atac->atac_dev,
 	    "channel %d: drive %d not ready, st=0x%02x, err=0x%02x\n",
 	    chp->ch_channel, xfer->c_drive, ATACH_ST(tfd), ATACH_ERR(tfd));
 	ata_bio->error = TIMEOUT;
-	mvsata_bio_done(chp, xfer);
-	return;
+	return ATASTART_ABORT;
+}
+
+static void
+mvsata_bio_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct mvsata_port *mvport = (struct mvsata_port *)chp;
+	struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
+
+	/* Wait for at last 400ns for status bit to be valid */
+	delay(1);
+	if (chp->ch_flags & ATACH_DMA_WAIT) {
+		mvsata_edma_wait(mvport, xfer, ATA_DELAY);
+		sc->sc_enable_intr(mvport, 1 /*on*/);
+		chp->ch_flags &= ~ATACH_DMA_WAIT;
+	}
+
+	if ((xfer->c_bio.flags & ATA_ITSDONE) == 0)
+		mvsata_bio_intr(chp, xfer, 0);
 }
 
 static int
@@ -1353,6 +1365,8 @@ mvsata_bio_intr(struct ata_channel *chp,
 	    device_xname(atac->atac_dev), chp->ch_channel, __func__,
 	    xfer->c_drive));
 
+	ata_channel_lock(chp);
+
 	chp->ch_flags &= ~(ATACH_DMA_WAIT);
 
 	/*
@@ -1361,6 +1375,7 @@ mvsata_bio_intr(struct ata_channel *chp,
 	 */
 	if (xfer->c_flags & C_TIMEOU) {
 		ata_bio->error = TIMEOUT;
+		ata_channel_unlock(chp);
 		mvsata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -1384,6 +1399,7 @@ mvsata_bio_intr(struct ata_channel *chp,
 		    chp->ch_channel, xfer->c_drive, xfer->c_bcount,
 		    xfer->c_skip);
 		ata_bio->error = TIMEOUT;
+		ata_channel_unlock(chp);
 		mvsata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -1398,6 +1414,7 @@ mvsata_bio_intr(struct ata_channel *chp,
 
 	/* if we had an error, end */
 	if (ata_bio->error != NOERROR) {
+		ata_channel_unlock(chp);
 		mvsata_bio_done(chp, xfer);
 		return 1;
 	}
@@ -1409,6 +1426,7 @@ mvsata_bio_intr(struct ata_channel *chp,
 			    "channel %d: drive %d read intr before drq\n",
 			    chp->ch_channel, xfer->c_drive);
 			ata_bio->error = TIMEOUT;
+			ata_channel_unlock(chp);
 			mvsata_bio_done(chp, xfer);
 			return 1;
 		}
@@ -1421,16 +1439,19 @@ end:
 	ata_bio->blkdone += ata_bio->nblks;
 	xfer->c_skip += ata_bio->nbytes;
 	xfer->c_bcount -= ata_bio->nbytes;
+
 	/* See if this transfer is complete. */
 	if (xfer->c_bcount > 0) {
-		if ((ata_bio->flags & ATA_POLL) == 0)
+		if ((ata_bio->flags & ATA_POLL) == 0) {
 			/* Start the next operation */
-			mvsata_bio_start(chp, xfer);
-		else
+			ata_xfer_start(xfer);
+		} else {
 			/* Let mvsata_bio_start do the loop */
-			return 1;
+		}
+		ata_channel_unlock(chp);
 	} else { /* Done with this transfer */
 		ata_bio->error = NOERROR;
+		ata_channel_unlock(chp);
 		mvsata_bio_done(chp, xfer);
 	}
 	return 1;
@@ -1654,6 +1675,8 @@ mvsata_exec_command(struct ata_drive_dat
 	xfer->c_bcount = ata_c->bcount;
 	xfer->c_start = mvsata_wdc_cmd_start;
 	xfer->c_intr = mvsata_wdc_cmd_intr;
+	xfer->c_poll = mvsata_wdc_cmd_poll;
+	xfer->c_abort = mvsata_wdc_cmd_done;
 	xfer->c_kill_xfer = mvsata_wdc_cmd_kill_xfer;
 	s = splbio();
 	ata_exec_xfer(chp, xfer);
@@ -1676,7 +1699,7 @@ mvsata_exec_command(struct ata_drive_dat
 	return rv;
 }
 
-static void
+static int
 mvsata_wdc_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct mvsata_port *mvport = (struct mvsata_port *)chp;
@@ -1689,6 +1712,8 @@ mvsata_wdc_cmd_start(struct ata_channel 
 	    ("%s:%d: mvsata_cmd_start: drive=%d\n",
 	    device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, drive));
 
+	ata_channel_lock_owned(chp);
+
 	/* First, EDMA disable, if enabled this channel. */
 	KASSERT((chp->ch_flags & ATACH_NCQ) == 0);
 	if (mvport->port_edmamode_curr != nodma)
@@ -1703,10 +1728,9 @@ mvsata_wdc_cmd_start(struct ata_channel 
 		break;
 	case WDCWAIT_TOUT:
 		ata_c->flags |= AT_TIMEOU;
-		mvsata_wdc_cmd_done(chp, xfer, tfd);
-		return;
+		return ATASTART_ABORT;
 	case WDCWAIT_THR:
-		return;
+		return ATASTART_TH;
 	}
 	if (ata_c->flags & AT_POLL)
 		/* polled command, disable interrupts */
@@ -1728,8 +1752,15 @@ mvsata_wdc_cmd_start(struct ata_channel 
 	if ((ata_c->flags & AT_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, ata_c->timeout / 1000 * hz,
 		    wdctimeout, xfer);
-		return;
+		return ATASTART_STARTED;
 	}
+
+	return ATASTART_POLL;
+}
+
+static void
+mvsata_wdc_cmd_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
 	/*
 	 * Polled command. Wait for drive ready or drq. Done in intr().
 	 * Wait for at last 400ns for status bit to be valid.
@@ -1750,6 +1781,8 @@ mvsata_wdc_cmd_intr(struct ata_channel *
 	int drive_flags;
 	int tfd;
 
+	ata_channel_lock(chp);
+
 	if (ata_c->r_command == WDCC_IDENTIFY ||
 	    ata_c->r_command == ATAPI_IDENTIFY_DEVICE)
 		/*
@@ -1795,16 +1828,20 @@ again:
 		if (wdcwait(chp, ata_c->r_st_bmask | WDCS_DRQ,
 		    ata_c->r_st_bmask, (irq == 0) ? ata_c->timeout : 0,
 		    wflags, &tfd) ==  WDCWAIT_TOUT) {
-			if (irq && (xfer->c_flags & C_TIMEOU) == 0)
+			if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
+				ata_channel_unlock(chp);
 				return 0;	/* IRQ was not for us */
+			}
 			ata_c->flags |= AT_TIMEOU;
 		}
 		goto out;
 	}
 	if (wdcwait(chp, ata_c->r_st_pmask, ata_c->r_st_pmask,
 	    (irq == 0) ? ata_c->timeout : 0, wflags, &tfd) == WDCWAIT_TOUT) {
-		if (irq && (xfer->c_flags & C_TIMEOU) == 0)
-		    return 0;	/* IRQ was not for us */
+		if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
+			ata_channel_unlock(chp);
+			return 0;	/* IRQ was not for us */
+		}
 		ata_c->flags |= AT_TIMEOU;
 		goto out;
 	}
@@ -1831,12 +1868,24 @@ again:
 		if ((ata_c->flags & AT_POLL) == 0) {
 			callout_reset(&xfer->c_timo_callout,
 			    mstohz(ata_c->timeout), wdctimeout, xfer);
+			ata_channel_unlock(chp);
 			return 1;
 		} else
 			goto again;
 	}
 out:
-	mvsata_wdc_cmd_done(chp, xfer, tfd);
+	if (ATACH_ST(tfd) & WDCS_DWF)
+		ata_c->flags |= AT_DF;
+	if (ATACH_ST(tfd) & WDCS_ERR) {
+		ata_c->flags |= AT_ERROR;
+		ata_c->r_error = ATACH_ERR(tfd);
+	}
+	ata_channel_unlock(chp);
+	mvsata_wdc_cmd_done(chp, xfer);
+
+	if ((ATACH_ST(tfd) & WDCS_ERR) == 0)
+		atastart(chp);
+
 	return 1;
 }
 
@@ -1880,7 +1929,7 @@ mvsata_wdc_cmd_kill_xfer(struct ata_chan
 }
 
 static void
-mvsata_wdc_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer, int tfd)
+mvsata_wdc_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct mvsata_port *mvport = (struct mvsata_port *)chp;
 	struct atac_softc *atac = chp->ch_atac;
@@ -1894,12 +1943,6 @@ mvsata_wdc_cmd_done(struct ata_channel *
 	if (ata_waitdrain_xfer_check(chp, xfer))
 		return;
 
-	if (ATACH_ST(tfd) & WDCS_DWF)
-		ata_c->flags |= AT_DF;
-	if (ATACH_ST(tfd) & WDCS_ERR) {
-		ata_c->flags |= AT_ERROR;
-		ata_c->r_error = ATACH_ERR(tfd);
-	}
 	if ((ata_c->flags & AT_READREG) != 0 &&
 	    device_is_active(atac->atac_dev) &&
 	    (ata_c->flags & (AT_ERROR | AT_DF)) == 0) {
@@ -1952,8 +1995,6 @@ mvsata_wdc_cmd_done(struct ata_channel *
 	}
 
 	mvsata_wdc_cmd_done_end(chp, xfer);
-	if ((ATACH_ST(tfd) & WDCS_ERR) == 0)
-		atastart(chp);
 }
 
 static void
@@ -2017,6 +2058,8 @@ mvsata_atapi_scsipi_request(struct scsip
 		xfer->c_bcount = sc_xfer->datalen;
 		xfer->c_start = mvsata_atapi_start;
 		xfer->c_intr = mvsata_atapi_intr;
+		xfer->c_poll = mvsata_atapi_poll;
+		xfer->c_abort = mvsata_atapi_reset;
 		xfer->c_kill_xfer = mvsata_atapi_kill_xfer;
 		xfer->c_dscpoll = 0;
 		s = splbio();
@@ -2036,7 +2079,7 @@ mvsata_atapi_scsipi_request(struct scsip
 	}
 }
 
-static void
+static int
 mvsata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct mvsata_softc *sc = (struct mvsata_softc *)chp->ch_atac;
@@ -2053,6 +2096,8 @@ mvsata_atapi_start(struct ata_channel *c
 	    device_xname(chp->ch_atac->atac_dev), chp->ch_channel,
 	    xfer->c_drive, sc_xfer->xs_control));
 
+	ata_channel_lock_owned(chp);
+
 	KASSERT((chp->ch_flags  & ATACH_NCQ) == 0);
 	if (mvport->port_edmamode_curr != nodma)
 		mvsata_edma_disable(mvport, 10 /* ms */, wait_flags);
@@ -2067,8 +2112,7 @@ mvsata_atapi_start(struct ata_channel *c
 		/* If it's not a polled command, we need the kernel thread */
 		if ((sc_xfer->xs_control & XS_CTL_POLL) == 0 &&
 		    (chp->ch_flags & ATACH_TH_RUN) == 0) {
-			ata_thread_wake(chp);
-			return;
+			return ATASTART_TH;
 		}
 		/*
 		 * disable interrupts, all commands here should be quick
@@ -2154,11 +2198,11 @@ ready:
 		    wdctimeout, xfer);
 
 	MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
-	if (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags, &tfd)) {
+	if (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags, &tfd) != 0) {
 		aprint_error_dev(atac->atac_dev, "not ready, st = %02x\n",
 		    ATACH_ST(tfd));
 		sc_xfer->error = XS_TIMEOUT;
-		mvsata_atapi_reset(chp, xfer);
+		return ATASTART_ABORT;
 	}
 
 	/*
@@ -2176,27 +2220,13 @@ ready:
 
 	/*
 	 * If there is no interrupt for CMD input, busy-wait for it (done in
-	 * the interrupt routine. If it is a polled command, call the interrupt
-	 * routine until command is done.
+	 * the interrupt routine. Poll routine will exit early in this case.
 	 */
 	if ((sc_xfer->xs_periph->periph_cap & ATAPI_CFG_DRQ_MASK) !=
-	    ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL)) {
-		/* Wait for at last 400ns for status bit to be valid */
-		DELAY(1);
-		mvsata_atapi_intr(chp, xfer, 0);
-	}
-	if (sc_xfer->xs_control & XS_CTL_POLL) {
-		if (chp->ch_flags & ATACH_DMA_WAIT) {
-			wdc_dmawait(chp, xfer, sc_xfer->timeout);
-			chp->ch_flags &= ~ATACH_DMA_WAIT;
-		}
-		while ((sc_xfer->xs_status & XS_STS_DONE) == 0) {
-			/* Wait for at last 400ns for status bit to be valid */
-			DELAY(1);
-			mvsata_atapi_intr(chp, xfer, 0);
-		}
-	}
-	return;
+	    ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL))
+		return ATASTART_POLL;
+	else
+		return ATASTART_STARTED;
 
 timeout:
 	aprint_error_dev(atac->atac_dev, "channel %d drive %d: %s timed out\n",
@@ -2204,8 +2234,7 @@ timeout:
 	sc_xfer->error = XS_TIMEOUT;
 	MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
 	delay(10);		/* some drives need a little delay here */
-	mvsata_atapi_reset(chp, xfer);
-	return;
+	return ATASTART_ABORT;
 
 error:
 	aprint_error_dev(atac->atac_dev,
@@ -2215,8 +2244,36 @@ error:
 	sc_xfer->sense.atapi_sense = ATACH_ERR(tfd);
 	MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
 	delay(10);		/* some drives need a little delay here */
-	mvsata_atapi_reset(chp, xfer);
-	return;
+	return ATASTART_ABORT;
+}
+
+static void
+mvsata_atapi_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	/*
+	 * If there is no interrupt for CMD input, busy-wait for it (done in
+	 * the interrupt routine. If it is a polled command, call the interrupt
+	 * routine until command is done.
+	 */
+	const bool poll = ((xfer->c_scsipi->xs_control & XS_CTL_POLL) != 0);
+
+	/* Wait for at last 400ns for status bit to be valid */
+	DELAY(1);
+	mvsata_atapi_intr(chp, xfer, 0);
+
+	if (!poll)
+		return;
+
+	if (chp->ch_flags & ATACH_DMA_WAIT) {
+		wdc_dmawait(chp, xfer, xfer->c_scsipi->timeout);
+		chp->ch_flags &= ~ATACH_DMA_WAIT;
+	}
+
+	while ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) {
+		/* Wait for at last 400ns for status bit to be valid */
+		DELAY(1);
+		mvsata_atapi_intr(chp, xfer, 0);
+	}
 }
 
 static int
@@ -2231,6 +2288,8 @@ mvsata_atapi_intr(struct ata_channel *ch
 	int tfd;
 	void *cmd;
 
+	ata_channel_lock(chp);
+
 	DPRINTF(DEBUG_FUNCS|DEBUG_XFERS,
 	    ("%s:%d:%d: mvsata_atapi_intr\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
@@ -2247,6 +2306,7 @@ mvsata_atapi_intr(struct ata_channel *ch
 	 * Don't try to continue transfer, we may have missed cycles.
 	 */
 	if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_TIMEOUT;
 		mvsata_atapi_reset(chp, xfer);
 		return 1;
@@ -2256,14 +2316,17 @@ mvsata_atapi_intr(struct ata_channel *ch
 	MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
 	if (wdc_wait_for_unbusy(chp,
 	    (irq == 0) ? sc_xfer->timeout : 0, AT_POLL, &tfd) == WDCWAIT_TOUT) {
-		if (irq && (xfer->c_flags & C_TIMEOU) == 0)
+		if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
+			ata_channel_unlock(chp);
 			return 0; /* IRQ was not for us */
+		}
 		aprint_error_dev(atac->atac_dev,
 		    "channel %d: device timeout, c_bcount=%d, c_skip=%d\n",
 		    chp->ch_channel, xfer->c_bcount, xfer->c_skip);
 		if (xfer->c_flags & C_DMA)
 			ata_dmaerr(drvp,
 			    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_TIMEOUT;
 		mvsata_atapi_reset(chp, xfer);
 		return 1;
@@ -2275,6 +2338,7 @@ mvsata_atapi_intr(struct ata_channel *ch
 	 */
 	if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) {
 		ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_RESET;
 		mvsata_atapi_reset(chp, xfer);
 		return (1);
@@ -2324,6 +2388,7 @@ again:
 			mvsata_bdma_start(mvport);
 			chp->ch_flags |= ATACH_DMA_WAIT;
 		}
+		ata_channel_unlock(chp);
 		return 1;
 
 	case PHASE_DATAOUT:
@@ -2358,6 +2423,7 @@ again:
 
 		xfer->c_skip += len;
 		xfer->c_bcount -= len;
+		ata_channel_unlock(chp);
 		return 1;
 
 	case PHASE_DATAIN:
@@ -2371,6 +2437,7 @@ again:
 			if (xfer->c_flags & C_DMA)
 				ata_dmaerr(drvp,
 				    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_TIMEOUT;
 			mvsata_atapi_reset(chp, xfer);
 			return 1;
@@ -2392,6 +2459,7 @@ again:
 
 		xfer->c_skip += len;
 		xfer->c_bcount -= len;
+		ata_channel_unlock(chp);
 		return 1;
 
 	case PHASE_ABORTED:
@@ -2400,6 +2468,7 @@ again:
 		if (xfer->c_flags & C_DMA)
 			xfer->c_bcount -= sc_xfer->datalen;
 		sc_xfer->resid = xfer->c_bcount;
+		/* this will unlock channel lock too */
 		mvsata_atapi_phase_complete(xfer);
 		return 1;
 
@@ -2422,6 +2491,7 @@ again:
 			if (xfer->c_flags & C_DMA)
 				ata_dmaerr(drvp,
 				    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_RESET;
 			mvsata_atapi_reset(chp, xfer);
 			return (1);
@@ -2431,6 +2501,7 @@ again:
 	    ("mvsata_atapi_intr: %s (end), error 0x%x "
 	    "sense 0x%x\n", __func__,
 	    sc_xfer->error, sc_xfer->sense.atapi_sense));
+	ata_channel_unlock(chp);
 	mvsata_atapi_done(chp, xfer);
 	return 1;
 }
@@ -2481,6 +2552,8 @@ mvsata_atapi_reset(struct ata_channel *c
 	struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
 	int tfd;
 
+	ata_channel_lock(chp);
+
 	mvsata_pmp_select(mvport, xfer->c_drive);
 
 	wdccommandshort(chp, 0, ATAPI_SOFT_RESET);
@@ -2490,6 +2563,9 @@ mvsata_atapi_reset(struct ata_channel *c
 		    chp->ch_channel, xfer->c_drive);
 		sc_xfer->error = XS_SELTIMEOUT;
 	}
+
+	ata_channel_unlock(chp);
+
 	mvsata_atapi_done(chp, xfer);
 	return;
 }
@@ -2504,6 +2580,8 @@ mvsata_atapi_phase_complete(struct ata_x
 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
 	int tfd;
 
+	ata_channel_lock_owned(chp);
+
 	/* wait for DSC if needed */
 	if (drvp->drive_flags & ATA_DRIVE_ATAPIDSCW) {
 		DPRINTF(DEBUG_XFERS,
@@ -2520,12 +2598,14 @@ mvsata_atapi_phase_complete(struct ata_x
 				aprint_error_dev(atac->atac_dev,
 				    "channel %d: wait_for_dsc failed\n",
 				    chp->ch_channel);
+				ata_channel_unlock(chp);
 				sc_xfer->error = XS_TIMEOUT;
 				mvsata_atapi_reset(chp, xfer);
-				return;
-			} else
+			} else {
 				callout_reset(&xfer->c_timo_callout, 1,
 				    mvsata_atapi_polldsc, xfer);
+				ata_channel_unlock(chp);
+			}
 			return;
 		}
 	}
@@ -2550,6 +2630,7 @@ mvsata_atapi_phase_complete(struct ata_x
 		    if (wdc->dma_status & (WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) {
 			ata_dmaerr(drvp,
 			    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_RESET;
 			mvsata_atapi_reset(chp, xfer);
 			return;
@@ -2575,6 +2656,7 @@ mvsata_atapi_phase_complete(struct ata_x
 	    " mvsata_atapi_done(), error 0x%x sense 0x%x\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
 	    sc_xfer->error, sc_xfer->sense.atapi_sense));
+	ata_channel_unlock(chp);
 	mvsata_atapi_done(chp, xfer);
 }
 
@@ -2614,8 +2696,13 @@ mvsata_atapi_done(struct ata_channel *ch
 static void
 mvsata_atapi_polldsc(void *arg)
 {
+	struct ata_xfer *xfer = arg;
+	struct ata_channel *chp = xfer->c_chp;
+
+	ata_channel_lock(chp);
 
-	mvsata_atapi_phase_complete(arg);
+	/* this will unlock channel lock too */
+	mvsata_atapi_phase_complete(xfer);
 }
 #endif	/* NATAPIBUS > 0 */
 

Index: src/sys/dev/ic/siisata.c
diff -u src/sys/dev/ic/siisata.c:1.30.4.36 src/sys/dev/ic/siisata.c:1.30.4.37
--- src/sys/dev/ic/siisata.c:1.30.4.36	Tue Aug 15 20:12:28 2017
+++ src/sys/dev/ic/siisata.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.30.4.36 2017/08/15 20:12:28 jakllsch Exp $ */
+/* $NetBSD: siisata.c,v 1.30.4.37 2017/09/10 19:31:15 jdolecek Exp $ */
 
 /* from ahcisata_core.c */
 
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.36 2017/08/15 20:12:28 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.30.4.37 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -147,14 +147,18 @@ int siisata_ata_addref(struct ata_drive_
 void siisata_ata_delref(struct ata_drive_datas *);
 void siisata_killpending(struct ata_drive_datas *);
 
-void siisata_cmd_start(struct ata_channel *, struct ata_xfer *);
+int siisata_cmd_start(struct ata_channel *, struct ata_xfer *);
 int siisata_cmd_complete(struct ata_channel *, struct ata_xfer *, int);
+void siisata_cmd_poll(struct ata_channel *, struct ata_xfer *);
+void siisata_cmd_abort(struct ata_channel *, struct ata_xfer *);
 void siisata_cmd_done(struct ata_channel *, struct ata_xfer *, int);
 static void siisata_cmd_done_end(struct ata_channel *, struct ata_xfer *);
 void siisata_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
 
-void siisata_bio_start(struct ata_channel *, struct ata_xfer *);
+int siisata_bio_start(struct ata_channel *, struct ata_xfer *);
 int siisata_bio_complete(struct ata_channel *, struct ata_xfer *, int);
+void siisata_bio_poll(struct ata_channel *, struct ata_xfer *);
+void siisata_bio_abort(struct ata_channel *, struct ata_xfer *);
 void siisata_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
 int siisata_exec_command(struct ata_drive_datas *, struct ata_xfer *);
 
@@ -169,8 +173,10 @@ void siisata_channel_recover(struct ata_
 void siisata_atapibus_attach(struct atabus_softc *);
 void siisata_atapi_probe_device(struct atapibus_softc *, int);
 void siisata_atapi_minphys(struct buf *);
-void siisata_atapi_start(struct ata_channel *,struct ata_xfer *);
+int siisata_atapi_start(struct ata_channel *,struct ata_xfer *);
 int siisata_atapi_complete(struct ata_channel *, struct ata_xfer *, int);
+void siisata_atapi_poll(struct ata_channel *, struct ata_xfer *);
+void siisata_atapi_abort(struct ata_channel *, struct ata_xfer *);
 void siisata_atapi_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
 void siisata_atapi_scsipi_request(struct scsipi_channel *,
     scsipi_adapter_req_t, void *);
@@ -979,6 +985,8 @@ siisata_exec_command(struct ata_drive_da
 	xfer->c_bcount = ata_c->bcount;
 	xfer->c_start = siisata_cmd_start;
 	xfer->c_intr = siisata_cmd_complete;
+	xfer->c_poll = siisata_cmd_poll;
+	xfer->c_abort = siisata_cmd_abort;
 	xfer->c_kill_xfer = siisata_cmd_kill_xfer;
 	s = splbio();
 	ata_exec_xfer(chp, xfer);
@@ -1010,19 +1018,20 @@ siisata_exec_command(struct ata_drive_da
 	return ret;
 }
 
-void
+int
 siisata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct ata_command *ata_c = &xfer->c_ata_c;
 	struct siisata_prb *prb;
-	int i;
 
 	SIISATA_DEBUG_PRINT(("%s: %s port %d drive %d command 0x%x, slot %d\n",
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), __func__,
 	    chp->ch_channel, xfer->c_drive, ata_c->r_command, xfer->c_slot),
 	    DEBUG_FUNCS|DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
+
 	prb = schp->sch_prb[xfer->c_slot];
 	memset(prb, 0, SIISATA_CMD_SIZE);
 
@@ -1040,8 +1049,7 @@ siisata_cmd_start(struct ata_channel *ch
 	    ata_c->bcount,
 	    (ata_c->flags & AT_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)) {
 		ata_c->flags |= AT_DF;
-		siisata_cmd_complete(chp, xfer, 0);
-		return;
+		return ATASTART_ABORT;
 	}
 
 	if (xfer->c_flags & C_POLL) {
@@ -1056,30 +1064,42 @@ siisata_cmd_start(struct ata_channel *ch
 	if ((ata_c->flags & AT_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(ata_c->timeout),
 		    ata_timeout, xfer);
-		goto out;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+void
+siisata_cmd_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct siisata_channel *schp = (struct siisata_channel *)chp;
 
 	/*
 	 * polled command
 	 */
-	for (i = 0; i < ata_c->timeout * 10; i++) {
-		if (ata_c->flags & AT_DONE)
+	for (int i = 0; i < xfer->c_ata_c.timeout * 10; i++) {
+		if (xfer->c_ata_c.flags & AT_DONE)
 			break;
 		siisata_intr_port(schp);
 		DELAY(100);
 	}
 
-	if ((ata_c->flags & AT_DONE) == 0) {
+	if ((xfer->c_ata_c.flags & AT_DONE) == 0) {
 		ata_timeout(xfer);
 	}
 
 	/* reenable interrupts */
 	siisata_enable_port_interrupt(chp);
-out:
+
 	SIISATA_DEBUG_PRINT(("%s: %s: done\n",
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), __func__),
 	    DEBUG_FUNCS);
-	return;
+}
+
+void
+siisata_cmd_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	siisata_cmd_complete(chp, xfer, 0);
 }
 
 void
@@ -1228,24 +1248,27 @@ siisata_ata_bio(struct ata_drive_datas *
 	xfer->c_bcount = ata_bio->bcount;
 	xfer->c_start = siisata_bio_start;
 	xfer->c_intr = siisata_bio_complete;
+	xfer->c_poll = siisata_bio_poll;
+	xfer->c_abort = siisata_bio_abort;
 	xfer->c_kill_xfer = siisata_bio_kill_xfer;
 	ata_exec_xfer(chp, xfer);
 	return (ata_bio->flags & ATA_ITSDONE) ?
 	    ATACMD_COMPLETE : ATACMD_QUEUED;
 }
 
-void
+int
 siisata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct siisata_prb *prb;
 	struct ata_bio *ata_bio = &xfer->c_bio;
-	int i;
 
 	SIISATA_DEBUG_PRINT(("%s: %s port %d slot %d drive %d\n",
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), __func__,
 	    chp->ch_channel, xfer->c_slot, xfer->c_drive), DEBUG_FUNCS);
 
+	ata_channel_lock_owned(chp);
+
 	prb = schp->sch_prb[xfer->c_slot];
 	memset(prb, 0, SIISATA_CMD_SIZE);
 
@@ -1257,8 +1280,7 @@ siisata_bio_start(struct ata_channel *ch
 	    (ata_bio->flags & ATA_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)) {
 		ata_bio->error = ERR_DMA;
 		ata_bio->r_error = 0;
-		siisata_bio_complete(chp, xfer, 0);
-		return;
+		return ATASTART_ABORT;
 	}
 
 	if (xfer->c_flags & C_POLL) {
@@ -1272,29 +1294,41 @@ siisata_bio_start(struct ata_channel *ch
 	if ((ata_bio->flags & ATA_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(ATA_DELAY),
 		    ata_timeout, xfer);
-		goto out;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+void
+siisata_bio_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct siisata_channel *schp = (struct siisata_channel *)chp;
 
 	/*
 	 * polled command
 	 */
-	for (i = 0; i < ATA_DELAY * 10; i++) {
-		if (ata_bio->flags & ATA_ITSDONE)
+	for (int i = 0; i < ATA_DELAY * 10; i++) {
+		if (xfer->c_bio.flags & ATA_ITSDONE)
 			break;
 		siisata_intr_port(schp);
 		DELAY(100);
 	}
 
-	if ((ata_bio->flags & ATA_ITSDONE) == 0) {
+	if ((xfer->c_bio.flags & ATA_ITSDONE) == 0) {
 		ata_timeout(xfer);
 	}
 
 	siisata_enable_port_interrupt(chp);
-out:
+
 	SIISATA_DEBUG_PRINT(("%s: %s: done\n",
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), __func__),
 	    DEBUG_FUNCS);
-	return;
+}
+
+void
+siisata_bio_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	siisata_cmd_complete(chp, xfer, 0);
 }
 
 void
@@ -1797,6 +1831,8 @@ siisata_atapi_scsipi_request(struct scsi
 		xfer->c_bcount = sc_xfer->datalen;
 		xfer->c_start = siisata_atapi_start;
 		xfer->c_intr = siisata_atapi_complete;
+		xfer->c_poll = siisata_atapi_poll;
+		xfer->c_abort = siisata_atapi_abort;
 		xfer->c_kill_xfer = siisata_atapi_kill_xfer;
 		xfer->c_dscpoll = 0;
 		s = splbio();
@@ -1815,20 +1851,21 @@ siisata_atapi_scsipi_request(struct scsi
 	}
 }
 
-void
+int
 siisata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct siisata_prb *prbp;
 
 	struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
-	int i;
 
 	SIISATA_DEBUG_PRINT( ("%s: %s:%d:%d, scsi flags 0x%x\n", __func__,
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), chp->ch_channel,
 	    chp->ch_drive[xfer->c_drive].drive, sc_xfer->xs_control),
 	    DEBUG_XFERS);
 
+	ata_channel_lock_owned(chp);
+
 	prbp = schp->sch_prb[xfer->c_slot];
 	memset(prbp, 0, SIISATA_CMD_SIZE);
 
@@ -1851,8 +1888,10 @@ siisata_atapi_start(struct ata_channel *
 		xfer->c_bcount,
 		(sc_xfer->xs_control & XS_CTL_DATA_IN) ?
 		BUS_DMA_READ : BUS_DMA_WRITE)
-	)
-		panic("%s", __func__);
+	) {
+		sc_xfer->error = XS_DRIVER_STUFFUP;
+		return ATASTART_ABORT;
+	}
 
 	if (xfer->c_flags & C_POLL) {
 		/* polled command, disable interrupts */
@@ -1865,28 +1904,40 @@ siisata_atapi_start(struct ata_channel *
 	if ((xfer->c_flags & C_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout),
 		    ata_timeout, xfer);
-		goto out;
-	}
+		return ATASTART_STARTED;
+	} else
+		return ATASTART_POLL;
+}
+
+void
+siisata_atapi_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	struct siisata_channel *schp = (struct siisata_channel *)chp;
 
 	/*
 	 * polled command
 	 */
-	for (i = 0; i < ATA_DELAY * 10; i++) {
-		if (sc_xfer->xs_status & XS_STS_DONE)
+	for (int i = 0; i < ATA_DELAY * 10; i++) {
+		if (xfer->c_scsipi->xs_status & XS_STS_DONE)
 			break;
 		siisata_intr_port(schp);
 		DELAY(100);
 	}
-	if ((sc_xfer->xs_status & XS_STS_DONE) == 0) {
+	if ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) {
 		ata_timeout(xfer);
 	}
 	/* reenable interrupts */
 	siisata_enable_port_interrupt(chp);
-out:
+
 	SIISATA_DEBUG_PRINT(("%s: %s: done\n",
 	    SIISATANAME((struct siisata_softc *)chp->ch_atac), __func__),
-	    DEBUG_FUNCS);
-	return;
+            DEBUG_FUNCS);
+}
+
+void
+siisata_atapi_abort(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	siisata_atapi_complete(chp, xfer, 0);
 }
 
 int
@@ -1909,8 +1960,6 @@ siisata_atapi_complete(struct ata_channe
 
 	if (xfer->c_flags & C_TIMEOU) {
 		sc_xfer->error = XS_TIMEOUT;
-	} else if ((ATACH_ST(tfd) & WDCS_ERR) == 0) {
-		sc_xfer->error = XS_NOERROR;
 	}
 
 	bus_dmamap_sync(sc->sc_dmat, schp->sch_datad[xfer->c_slot], 0,

Index: src/sys/dev/ic/wdc.c
diff -u src/sys/dev/ic/wdc.c:1.283.2.13 src/sys/dev/ic/wdc.c:1.283.2.14
--- src/sys/dev/ic/wdc.c:1.283.2.13	Sat Aug 12 14:41:54 2017
+++ src/sys/dev/ic/wdc.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: wdc.c,v 1.283.2.13 2017/08/12 14:41:54 jdolecek Exp $ */
+/*	$NetBSD: wdc.c,v 1.283.2.14 2017/09/10 19:31:15 jdolecek Exp $ */
 
 /*
  * Copyright (c) 1998, 2001, 2003 Manuel Bouyer.  All rights reserved.
@@ -58,7 +58,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.283.2.13 2017/08/12 14:41:54 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.283.2.14 2017/09/10 19:31:15 jdolecek Exp $");
 
 #include "opt_ata.h"
 #include "opt_wdc.h"
@@ -146,11 +146,12 @@ static int	wdcprobe1(struct ata_channel 
 static int	wdcreset(struct ata_channel *, int);
 static void	__wdcerror(struct ata_channel *, const char *);
 static int	__wdcwait_reset(struct ata_channel *, int, int);
-static void	__wdccommand_done(struct ata_channel *, struct ata_xfer *, int);
+static void	__wdccommand_done(struct ata_channel *, struct ata_xfer *);
+static void	__wdccommand_poll(struct ata_channel *, struct ata_xfer *);
 static void	__wdccommand_done_end(struct ata_channel *, struct ata_xfer *);
 static void	__wdccommand_kill_xfer(struct ata_channel *,
 			               struct ata_xfer *, int);
-static void	__wdccommand_start(struct ata_channel *, struct ata_xfer *);
+static int	__wdccommand_start(struct ata_channel *, struct ata_xfer *);
 static int	__wdccommand_intr(struct ata_channel *, struct ata_xfer *, int);
 static int	__wdcwait(struct ata_channel *, int, int, int, int *);
 
@@ -1227,6 +1228,8 @@ wdcwait(struct ata_channel *chp, int mas
 {
 	int error, i, timeout_hz = mstohz(timeout);
 
+	ata_channel_lock_owned(chp);
+
 	if (timeout_hz == 0 ||
 	    (flags & (AT_WAIT | AT_POLL)) == AT_POLL)
 		error = __wdcwait(chp, mask, bits, timeout, tfd);
@@ -1245,14 +1248,14 @@ wdcwait(struct ata_channel *chp, int mas
 						error = 0;
 						break;
 					}
-					tsleep(&chp, PRIBIO, "atapoll", 1);
+					kpause("atapoll", true, 1,
+					    &chp->ch_lock);
 				}
 			} else {
 				/*
 				 * we're probably in interrupt context,
-				 * ask the thread to come back here
+				 * caller must ask the thread to come back here
 				 */
-				ata_thread_wake(chp);
 				return(WDCWAIT_THR);
 			}
 		}
@@ -1301,10 +1304,10 @@ wdctimeout(void *arg)
 	s = splbio();
 	KASSERT(xfer != NULL);
 
-        if (ata_timo_xfer_check(xfer)) {
-                /* Already logged */
-                goto out;
-        }
+	if (ata_timo_xfer_check(xfer)) {
+		/* Already logged */
+		goto out;
+	}
 
 	__wdcerror(chp, "lost interrupt");
 	printf("\ttype: %s tc_bcount: %d tc_skip: %d\n",
@@ -1357,6 +1360,8 @@ wdc_exec_command(struct ata_drive_datas 
 	xfer->c_databuf = ata_c->data;
 	xfer->c_bcount = ata_c->bcount;
 	xfer->c_start = __wdccommand_start;
+	xfer->c_poll = __wdccommand_poll;
+	xfer->c_abort = __wdccommand_done;
 	xfer->c_intr = __wdccommand_intr;
 	xfer->c_kill_xfer = __wdccommand_kill_xfer;
 
@@ -1383,7 +1388,7 @@ wdc_exec_command(struct ata_drive_datas 
 	return ret;
 }
 
-static void
+static int
 __wdccommand_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct wdc_softc *wdc = CHAN_TO_WDC(chp);
@@ -1407,10 +1412,9 @@ __wdccommand_start(struct ata_channel *c
 		break;
 	case WDCWAIT_TOUT:
 		ata_c->flags |= AT_TIMEOU;
-		__wdccommand_done(chp, xfer, tfd);
-		return;
+		return ATASTART_ABORT;
 	case WDCWAIT_THR:
-		return;
+		return ATASTART_TH;
 	}
 	if (ata_c->flags & AT_POLL) {
 		/* polled command, disable interrupts */
@@ -1436,13 +1440,20 @@ __wdccommand_start(struct ata_channel *c
 	if ((ata_c->flags & AT_POLL) == 0) {
 		callout_reset(&xfer->c_timo_callout, ata_c->timeout / 1000 * hz,
 		    wdctimeout, xfer);
-		return;
+		return ATASTART_STARTED;
 	}
+
 	/*
 	 * Polled command. Wait for drive ready or drq. Done in intr().
 	 * Wait for at last 400ns for status bit to be valid.
 	 */
 	delay(10);	/* 400ns delay */
+	return ATASTART_POLL;
+}
+
+static void
+__wdccommand_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
 	__wdccommand_intr(chp, xfer, 0);
 }
 
@@ -1486,7 +1497,9 @@ __wdccommand_intr(struct ata_channel *ch
 	}
 #endif
 
- again:
+	ata_channel_lock(chp);
+
+again:
 	ATADEBUG_PRINT(("__wdccommand_intr %s:%d:%d\n",
 	    device_xname(chp->ch_atac->atac_dev), chp->ch_channel,
 	    xfer->c_drive), DEBUG_INTR);
@@ -1508,16 +1521,20 @@ __wdccommand_intr(struct ata_channel *ch
 		if (wdcwait(chp, ata_c->r_st_bmask | WDCS_DRQ,
 		    ata_c->r_st_bmask, (irq == 0)  ? ata_c->timeout : 0,
 		    wflags, &tfd) ==  WDCWAIT_TOUT) {
-			if (irq && (xfer->c_flags & C_TIMEOU) == 0)
+			if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
+				ata_channel_unlock(chp);
 				return 0; /* IRQ was not for us */
+			}
 			ata_c->flags |= AT_TIMEOU;
 		}
 		goto out;
 	}
 	if (wdcwait(chp, ata_c->r_st_pmask, ata_c->r_st_pmask,
 	     (irq == 0)  ? ata_c->timeout : 0, wflags, &tfd) == WDCWAIT_TOUT) {
-		if (irq && (xfer->c_flags & C_TIMEOU) == 0)
+		if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
+			ata_channel_unlock(chp);
 			return 0; /* IRQ was not for us */
+		}
 		ata_c->flags |= AT_TIMEOU;
 		goto out;
 	}
@@ -1545,18 +1562,28 @@ __wdccommand_intr(struct ata_channel *ch
 		if ((ata_c->flags & AT_POLL) == 0) {
 			callout_reset(&xfer->c_timo_callout,
 			    mstohz(ata_c->timeout), wdctimeout, xfer);
+			ata_channel_unlock(chp);
 			return 1;
 		} else {
 			goto again;
 		}
 	}
- out:
-	__wdccommand_done(chp, xfer, tfd);
+out:
+	if (ATACH_ST(tfd) & WDCS_DWF)
+		ata_c->flags |= AT_DF;
+	if (ATACH_ST(tfd) & WDCS_ERR) {
+		ata_c->flags |= AT_ERROR;
+		ata_c->r_error = ATACH_ST(tfd);
+	}
+
+	ata_channel_unlock(chp);
+
+	__wdccommand_done(chp, xfer);
 	return 1;
 }
 
 static void
-__wdccommand_done(struct ata_channel *chp, struct ata_xfer *xfer, int tfd)
+__wdccommand_done(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct atac_softc *atac = chp->ch_atac;
 	struct wdc_softc *wdc = CHAN_TO_WDC(chp);
@@ -1573,12 +1600,6 @@ __wdccommand_done(struct ata_channel *ch
 		goto out;
 	}
 
-	if (ATACH_ST(tfd) & WDCS_DWF)
-		ata_c->flags |= AT_DF;
-	if (ATACH_ST(tfd) & WDCS_ERR) {
-		ata_c->flags |= AT_ERROR;
-		ata_c->r_error = ATACH_ST(tfd);
-	}
 	if ((ata_c->flags & AT_READREG) != 0 &&
 	    device_is_active(atac->atac_dev) &&
 	    (ata_c->flags & (AT_ERROR | AT_DF)) == 0) {

Index: src/sys/dev/scsipi/atapi_wdc.c
diff -u src/sys/dev/scsipi/atapi_wdc.c:1.123.4.12 src/sys/dev/scsipi/atapi_wdc.c:1.123.4.13
--- src/sys/dev/scsipi/atapi_wdc.c:1.123.4.12	Sat Aug 12 14:41:54 2017
+++ src/sys/dev/scsipi/atapi_wdc.c	Sun Sep 10 19:31:15 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: atapi_wdc.c,v 1.123.4.12 2017/08/12 14:41:54 jdolecek Exp $	*/
+/*	$NetBSD: atapi_wdc.c,v 1.123.4.13 2017/09/10 19:31:15 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.123.4.12 2017/08/12 14:41:54 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.123.4.13 2017/09/10 19:31:15 jdolecek Exp $");
 
 #ifndef ATADEBUG
 #define ATADEBUG
@@ -82,11 +82,12 @@ static int	wdc_atapi_get_params(struct s
 				     struct ataparams *);
 static void	wdc_atapi_probe_device(struct atapibus_softc *, int);
 static void	wdc_atapi_minphys (struct buf *bp);
-static void	wdc_atapi_start(struct ata_channel *,struct ata_xfer *);
+static int	wdc_atapi_start(struct ata_channel *,struct ata_xfer *);
 static int	wdc_atapi_intr(struct ata_channel *, struct ata_xfer *, int);
 static void	wdc_atapi_kill_xfer(struct ata_channel *,
 				    struct ata_xfer *, int);
 static void	wdc_atapi_phase_complete(struct ata_xfer *);
+static void	wdc_atapi_poll(struct ata_channel *, struct ata_xfer *);
 static void	wdc_atapi_done(struct ata_channel *, struct ata_xfer *);
 static void	wdc_atapi_reset(struct ata_channel *, struct ata_xfer *);
 static void	wdc_atapi_scsipi_request(struct scsipi_channel *,
@@ -453,6 +454,8 @@ wdc_atapi_scsipi_request(struct scsipi_c
 		xfer->c_bcount = sc_xfer->datalen;
 		xfer->c_start = wdc_atapi_start;
 		xfer->c_intr = wdc_atapi_intr;
+		xfer->c_poll = wdc_atapi_poll;
+		xfer->c_abort = wdc_atapi_reset;
 		xfer->c_kill_xfer = wdc_atapi_kill_xfer;
 		xfer->c_dscpoll = 0;
 		s = splbio();
@@ -472,7 +475,7 @@ wdc_atapi_scsipi_request(struct scsipi_c
 	}
 }
 
-static void
+static int
 wdc_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
 	struct atac_softc *atac = chp->ch_atac;
@@ -487,6 +490,9 @@ wdc_atapi_start(struct ata_channel *chp,
 	ATADEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x \n",
 	    device_xname(atac->atac_dev), chp->ch_channel, drvp->drive,
 	    sc_xfer->xs_control), DEBUG_XFERS);
+
+	ata_channel_lock_owned(chp);
+
 #if NATA_DMA
 	if ((xfer->c_flags & C_DMA) && (drvp->n_xfers <= NXFER))
 		drvp->n_xfers++;
@@ -496,8 +502,7 @@ wdc_atapi_start(struct ata_channel *chp,
 		/* If it's not a polled command, we need the kernel thread */
 		if ((sc_xfer->xs_control & XS_CTL_POLL) == 0 &&
 		    (chp->ch_flags & ATACH_TH_RUN) == 0) {
-			ata_thread_wake(chp);
-			return;
+			return ATASTART_TH;
 		}
 		/*
 		 * disable interrupts, all commands here should be quick
@@ -604,10 +609,9 @@ ready:
 		printf("wdc_atapi_start: not ready, st = %02x\n",
 		    ATACH_ST(tfd));
 		sc_xfer->error = XS_TIMEOUT;
-		wdc_atapi_reset(chp, xfer);
-		return;
+		return ATASTART_ABORT;
 	case WDCWAIT_THR:
-		return;
+		return ATASTART_TH;
 	}
 
 	/*
@@ -663,29 +667,14 @@ ready:
 #endif
 	/*
 	 * If there is no interrupt for CMD input, busy-wait for it (done in
-	 * the interrupt routine. If it is a polled command, call the interrupt
-	 * routine until command is done.
+	 * the interrupt routine. Poll routine will exit early in this case.
 	 */
 	if ((sc_xfer->xs_periph->periph_cap & ATAPI_CFG_DRQ_MASK) !=
-	    ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL)) {
-		/* Wait for at last 400ns for status bit to be valid */
-		DELAY(1);
-		wdc_atapi_intr(chp, xfer, 0);
-	}
-	if (sc_xfer->xs_control & XS_CTL_POLL) {
-#if NATA_DMA
-		if (chp->ch_flags & ATACH_DMA_WAIT) {
-			wdc_dmawait(chp, xfer, sc_xfer->timeout);
-			chp->ch_flags &= ~ATACH_DMA_WAIT;
-		}
-#endif
-		while ((sc_xfer->xs_status & XS_STS_DONE) == 0) {
-			/* Wait for at last 400ns for status bit to be valid */
-			DELAY(1);
-			wdc_atapi_intr(chp, xfer, 0);
-		}
-	}
-	return;
+	    ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL))
+		return ATASTART_POLL;
+	else
+		return ATASTART_STARTED;
+
 timeout:
 	printf("%s:%d:%d: %s timed out\n",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
@@ -693,8 +682,8 @@ timeout:
 	sc_xfer->error = XS_TIMEOUT;
 	bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT);
 	delay(10); /* some drives need a little delay here */
-	wdc_atapi_reset(chp, xfer);
-	return;
+	return ATASTART_ABORT;
+
 error:
 	printf("%s:%d:%d: %s ",
 	    device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
@@ -704,8 +693,37 @@ error:
 	sc_xfer->sense.atapi_sense = ATACH_ERR(tfd);
 	bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT);
 	delay(10); /* some drives need a little delay here */
-	wdc_atapi_reset(chp, xfer);
-	return;
+	return ATASTART_ABORT;
+}
+
+static void
+wdc_atapi_poll(struct ata_channel *chp, struct ata_xfer *xfer)
+{
+	/*
+	 * If there is no interrupt for CMD input, busy-wait for it (done in
+	 * the interrupt routine. If it is a polled command, call the interrupt
+	 * routine until command is done.
+	 */
+	const bool poll = ((xfer->c_scsipi->xs_control & XS_CTL_POLL) != 0);
+
+	/* Wait for at last 400ns for status bit to be valid */
+	DELAY(1);
+	wdc_atapi_intr(chp, xfer, 0);
+
+	if (!poll)
+		return;
+
+#if NATA_DMA
+	if (chp->ch_flags & ATACH_DMA_WAIT) {
+		wdc_dmawait(chp, xfer, xfer->c_scsipi->timeout);
+		chp->ch_flags &= ~ATACH_DMA_WAIT;
+	}
+#endif
+	while ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) {
+		/* Wait for at last 400ns for status bit to be valid */
+		DELAY(1);
+		wdc_atapi_intr(chp, xfer, 0);
+	}
 }
 
 static int
@@ -731,6 +749,8 @@ wdc_atapi_intr(struct ata_channel *chp, 
 	    device_xname(atac->atac_dev), chp->ch_channel, drvp->drive),
 	    DEBUG_INTR);
 
+	ata_channel_lock(chp);
+
 	/* Is it not a transfer, but a control operation? */
 	if (drvp->state < READY) {
 		printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n",
@@ -743,6 +763,7 @@ wdc_atapi_intr(struct ata_channel *chp, 
 	 * Don't try to continue transfer, we may have missed cycles.
 	 */
 	if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_TIMEOUT;
 		wdc_atapi_reset(chp, xfer);
 		return 1;
@@ -772,8 +793,10 @@ wdc_atapi_intr(struct ata_channel *chp, 
 	    WDSD_IBM | (xfer->c_drive << 4));
 	if (wdc_wait_for_unbusy(chp,
 	    poll ? sc_xfer->timeout : 0, AT_POLL, &tfd) == WDCWAIT_TOUT) {
-		if (!poll && (xfer->c_flags & C_TIMEOU) == 0)
+		if (!poll && (xfer->c_flags & C_TIMEOU) == 0) {
+			ata_channel_unlock(chp);
 			return 0; /* IRQ was not for us */
+		}
 		printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n",
 		    device_xname(atac->atac_dev), chp->ch_channel,
 		    xfer->c_drive, xfer->c_bcount, xfer->c_skip);
@@ -783,6 +806,7 @@ wdc_atapi_intr(struct ata_channel *chp, 
 			    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
 		}
 #endif
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_TIMEOUT;
 		wdc_atapi_reset(chp, xfer);
 		return 1;
@@ -797,6 +821,7 @@ wdc_atapi_intr(struct ata_channel *chp, 
 	 */
 	if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) {
 		ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0);
+		ata_channel_unlock(chp);
 		sc_xfer->error = XS_RESET;
 		wdc_atapi_reset(chp, xfer);
 		return (1);
@@ -860,6 +885,7 @@ again:
 			chp->ch_flags |= ATACH_DMA_WAIT;
 		}
 #endif
+		ata_channel_unlock(chp);
 		return 1;
 
 	 case PHASE_DATAOUT:
@@ -873,6 +899,7 @@ again:
 				ata_dmaerr(drvp,
 				    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
 			}
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_TIMEOUT;
 			wdc_atapi_reset(chp, xfer);
 			return 1;
@@ -892,6 +919,7 @@ again:
 			    chp->ch_channel, xfer->c_drive,
 			    xfer->c_skip, len, WDC_PIOBM_XFER_IRQ);
 			chp->ch_flags |= ATACH_DMA_WAIT | ATACH_PIOBM_WAIT;
+			ata_channel_unlock(chp);
 			return 1;
 		}
 #endif
@@ -907,6 +935,7 @@ again:
 
 		xfer->c_skip += len;
 		xfer->c_bcount -= len;
+		ata_channel_unlock(chp);
 		return 1;
 
 	case PHASE_DATAIN:
@@ -920,6 +949,7 @@ again:
 				ata_dmaerr(drvp,
 				    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
 			}
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_TIMEOUT;
 			wdc_atapi_reset(chp, xfer);
 			return 1;
@@ -939,6 +969,7 @@ again:
 			    chp->ch_channel, xfer->c_drive,
 			    xfer->c_skip, len, WDC_PIOBM_XFER_IRQ);
 			chp->ch_flags |= ATACH_DMA_WAIT | ATACH_PIOBM_WAIT;
+			ata_channel_unlock(chp);
 			return 1;
 		}
 #endif
@@ -953,6 +984,7 @@ again:
 
 		xfer->c_skip += len;
 		xfer->c_bcount -= len;
+		ata_channel_unlock(chp);
 		return 1;
 
 	case PHASE_ABORTED:
@@ -964,6 +996,7 @@ again:
 		}
 #endif
 		sc_xfer->resid = xfer->c_bcount;
+		/* this will unlock channel lock too */
 		wdc_atapi_phase_complete(xfer);
 		return(1);
 
@@ -989,6 +1022,7 @@ again:
 				    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
 			}
 #endif
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_RESET;
 			wdc_atapi_reset(chp, xfer);
 			return (1);
@@ -997,6 +1031,7 @@ again:
 	ATADEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x "
 	    "sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense),
 	    DEBUG_INTR);
+	ata_channel_unlock(chp);
 	wdc_atapi_done(chp, xfer);
 	return (1);
 }
@@ -1013,6 +1048,8 @@ wdc_atapi_phase_complete(struct ata_xfer
 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
 	int tfd;
 
+	ata_channel_lock_owned(chp);
+
 	/* wait for DSC if needed */
 	if (drvp->drive_flags & ATA_DRIVE_ATAPIDSCW) {
 		ATADEBUG_PRINT(("wdc_atapi_phase_complete(%s:%d:%d) "
@@ -1032,12 +1069,14 @@ wdc_atapi_phase_complete(struct ata_xfer
 				    "failed\n",
 				    device_xname(atac->atac_dev),
 				    chp->ch_channel, xfer->c_drive);
+				ata_channel_unlock(chp);
 				sc_xfer->error = XS_TIMEOUT;
 				wdc_atapi_reset(chp, xfer);
-				return;
-			} else
+			} else {
 				callout_reset(&xfer->c_timo_callout, 1,
 				    wdc_atapi_polldsc, xfer);
+				ata_channel_unlock(chp);
+			}
 			return;
 		}
 	}
@@ -1067,6 +1106,7 @@ wdc_atapi_phase_complete(struct ata_xfer
 			ata_dmaerr(drvp,
 			    (xfer->c_flags & C_POLL) ? AT_POLL : 0);
 #endif
+			ata_channel_unlock(chp);
 			sc_xfer->error = XS_RESET;
 			wdc_atapi_reset(chp, xfer);
 			return;
@@ -1086,6 +1126,7 @@ wdc_atapi_phase_complete(struct ata_xfer
 	ATADEBUG_PRINT(("wdc_atapi_phase_complete: wdc_atapi_done(), "
 	    "error 0x%x sense 0x%x\n", sc_xfer->error,
 	    sc_xfer->sense.atapi_sense), DEBUG_INTR);
+	ata_channel_unlock(chp);
 	wdc_atapi_done(chp, xfer);
 }
 
@@ -1120,6 +1161,7 @@ wdc_atapi_reset(struct ata_channel *chp,
 	struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
 	int tfd;
 
+	ata_channel_lock(chp);
 	wdccommandshort(chp, xfer->c_drive, ATAPI_SOFT_RESET);
 	drvp->state = 0;
 	if (wdc_wait_for_unbusy(chp, WDC_RESET_WAIT, AT_POLL, &tfd) != 0) {
@@ -1128,6 +1170,7 @@ wdc_atapi_reset(struct ata_channel *chp,
 		    xfer->c_drive);
 		sc_xfer->error = XS_SELTIMEOUT;
 	}
+	ata_channel_unlock(chp);
 	wdc_atapi_done(chp, xfer);
 	return;
 }
@@ -1135,6 +1178,11 @@ wdc_atapi_reset(struct ata_channel *chp,
 static void
 wdc_atapi_polldsc(void *arg)
 {
+	struct ata_xfer *xfer = arg;
+	struct ata_channel *chp = xfer->c_chp;
+
+	ata_channel_lock(chp);
 
-	wdc_atapi_phase_complete(arg);
+	/* this will unlock channel lock too */
+	wdc_atapi_phase_complete(xfer);
 }

Reply via email to