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); }