Module Name: src
Committed By: jdolecek
Date: Thu Oct 11 20:57:51 UTC 2018
Modified Files:
src/sys/dev/ata [jdolecek-ncqfixes]: TODO.ncq ata.c ata_subr.c atavar.h
files.ata
src/sys/dev/ic [jdolecek-ncqfixes]: ahcisata_core.c ahcisatavar.h
mvsata.c mvsatavar.h siisata.c siisatavar.h
Added Files:
src/sys/dev/ata [jdolecek-ncqfixes]: ata_recovery.c
Log Message:
refactor shared parts of the SATA error recovery into new function
ata_recovery_resume() and use for ahcisata/siisata/mvsata, also replace
per-controller hold/unhold with generic version
move the shared recovery code into separate file ata_recovery.c
To generate a diff of this commit:
cvs rdiff -u -r1.4.2.12 -r1.4.2.13 src/sys/dev/ata/TODO.ncq
cvs rdiff -u -r1.141.6.15 -r1.141.6.16 src/sys/dev/ata/ata.c
cvs rdiff -u -r0 -r1.1.2.1 src/sys/dev/ata/ata_recovery.c
cvs rdiff -u -r1.6.2.7 -r1.6.2.8 src/sys/dev/ata/ata_subr.c
cvs rdiff -u -r1.99.2.10 -r1.99.2.11 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.27 -r1.27.6.1 src/sys/dev/ata/files.ata
cvs rdiff -u -r1.62.2.8 -r1.62.2.9 src/sys/dev/ic/ahcisata_core.c
cvs rdiff -u -r1.18 -r1.18.6.1 src/sys/dev/ic/ahcisatavar.h
cvs rdiff -u -r1.41.2.6 -r1.41.2.7 src/sys/dev/ic/mvsata.c
cvs rdiff -u -r1.4 -r1.4.2.1 src/sys/dev/ic/mvsatavar.h
cvs rdiff -u -r1.35.6.8 -r1.35.6.9 src/sys/dev/ic/siisata.c
cvs rdiff -u -r1.7 -r1.7.6.1 src/sys/dev/ic/siisatavar.h
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.4.2.12 src/sys/dev/ata/TODO.ncq:1.4.2.13
--- src/sys/dev/ata/TODO.ncq:1.4.2.12 Sat Oct 6 20:27:28 2018
+++ src/sys/dev/ata/TODO.ncq Thu Oct 11 20:57:51 2018
@@ -4,7 +4,8 @@ jdolecek-ncqfixes goals:
- run recovery via atathread, move to new function and share ahci/siisata/mvsata
- maybe do device error handling in not-interrupt-context (maybe this should be
done on a mpata branch?)
-- remove controller-specific slot bitmaps (ic/siisata.c, ic/ahcisata_core.c)
+- adjust mvsata() intr code to accept tfd (instead of irq 0/1) so that
+ ata_recovery_resume() works properly for it
Bugs
----
Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.141.6.15 src/sys/dev/ata/ata.c:1.141.6.16
--- src/sys/dev/ata/ata.c:1.141.6.15 Sat Oct 6 21:19:55 2018
+++ src/sys/dev/ata/ata.c Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: ata.c,v 1.141.6.15 2018/10/06 21:19:55 jdolecek Exp $ */
+/* $NetBSD: ata.c,v 1.141.6.16 2018/10/11 20:57:51 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.141.6.15 2018/10/06 21:19:55 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.141.6.16 2018/10/11 20:57:51 jdolecek Exp $");
#include "opt_ata.h"
@@ -933,98 +933,6 @@ out:
return rv;
}
-int
-ata_read_log_ext_ncq(struct ata_drive_datas *drvp, uint8_t flags,
- uint8_t *slot, uint8_t *status, uint8_t *err)
-{
- struct ata_xfer *xfer = &drvp->recovery_xfer;
- int rv;
- struct ata_channel *chp = drvp->chnl_softc;
- struct atac_softc *atac = chp->ch_atac;
- uint8_t *tb, cksum, page;
-
- ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS);
-
- /* Only NCQ ATA drives support/need this */
- if (drvp->drive_type != ATA_DRIVET_ATA ||
- (drvp->drive_flags & ATA_DRIVE_NCQ) == 0)
- return EOPNOTSUPP;
-
- memset(xfer, 0, sizeof(*xfer));
-
- tb = drvp->recovery_blk;
- memset(tb, 0, sizeof(drvp->recovery_blk));
-
- /*
- * We could use READ LOG DMA EXT if drive supports it (i.e.
- * when it supports Streaming feature) to avoid PIO command,
- * and to make this a little faster. Realistically, it
- * should not matter.
- */
- xfer->c_flags |= C_SKIP_QUEUE;
- xfer->c_ata_c.r_command = WDCC_READ_LOG_EXT;
- xfer->c_ata_c.r_lba = page = WDCC_LOG_PAGE_NCQ;
- xfer->c_ata_c.r_st_bmask = WDCS_DRDY;
- xfer->c_ata_c.r_st_pmask = WDCS_DRDY;
- xfer->c_ata_c.r_count = 1;
- xfer->c_ata_c.r_device = WDSD_LBA;
- xfer->c_ata_c.flags = AT_READ | AT_LBA | AT_LBA48 | flags;
- xfer->c_ata_c.timeout = 1000; /* 1s */
- xfer->c_ata_c.data = tb;
- xfer->c_ata_c.bcount = sizeof(drvp->recovery_blk);
-
- if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
- xfer) != ATACMD_COMPLETE) {
- rv = EAGAIN;
- goto out;
- }
- if (xfer->c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
- rv = EINVAL;
- goto out;
- }
-
- cksum = 0;
- for (int i = 0; i < sizeof(drvp->recovery_blk); i++)
- cksum += tb[i];
- if (cksum != 0) {
- device_printf(drvp->drv_softc,
- "invalid checksum %x for READ LOG EXT page %x\n",
- cksum, page);
- rv = EINVAL;
- goto out;
- }
-
- if (tb[0] & WDCC_LOG_NQ) {
- /* not queued command */
- rv = EOPNOTSUPP;
- goto out;
- }
-
- *slot = tb[0] & 0x1f;
- *status = tb[2];
- *err = tb[3];
-
- if ((*status & WDCS_ERR) == 0) {
- /*
- * We expect error here. Normal physical drives always
- * do, it's part of ATA standard. However, QEMU AHCI emulation
- * misehandles READ LOG EXT in a way that the command itself
- * returns without error, but no data is transferred.
- */
- device_printf(drvp->drv_softc,
- "READ LOG EXT page %x failed to report error: "
- "slot %d err %x status %x\n",
- page, *slot, *err, *status);
- rv = EOPNOTSUPP;
- goto out;
- }
-
- rv = 0;
-
-out:
- return rv;
-}
-
#if NATA_DMA
void
ata_dmaerr(struct ata_drive_datas *drvp, int flags)
Index: src/sys/dev/ata/ata_subr.c
diff -u src/sys/dev/ata/ata_subr.c:1.6.2.7 src/sys/dev/ata/ata_subr.c:1.6.2.8
--- src/sys/dev/ata/ata_subr.c:1.6.2.7 Sat Oct 6 21:19:55 2018
+++ src/sys/dev/ata/ata_subr.c Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: ata_subr.c,v 1.6.2.7 2018/10/06 21:19:55 jdolecek Exp $ */
+/* $NetBSD: ata_subr.c,v 1.6.2.8 2018/10/11 20:57:51 jdolecek Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@@ -25,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.6.2.7 2018/10/06 21:19:55 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.6.2.8 2018/10/11 20:57:51 jdolecek Exp $");
#include "opt_ata.h"
@@ -333,3 +333,42 @@ ata_queue_free_slot(struct ata_channel *
chq->queue_xfers_avail |= __BIT(c_slot);
}
+
+void
+ata_queue_hold(struct ata_channel *chp)
+{
+ struct ata_queue *chq = chp->ch_queue;
+
+ KASSERT(mutex_owned(&chp->ch_lock));
+
+ chq->queue_hold |= chq->active_xfers_used;
+ chq->active_xfers_used = 0;
+}
+
+void
+ata_queue_unhold(struct ata_channel *chp)
+{
+ struct ata_queue *chq = chp->ch_queue;
+
+ KASSERT(mutex_owned(&chp->ch_lock));
+
+ chq->active_xfers_used |= chq->queue_hold;
+ chq->queue_hold = 0;
+}
+
+/*
+ * Must be called with interrupts blocked.
+ */
+uint32_t
+ata_queue_active(struct ata_channel *chp)
+{
+ struct ata_queue *chq = chp->ch_queue;
+
+ return chq->active_xfers_used;
+}
+
+uint8_t
+ata_queue_openings(struct ata_channel *chp)
+{
+ return chp->ch_queue->queue_openings;
+}
Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.99.2.10 src/sys/dev/ata/atavar.h:1.99.2.11
--- src/sys/dev/ata/atavar.h:1.99.2.10 Sat Oct 6 21:19:55 2018
+++ src/sys/dev/ata/atavar.h Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: atavar.h,v 1.99.2.10 2018/10/06 21:19:55 jdolecek Exp $ */
+/* $NetBSD: atavar.h,v 1.99.2.11 2018/10/11 20:57:51 jdolecek Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -233,6 +233,7 @@ struct ata_queue {
TAILQ_HEAD(, ata_xfer) active_xfers; /* active commands */
uint32_t active_xfers_used; /* mask of active commands */
uint32_t queue_xfers_avail; /* available xfers mask */
+ uint32_t queue_hold; /* slots held during recovery */
kcondvar_t c_active; /* somebody actively waiting for xfer */
kcondvar_t c_cmd_finish; /* somebody waiting for cmd finish */
};
@@ -414,6 +415,7 @@ struct ata_channel {
#define ATACH_NCQ 0x800 /* channel executing NCQ commands */
#define ATACH_DMA_BEFORE_CMD 0x1000 /* start DMA first */
#define ATACH_TH_DRIVE_RESET 0x2000 /* thread asked for drive(s) reset */
+#define ATACH_RECOVERING 0x4000 /* channel is recovering */
#define ATACH_NODRIVE 0xff /* no drive selected for reset */
@@ -524,6 +526,7 @@ int ata_get_params(struct ata_drive_data
int ata_set_mode(struct ata_drive_datas *, uint8_t, uint8_t);
int ata_read_log_ext_ncq(struct ata_drive_datas *, uint8_t, uint8_t *,
uint8_t *, uint8_t *);
+void ata_recovery_resume(struct ata_channel *, int, int, int);
/* return code for these cmds */
#define CMD_OK 0
@@ -573,6 +576,10 @@ struct ata_xfer *
ata_queue_drive_active_xfer(struct ata_channel *, int);
bool ata_queue_alloc_slot(struct ata_channel *, uint8_t *, uint8_t);
void ata_queue_free_slot(struct ata_channel *, uint8_t);
+uint32_t ata_queue_active(struct ata_channel *);
+uint8_t ata_queue_openings(struct ata_channel *);
+void ata_queue_hold(struct ata_channel *);
+void ata_queue_unhold(struct ata_channel *);
void ata_delay(struct ata_channel *, int, const char *, int);
Index: src/sys/dev/ata/files.ata
diff -u src/sys/dev/ata/files.ata:1.27 src/sys/dev/ata/files.ata:1.27.6.1
--- src/sys/dev/ata/files.ata:1.27 Tue Oct 10 17:19:38 2017
+++ src/sys/dev/ata/files.ata Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-# $NetBSD: files.ata,v 1.27 2017/10/10 17:19:38 jdolecek Exp $
+# $NetBSD: files.ata,v 1.27.6.1 2018/10/11 20:57:51 jdolecek Exp $
#
# Config file and device description for machine-independent devices
# which attach to ATA busses. Included by ports that need it. Ports
@@ -16,6 +16,7 @@ defflag opt_wd.h WD_CHAOS_MONKEY
file dev/ata/ata.c (ata_hl | atapi) & atabus
file dev/ata/ata_subr.c (ata_hl | atapi)
+file dev/ata/ata_recovery.c (ata_hl | atapi)
# ATA RAID configuration support
defpseudodev ataraid {[vendtype = -1], [unit = -1]}
Index: src/sys/dev/ic/ahcisata_core.c
diff -u src/sys/dev/ic/ahcisata_core.c:1.62.2.8 src/sys/dev/ic/ahcisata_core.c:1.62.2.9
--- src/sys/dev/ic/ahcisata_core.c:1.62.2.8 Sun Oct 7 15:44:47 2018
+++ src/sys/dev/ic/ahcisata_core.c Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: ahcisata_core.c,v 1.62.2.8 2018/10/07 15:44:47 jdolecek Exp $ */
+/* $NetBSD: ahcisata_core.c,v 1.62.2.9 2018/10/11 20:57:51 jdolecek Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.62.2.8 2018/10/07 15:44:47 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.62.2.9 2018/10/11 20:57:51 jdolecek Exp $");
#include <sys/types.h>
#include <sys/malloc.h>
@@ -569,6 +569,7 @@ ahci_intr_port(struct ahci_softc *sc, st
struct ata_xfer *xfer;
int slot = -1;
bool recover = false;
+ uint32_t aslots;
is = AHCI_READ(sc, AHCI_P_IS(chp->ch_channel));
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), is);
@@ -622,14 +623,14 @@ ahci_intr_port(struct ahci_softc *sc, st
DEBUG_INTR);
}
- if (!achp->ahcic_recovering)
+ if (!ISSET(chp->ch_flags, ATACH_RECOVERING))
recover = true;
} else if (is & (AHCI_P_IX_DHRS|AHCI_P_IX_SDBS)) {
tfd = AHCI_READ(sc, AHCI_P_TFD(chp->ch_channel));
/* D2H Register FIS or Set Device Bits */
if ((tfd & WDCS_ERR) != 0) {
- if (!achp->ahcic_recovering)
+ if (!ISSET(chp->ch_flags, ATACH_RECOVERING))
recover = true;
AHCIDEBUG_PRINT(("%s port %d: transfer aborted 0x%x\n",
@@ -643,8 +644,10 @@ ahci_intr_port(struct ahci_softc *sc, st
if (__predict_false(recover))
ata_channel_freeze(chp);
+ aslots = ata_queue_active(chp);
+
if (slot >= 0) {
- if ((achp->ahcic_cmds_active & __BIT(slot)) != 0 &&
+ if ((aslots & __BIT(slot)) != 0 &&
(sact & __BIT(slot)) == 0) {
xfer = ata_queue_hwslot_to_xfer(chp, slot);
xfer->ops->c_intr(chp, xfer, tfd);
@@ -659,7 +662,6 @@ ahci_intr_port(struct ahci_softc *sc, st
* can activate another command(s), so must only process
* commands active before we start processing.
*/
- uint32_t aslots = achp->ahcic_cmds_active;
for (slot=0; slot < sc->sc_ncmds; slot++) {
if ((aslots & __BIT(slot)) != 0 &&
@@ -1071,7 +1073,6 @@ ahci_cmd_start(struct ata_channel *chp,
DEBUG_XFERS);
ata_channel_lock_owned(chp);
- KASSERT((achp->ahcic_cmds_active & (1U << slot)) == 0);
cmd_tbl = achp->ahcic_cmd_tbl[slot];
AHCIDEBUG_PRINT(("%s port %d tbl %p\n", AHCINAME(sc), chp->ch_channel,
@@ -1105,8 +1106,6 @@ ahci_cmd_start(struct ata_channel *chp,
}
/* start command */
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << slot);
- /* and says we started this command */
- achp->ahcic_cmds_active |= 1U << slot;
if ((ata_c->flags & AT_POLL) == 0) {
callout_reset(&chp->c_timo_callout, mstohz(ata_c->timeout),
@@ -1164,7 +1163,6 @@ ahci_cmd_abort(struct ata_channel *chp,
static void
ahci_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason)
{
- struct ahci_channel *achp = (struct ahci_channel *)chp;
struct ata_command *ata_c = &xfer->c_ata_c;
bool deactivate = true;
@@ -1191,11 +1189,8 @@ ahci_cmd_kill_xfer(struct ata_channel *c
ahci_cmd_done_end(chp, xfer);
- if (deactivate) {
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
}
static int
@@ -1229,8 +1224,6 @@ ahci_cmd_complete(struct ata_channel *ch
ahci_cmd_done(chp, xfer);
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
ata_deactivate_xfer(chp, xfer);
if ((ata_c->flags & (AT_TIMEOU|AT_ERROR)) == 0)
@@ -1359,8 +1352,6 @@ ahci_bio_start(struct ata_channel *chp,
AHCI_WRITE(sc, AHCI_P_SACT(chp->ch_channel), 1U << xfer->c_slot);
/* start command */
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot);
- /* and says we started this command */
- achp->ahcic_cmds_active |= 1U << xfer->c_slot;
if ((xfer->c_flags & C_POLL) == 0) {
callout_reset(&chp->c_timo_callout, mstohz(ATA_DELAY),
@@ -1413,7 +1404,6 @@ ahci_bio_kill_xfer(struct ata_channel *c
{
int drive = xfer->c_drive;
struct ata_bio *ata_bio = &xfer->c_bio;
- struct ahci_channel *achp = (struct ahci_channel *)chp;
bool deactivate = true;
AHCIDEBUG_PRINT(("ahci_bio_kill_xfer channel %d\n", chp->ch_channel),
@@ -1439,11 +1429,8 @@ ahci_bio_kill_xfer(struct ata_channel *c
}
ata_bio->r_error = WDCE_ABRT;
- if (deactivate) {
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer);
}
@@ -1503,8 +1490,6 @@ ahci_bio_complete(struct ata_channel *ch
}
AHCIDEBUG_PRINT((" now %ld\n", ata_bio->bcount), DEBUG_XFERS);
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
ata_deactivate_xfer(chp, xfer);
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer);
@@ -1579,34 +1564,17 @@ ahci_channel_start(struct ahci_softc *sc
AHCI_WRITE(sc, AHCI_P_CMD(chp->ch_channel), p_cmd);
}
-static void
-ahci_hold(struct ahci_channel *achp)
-{
- achp->ahcic_cmds_hold |= achp->ahcic_cmds_active;
- achp->ahcic_cmds_active = 0;
-}
-
-static void
-ahci_unhold(struct ahci_channel *achp)
-{
- achp->ahcic_cmds_active = achp->ahcic_cmds_hold;
- achp->ahcic_cmds_hold = 0;
-}
-
/* Recover channel after command failure */
void
ahci_channel_recover(struct ahci_softc *sc, struct ata_channel *chp, int tfd)
{
- struct ahci_channel *achp = (struct ahci_channel *)chp;
- struct ata_drive_datas *drvp;
- uint8_t slot, eslot, st, err;
- int drive = -1, error;
- struct ata_xfer *xfer;
+ int drive = ATACH_NODRIVE;
bool reset = false;
- KASSERT(!achp->ahcic_recovering);
-
- achp->ahcic_recovering = true;
+ ata_channel_lock(chp);
+ KASSERT(!ISSET(chp->ch_flags, ATACH_RECOVERING));
+ SET(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
/*
* Read FBS to get the drive which caused the error, if PM is in use.
@@ -1637,19 +1605,17 @@ ahci_channel_recover(struct ahci_softc *
}
if ((fbs & AHCI_P_FBS_DEC) != 0) {
/* follow non-device specific recovery */
- drive = -1;
+ drive = ATACH_NODRIVE;
reset = true;
}
} else {
/* not device specific, reset channel */
- drive = -1;
+ drive = ATACH_NODRIVE;
reset = true;
}
} else
drive = 0;
- drvp = &chp->ch_drive[drive];
-
/*
* If BSY or DRQ bits are set, must execute COMRESET to return
* device to idle state. If drive is idle, it's enough to just
@@ -1657,86 +1623,26 @@ ahci_channel_recover(struct ahci_softc *
* After resetting CMD.ST, need to execute READ LOG EXT for NCQ
* to unblock device processing if COMRESET was not done.
*/
- if (reset || (AHCI_TFD_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0)
- goto reset;
-
- KASSERT(drive >= 0);
- ahci_channel_stop(sc, chp, AT_POLL);
- ahci_channel_start(sc, chp, AT_POLL,
- (sc->sc_ahci_cap & AHCI_CAP_CLO) ? 1 : 0);
-
- ahci_hold(achp);
-
- /*
- * When running NCQ commands, READ LOG EXT is necessary to clear the
- * error condition and unblock the device.
- */
- error = ata_read_log_ext_ncq(drvp, AT_POLL, &eslot, &st, &err);
-
- ahci_unhold(achp);
-
- switch (error) {
- case 0:
- /* Error out the particular NCQ xfer, then requeue the others */
- if ((achp->ahcic_cmds_active & (1U << eslot)) != 0) {
- xfer = ata_queue_hwslot_to_xfer(chp, eslot);
- xfer->c_flags |= C_RECOVERED;
- xfer->ops->c_intr(chp, xfer,
- (err << AHCI_P_TFD_ERR_SHIFT) | st);
- }
- break;
-
- case EOPNOTSUPP:
- /*
- * Non-NCQ command error, just find the slot and end with
- * the error.
- */
- for (slot = 0; slot < sc->sc_ncmds; slot++) {
- if ((achp->ahcic_cmds_active & (1U << slot)) != 0) {
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- xfer->ops->c_intr(chp, xfer, tfd);
- }
- }
- break;
-
- case EAGAIN:
- /*
- * Failed to get resources to run the recovery command, must
- * reset the drive. This will also kill all still outstanding
- * transfers.
- */
-reset:
+ if (reset || (AHCI_TFD_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0) {
ata_channel_lock(chp);
ahci_reset_channel(chp, AT_POLL);
ata_channel_unlock(chp);
goto out;
- /* NOTREACHED */
-
- default:
- /*
- * The command to get the slot failed. Kill outstanding
- * commands for the same drive only. No need to reset
- * the drive, it's unblocked nevertheless.
- */
- break;
}
- /* Requeue all unfinished commands for same drive as failed command */
- for (slot = 0; slot < sc->sc_ncmds; slot++) {
- if ((achp->ahcic_cmds_active & (1U << slot)) == 0)
- continue;
-
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (drive != xfer->c_drive)
- continue;
+ KASSERT(drive != ATACH_NODRIVE && drive >= 0);
+ ahci_channel_stop(sc, chp, AT_POLL);
+ ahci_channel_start(sc, chp, AT_POLL,
+ (sc->sc_ahci_cap & AHCI_CAP_CLO) ? 1 : 0);
- xfer->ops->c_kill_xfer(chp, xfer,
- (error == 0) ? KILL_REQUEUE : KILL_RESET);
- }
+ ata_recovery_resume(chp, drive, tfd, AT_POLL);
out:
/* Drive unblocked, back to normal operation */
- achp->ahcic_recovering = false;
+ ata_channel_lock(chp);
+ CLR(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
+
atastart(chp);
}
@@ -1951,8 +1857,6 @@ ahci_atapi_start(struct ata_channel *chp
}
/* start command */
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot);
- /* and says we started this command */
- achp->ahcic_cmds_active |= 1U << xfer->c_slot;
if ((xfer->c_flags & C_POLL) == 0) {
callout_reset(&chp->c_timo_callout, mstohz(sc_xfer->timeout),
@@ -2044,8 +1948,6 @@ ahci_atapi_complete(struct ata_channel *
}
}
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
ata_deactivate_xfer(chp, xfer);
ata_free_xfer(chp, xfer);
@@ -2059,7 +1961,6 @@ static void
ahci_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason)
{
struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
- struct ahci_channel *achp = (struct ahci_channel *)chp;
bool deactivate = true;
/* remove this command from xfer queue */
@@ -2081,11 +1982,8 @@ ahci_atapi_kill_xfer(struct ata_channel
panic("ahci_ata_atapi_kill_xfer");
}
- if (deactivate) {
- KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0);
- achp->ahcic_cmds_active &= ~(1U << xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
ata_free_xfer(chp, xfer);
scsipi_done(sc_xfer);
Index: src/sys/dev/ic/ahcisatavar.h
diff -u src/sys/dev/ic/ahcisatavar.h:1.18 src/sys/dev/ic/ahcisatavar.h:1.18.6.1
--- src/sys/dev/ic/ahcisatavar.h:1.18 Sat Oct 7 16:05:32 2017
+++ src/sys/dev/ic/ahcisatavar.h Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: ahcisatavar.h,v 1.18 2017/10/07 16:05:32 jdolecek Exp $ */
+/* $NetBSD: ahcisatavar.h,v 1.18.6.1 2018/10/11 20:57:51 jdolecek Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@@ -82,9 +82,6 @@ struct ahci_softc {
struct ahci_cmd_tbl *ahcic_cmd_tbl[AHCI_MAX_CMDS];
bus_addr_t ahcic_bus_cmd_tbl[AHCI_MAX_CMDS];
bus_dmamap_t ahcic_datad[AHCI_MAX_CMDS];
- volatile uint32_t ahcic_cmds_active; /* active commands */
- uint32_t ahcic_cmds_hold; /* held commands */
- bool ahcic_recovering;
} sc_channels[AHCI_MAX_PORTS];
void (*sc_channel_start)(struct ahci_softc *, struct ata_channel *);
Index: src/sys/dev/ic/mvsata.c
diff -u src/sys/dev/ic/mvsata.c:1.41.2.6 src/sys/dev/ic/mvsata.c:1.41.2.7
--- src/sys/dev/ic/mvsata.c:1.41.2.6 Thu Oct 4 17:59:35 2018
+++ src/sys/dev/ic/mvsata.c Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: mvsata.c,v 1.41.2.6 2018/10/04 17:59:35 jdolecek Exp $ */
+/* $NetBSD: mvsata.c,v 1.41.2.7 2018/10/11 20:57:51 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.41.2.6 2018/10/04 17:59:35 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.41.2.7 2018/10/11 20:57:51 jdolecek Exp $");
#include "opt_mvsata.h"
@@ -172,8 +172,6 @@ static void mvsata_channel_recover(struc
static int mvsata_port_init(struct mvsata_hc *, int);
static int mvsata_wdc_reg_init(struct mvsata_port *, struct wdc_regs *);
#ifndef MVSATA_WITHOUTDMA
-static inline void mvsata_quetag_get(struct mvsata_port *, uint8_t);
-static inline void mvsata_quetag_put(struct mvsata_port *, uint8_t);
static void *mvsata_edma_resource_prepare(struct mvsata_port *, bus_dma_tag_t,
bus_dmamap_t *, size_t, int);
static void mvsata_edma_resource_purge(struct mvsata_port *, bus_dma_tag_t,
@@ -539,7 +537,8 @@ mvsata_error(struct mvsata_port *mvport)
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port);
- if (!mvport->port_recovering)
+ if (!ISSET(mvport->port_ata_channel.ch_flags,
+ ATACH_RECOVERING))
mvsata_channel_recover(mvport);
}
@@ -547,31 +546,15 @@ mvsata_error(struct mvsata_port *mvport)
}
static void
-mvsata_hold(struct mvsata_port *mvport)
-{
- mvport->port_hold_slots |= mvport->port_quetagidx;
- mvport->port_quetagidx = 0;
-}
-
-static void
-mvsata_unhold(struct mvsata_port *mvport)
-{
- mvport->port_quetagidx = mvport->port_hold_slots;
- mvport->port_hold_slots = 0;
-}
-
-static void
mvsata_channel_recover(struct mvsata_port *mvport)
{
struct ata_channel *chp = &mvport->port_ata_channel;
- struct ata_drive_datas *drvp;
- int drive, error;
- uint8_t eslot, slot, st, err;
- struct ata_xfer *xfer;
+ int drive, tfd = 1;
- KASSERT(!mvport->port_recovering);
-
- mvport->port_recovering = true;
+ ata_channel_lock(chp);
+ KASSERT(!ISSET(chp->ch_flags, ATACH_RECOVERING));
+ SET(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
if (chp->ch_ndrives > PMP_PORT_CTL) {
/* Get PM port number for the device in error. This device
@@ -582,90 +565,29 @@ mvsata_channel_recover(struct mvsata_por
} else
drive = 0;
- drvp = &chp->ch_drive[drive];
-
/*
* Controller doesn't need any special action. Simply execute
* READ LOG EXT for NCQ to unblock device processing, then continue
* as if nothing happened.
*/
- KASSERT(drive >= 0);
-
- mvsata_hold(mvport);
- /*
- * When running NCQ commands, READ LOG EXT is necessary to clear the
- * error condition and unblock the device.
- */
- error = ata_read_log_ext_ncq(drvp, AT_POLL, &eslot, &st, &err);
-
- mvsata_unhold(mvport);
+#if 0
+ XXX recheck - mvsata used to have this to handle the successful
+ recovery via read_log_ext:
- switch (error) {
- case 0:
- /* Error out the particular NCQ xfer, then requeue the others */
- if ((mvport->port_quetagidx & (1 << eslot)) != 0) {
xfer = ata_queue_hwslot_to_xfer(chp, eslot);
xfer->c_flags |= C_RECOVERED;
xfer->c_bio.error = ERROR;
xfer->c_bio.r_error = err;
xfer->ops->c_intr(chp, xfer, 1);
- }
- break;
-
- case EOPNOTSUPP:
- /*
- * Non-NCQ command error, just find the slot and end it with
- * an error. Handler figures the error itself.
- */
- for (slot = 0; slot < MVSATA_EDMAQ_LEN; slot++) {
- if ((mvport->port_quetagidx & (1 << slot)) != 0) {
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (xfer->c_drive != drive)
- continue;
-
- xfer->ops->c_intr(chp, xfer, 1);
- }
- }
- break;
-
- case EAGAIN:
- /*
- * Failed to get resources to run the recovery command, must
- * reset the drive. This will also kill all still outstanding
- * transfers.
- */
- ata_channel_lock(chp);
- mvsata_reset_channel(chp, AT_POLL);
- ata_channel_unlock(chp);
- goto out;
- /* NOTREACHED */
-
- default:
- /*
- * The command to get the slot failed. Kill outstanding
- * commands for the same drive only. No need to reset
- * the drive, it's unblocked nevertheless.
- */
- break;
- }
-
- /* Requeue the non-errorred commands */
- for (slot = 0; slot < MVSATA_EDMAQ_LEN; slot++) {
- if (((mvport->port_quetagidx >> slot) & 1) == 0)
- continue;
-
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (xfer->c_drive != drive)
- continue;
-
- xfer->ops->c_kill_xfer(chp, xfer,
- (error == 0) ? KILL_REQUEUE : KILL_RESET);
- }
+#endif
+ ata_recovery_resume(chp, drive, tfd, AT_POLL);
-out:
/* Drive unblocked, back to normal operation */
- mvport->port_recovering = false;
+ ata_channel_lock(chp);
+ CLR(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
+
atastart(chp);
}
@@ -1129,8 +1051,6 @@ mvsata_bio_start(struct ata_channel *chp
ata_channel_lock_owned(chp);
- mvsata_quetag_get(mvport, xfer->c_slot);
-
if (xfer->c_flags & C_DMA)
if (drvp->n_xfers <= NXFER)
drvp->n_xfers++;
@@ -1521,10 +1441,8 @@ mvsata_bio_kill_xfer(struct ata_channel
}
ata_bio->r_error = WDCE_ABRT;
- if (deactivate) {
- mvsata_quetag_put(mvport, xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer);
}
@@ -1555,7 +1473,6 @@ mvsata_bio_done(struct ata_channel *chp,
ata_bio->bcount = xfer->c_bcount;
/* mark controller inactive and free xfer */
- mvsata_quetag_put(mvport, xfer->c_slot);
ata_deactivate_xfer(chp, xfer);
ata_bio->flags |= ATA_ITSDONE;
@@ -1741,8 +1658,6 @@ mvsata_wdc_cmd_start(struct ata_channel
ata_channel_lock_owned(chp);
- mvsata_quetag_get(mvport, xfer->c_slot);
-
/* First, EDMA disable, if enabled this channel. */
KASSERT((chp->ch_flags & ATACH_NCQ) == 0);
if (mvport->port_edmamode_curr != nodma)
@@ -1951,10 +1866,8 @@ mvsata_wdc_cmd_kill_xfer(struct ata_chan
mvsata_wdc_cmd_done_end(chp, xfer);
- if (deactivate) {
- mvsata_quetag_put(mvport, xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
}
static void
@@ -2014,8 +1927,6 @@ mvsata_wdc_cmd_done(struct ata_channel *
}
}
- mvsata_quetag_put(mvport, xfer->c_slot);
-
if (ata_c->flags & AT_POLL) {
/* enable interrupts */
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
@@ -2127,8 +2038,6 @@ mvsata_atapi_start(struct ata_channel *c
ata_channel_lock_owned(chp);
- mvsata_quetag_get(mvport, xfer->c_slot);
-
KASSERT((chp->ch_flags & ATACH_NCQ) == 0);
if (mvport->port_edmamode_curr != nodma)
mvsata_edma_disable(mvport, 10 /* ms */, wait_flags);
@@ -2566,10 +2475,8 @@ mvsata_atapi_kill_xfer(struct ata_channe
panic("mvsata_atapi_kill_xfer");
}
- if (deactivate) {
- mvsata_quetag_put(mvport, xfer->c_slot);
+ if (deactivate)
ata_deactivate_xfer(chp, xfer);
- }
ata_free_xfer(chp, xfer);
scsipi_done(sc_xfer);
@@ -2696,7 +2603,6 @@ mvsata_atapi_phase_complete(struct ata_x
static void
mvsata_atapi_done(struct ata_channel *chp, struct ata_xfer *xfer)
{
- struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct scsipi_xfer *sc_xfer = xfer->c_scsipi;
bool iserror = (sc_xfer->error != XS_NOERROR);
@@ -2709,7 +2615,6 @@ mvsata_atapi_done(struct ata_channel *ch
return;
/* mark controller inactive and free the command */
- mvsata_quetag_put(mvport, xfer->c_slot);
ata_deactivate_xfer(chp, xfer);
ata_free_xfer(chp, xfer);
@@ -2887,6 +2792,8 @@ mvsata_edma_handle(struct mvsata_port *m
erpqop * sizeof(struct crpb),
n * sizeof(struct crpb), BUS_DMASYNC_POSTREAD);
+ uint32_t aslots = ata_queue_active(chp);
+
prev_erpqop = erpqop;
while (erpqop != erpqip) {
#ifdef MVSATA_DEBUG
@@ -2898,7 +2805,7 @@ mvsata_edma_handle(struct mvsata_port *m
quetag = CRPB_CHOSTQUETAG(le16toh(crpb->id));
- if ((mvport->port_quetagidx & __BIT(quetag)) == 0) {
+ if ((aslots & __BIT(quetag)) == 0) {
/* not actually executing */
continue;
}
@@ -2987,10 +2894,12 @@ mvsata_edma_rqq_remove(struct mvsata_por
bus_dmamap_sync(mvport->port_dmat, mvport->port_crqb_dmamap, 0,
sizeof(union mvsata_crqb) * MVSATA_EDMAQ_LEN, BUS_DMASYNC_PREWRITE);
+ uint32_t aslots = ata_queue_active(chp);
+
for (i = 0, erqqip = 0; i < MVSATA_EDMAQ_LEN; i++) {
struct ata_xfer *rqxfer;
- if ((mvport->port_quetagidx & __BIT(i)) == 0)
+ if ((aslots & __BIT(i)) == 0)
continue;
if (i == xfer->c_slot) {
@@ -3294,30 +3203,6 @@ mvsata_wdc_reg_init(struct mvsata_port *
#ifndef MVSATA_WITHOUTDMA
-/*
- * There are functions to remember Host Queue Tag.
- */
-
-static inline void
-mvsata_quetag_get(struct mvsata_port *mvport, uint8_t quetag)
-{
- KASSERT(quetag <= 32);
-
- /*
- * Do not check whether it's already set, can happen when
- * postponing bio or atapi xfer to thread.
- */
- mvport->port_quetagidx |= __BIT(quetag);
-}
-
-static inline void
-mvsata_quetag_put(struct mvsata_port *mvport, uint8_t quetag)
-{
- KASSERT(quetag <= 32);
- KASSERT((mvport->port_quetagidx & __BIT(quetag)) != 0);
- mvport->port_quetagidx &= ~__BIT(quetag);
-}
-
static void *
mvsata_edma_resource_prepare(struct mvsata_port *mvport, bus_dma_tag_t dmat,
bus_dmamap_t *dmamap, size_t size, int write)
Index: src/sys/dev/ic/mvsatavar.h
diff -u src/sys/dev/ic/mvsatavar.h:1.4 src/sys/dev/ic/mvsatavar.h:1.4.2.1
--- src/sys/dev/ic/mvsatavar.h:1.4 Fri Aug 31 18:43:29 2018
+++ src/sys/dev/ic/mvsatavar.h Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: mvsatavar.h,v 1.4 2018/08/31 18:43:29 jdolecek Exp $ */
+/* $NetBSD: mvsatavar.h,v 1.4.2.1 2018/10/11 20:57:51 jdolecek Exp $ */
/*
* Copyright (c) 2008 KIYOHARA Takashi
* All rights reserved.
@@ -73,8 +73,6 @@ struct mvsata_port {
enum mvsata_edmamode port_edmamode_negotiated;
enum mvsata_edmamode port_edmamode_curr;
- volatile uint32_t port_quetagidx; /* Host Queue Tag valid */
-
int port_prev_erqqop; /* previous Req Queue Out-Pointer */
bus_dma_tag_t port_dmat;
union mvsata_crqb *port_crqb; /* EDMA Command Request Block */
@@ -96,10 +94,6 @@ struct mvsata_port {
bus_space_handle_t port_sata_sstatus; /* SATA Interface status reg */
struct _fix_phy_param _fix_phy_param;
-
- /* Recovery context */
- uint32_t port_hold_slots;
- bool port_recovering;
};
struct mvsata_hc {
Index: src/sys/dev/ic/siisata.c
diff -u src/sys/dev/ic/siisata.c:1.35.6.8 src/sys/dev/ic/siisata.c:1.35.6.9
--- src/sys/dev/ic/siisata.c:1.35.6.8 Sun Oct 7 15:42:47 2018
+++ src/sys/dev/ic/siisata.c Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.35.6.8 2018/10/07 15:42:47 jdolecek Exp $ */
+/* $NetBSD: siisata.c,v 1.35.6.9 2018/10/11 20:57:51 jdolecek Exp $ */
/* from ahcisata_core.c */
@@ -79,7 +79,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.35.6.8 2018/10/07 15:42:47 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.35.6.9 2018/10/11 20:57:51 jdolecek Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -535,7 +535,7 @@ siisata_intr_port(struct siisata_channel
* We don't expect the recovery to trigger error,
* but handle this just in case.
*/
- if (!schp->sch_recovering)
+ if (!ISSET(chp->ch_flags, ATACH_RECOVERING))
recover = true;
else {
aprint_error_dev(sc->sc_atac.atac_dev,
@@ -574,7 +574,7 @@ process:
* can activate another command(s), so must only process
* commands active before we start processing.
*/
- uint32_t aslots = schp->sch_active_slots;
+ uint32_t aslots = ata_queue_active(chp);
for (int slot=0; slot < SIISATA_MAX_SLOTS; slot++) {
if ((aslots & __BIT(slot)) != 0 &&
@@ -591,20 +591,6 @@ process:
}
}
-static void
-siisata_hold(struct siisata_channel *schp)
-{
- schp->sch_hold_slots |= schp->sch_active_slots;
- schp->sch_active_slots = 0;
-}
-
-static void
-siisata_unhold(struct siisata_channel *schp)
-{
- schp->sch_active_slots = schp->sch_hold_slots;
- schp->sch_hold_slots = 0;
-}
-
/* Recover channel after transfer aborted */
void
siisata_channel_recover(struct ata_channel *chp, uint32_t tfd)
@@ -612,14 +598,12 @@ siisata_channel_recover(struct ata_chann
struct siisata_channel *schp = (struct siisata_channel *)chp;
struct siisata_softc *sc =
(struct siisata_softc *)schp->ata_channel.ch_atac;
- struct ata_drive_datas *drvp;
- int drive, error;
- uint8_t eslot, slot, st, err;
- struct ata_xfer *xfer;
-
- KASSERT(!schp->sch_recovering);
+ int drive;
- schp->sch_recovering = true;
+ ata_channel_lock(chp);
+ KASSERT(!ISSET(chp->ch_flags, ATACH_RECOVERING));
+ SET(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
if (chp->ch_ndrives > PMP_PORT_CTL) {
/* Get PM port number for the device in error */
@@ -628,8 +612,6 @@ siisata_channel_recover(struct ata_chann
} else
drive = 0;
- drvp = &chp->ch_drive[drive];
-
/*
* If BSY or DRQ bits are set, must execute COMRESET to return
* device to idle state. Otherwise, commands can be reissued
@@ -637,86 +619,24 @@ siisata_channel_recover(struct ata_chann
* READ LOG EXT for NCQ to unblock device processing if COMRESET
* was not done.
*/
- if ((ATACH_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0)
- goto reset;
-
- KASSERT(drive >= 0);
- siisata_reinit_port(chp, drive);
-
- siisata_hold(schp);
-
- /*
- * When running NCQ commands, READ LOG EXT is necessary to clear the
- * error condition and unblock the device.
- */
- error = ata_read_log_ext_ncq(drvp, AT_POLL, &eslot, &st, &err);
-
- siisata_unhold(schp);
-
- switch (error) {
- case 0:
- /* Error out the particular NCQ xfer, then requeue the others */
- if ((schp->sch_active_slots & (1 << eslot)) != 0) {
- xfer = ata_queue_hwslot_to_xfer(chp, eslot);
- xfer->c_flags |= C_RECOVERED;
- xfer->ops->c_intr(chp, xfer, ATACH_ERR_ST(err, st));
- }
- break;
-
- case EOPNOTSUPP:
- /*
- * Non-NCQ command error, just find the slot and end it with
- * the error.
- */
- for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
- if ((schp->sch_active_slots & (1 << slot)) != 0) {
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (xfer->c_drive != drive)
- continue;
-
- xfer->ops->c_intr(chp, xfer, tfd);
- }
- }
- break;
-
- case EAGAIN:
- /*
- * Failed to get resources to run the recovery command, must
- * reset the drive. This will also kill all still outstanding
- * transfers.
- */
-reset:
+ if ((ATACH_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0) {
ata_channel_lock(chp);
siisata_device_reset(chp);
ata_channel_unlock(chp);
goto out;
- /* NOTREACHED */
-
- default:
- /*
- * The command to get the slot failed. Kill outstanding
- * commands for the same drive only. No need to reset
- * the drive, it's unblocked nevertheless.
- */
- break;
}
- /* Requeue the non-errorred commands */
- for (slot = 0; slot < SIISATA_MAX_SLOTS; slot++) {
- if (((schp->sch_active_slots >> slot) & 1) == 0)
- continue;
+ KASSERT(drive >= 0);
+ siisata_reinit_port(chp, drive);
- xfer = ata_queue_hwslot_to_xfer(chp, slot);
- if (xfer->c_drive != drive)
- continue;
-
- xfer->ops->c_kill_xfer(chp, xfer,
- (error == 0) ? KILL_REQUEUE : KILL_RESET);
- }
+ ata_recovery_resume(chp, drive, tfd, AT_POLL);
out:
/* Drive unblocked, back to normal operation */
- schp->sch_recovering = false;
+ ata_channel_lock(chp);
+ CLR(chp->ch_flags, ATACH_RECOVERING);
+ ata_channel_unlock(chp);
+
atastart(chp);
}
@@ -1493,12 +1413,7 @@ siisata_activate_prb(struct siisata_chan
sc = (struct siisata_softc *)schp->ata_channel.ch_atac;
- KASSERTMSG((schp->sch_active_slots & __BIT(slot)) == 0,
- "%s: trying to activate active slot %d", SIISATANAME(sc), slot);
-
SIISATA_PRB_SYNC(sc, schp, slot, BUS_DMASYNC_PREWRITE);
- /* keep track of what's going on */
- schp->sch_active_slots |= __BIT(slot);
offset = PRO_CARX(schp->ata_channel.ch_channel, slot);
@@ -1515,11 +1430,6 @@ siisata_deactivate_prb(struct siisata_ch
sc = (struct siisata_softc *)schp->ata_channel.ch_atac;
- KASSERTMSG((schp->sch_active_slots & __BIT(slot)) != 0,
- "%s: trying to deactivate inactive slot %d", SIISATANAME(sc),
- slot);
-
- schp->sch_active_slots &= ~__BIT(slot); /* mark free */
SIISATA_PRB_SYNC(sc, schp, slot, BUS_DMASYNC_POSTWRITE);
}
Index: src/sys/dev/ic/siisatavar.h
diff -u src/sys/dev/ic/siisatavar.h:1.7 src/sys/dev/ic/siisatavar.h:1.7.6.1
--- src/sys/dev/ic/siisatavar.h:1.7 Sat Oct 7 16:05:32 2017
+++ src/sys/dev/ic/siisatavar.h Thu Oct 11 20:57:51 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: siisatavar.h,v 1.7 2017/10/07 16:05:32 jdolecek Exp $ */
+/* $NetBSD: siisatavar.h,v 1.7.6.1 2018/10/11 20:57:51 jdolecek Exp $ */
/* from ahcisatavar.h */
@@ -99,10 +99,6 @@ struct siisata_softc {
bus_addr_t sch_bus_prb[SIISATA_MAX_SLOTS];
bus_dmamap_t sch_datad[SIISATA_MAX_SLOTS];
-
- volatile uint32_t sch_active_slots;
- uint32_t sch_hold_slots;
- bool sch_recovering;
} sc_channels[SIISATA_MAX_PORTS];
};
Added files:
Index: src/sys/dev/ata/ata_recovery.c
diff -u /dev/null src/sys/dev/ata/ata_recovery.c:1.1.2.1
--- /dev/null Thu Oct 11 20:57:51 2018
+++ src/sys/dev/ata/ata_recovery.c Thu Oct 11 20:57:51 2018
@@ -0,0 +1,247 @@
+/* $NetBSD: ata_recovery.c,v 1.1.2.1 2018/10/11 20:57:51 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ata_recovery.c,v 1.1.2.1 2018/10/11 20:57:51 jdolecek Exp $");
+
+#include "opt_ata.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/kthread.h>
+#include <sys/errno.h>
+#include <sys/ataio.h>
+#include <sys/kmem.h>
+#include <sys/intr.h>
+#include <sys/bus.h>
+#include <sys/bitops.h>
+
+#include <dev/ata/ataconf.h>
+#include <dev/ata/atareg.h>
+#include <dev/ata/atavar.h>
+
+#define DEBUG_FUNCS 0x08
+#define DEBUG_PROBE 0x10
+#define DEBUG_DETACH 0x20
+#define DEBUG_XFERS 0x40
+#ifdef ATADEBUG
+extern int atadebug_mask;
+#define ATADEBUG_PRINT(args, level) \
+ if (atadebug_mask & (level)) \
+ printf args
+#else
+#define ATADEBUG_PRINT(args, level)
+#endif
+
+int
+ata_read_log_ext_ncq(struct ata_drive_datas *drvp, uint8_t flags,
+ uint8_t *slot, uint8_t *status, uint8_t *err)
+{
+ struct ata_xfer *xfer = &drvp->recovery_xfer;
+ int rv;
+ struct ata_channel *chp = drvp->chnl_softc;
+ struct atac_softc *atac = chp->ch_atac;
+ uint8_t *tb, cksum, page;
+
+ ATADEBUG_PRINT(("%s\n", __func__), DEBUG_FUNCS);
+
+ /* Only NCQ ATA drives support/need this */
+ if (drvp->drive_type != ATA_DRIVET_ATA ||
+ (drvp->drive_flags & ATA_DRIVE_NCQ) == 0)
+ return EOPNOTSUPP;
+
+ memset(xfer, 0, sizeof(*xfer));
+
+ tb = drvp->recovery_blk;
+ memset(tb, 0, sizeof(drvp->recovery_blk));
+
+ /*
+ * We could use READ LOG DMA EXT if drive supports it (i.e.
+ * when it supports Streaming feature) to avoid PIO command,
+ * and to make this a little faster. Realistically, it
+ * should not matter.
+ */
+ xfer->c_flags |= C_SKIP_QUEUE;
+ xfer->c_ata_c.r_command = WDCC_READ_LOG_EXT;
+ xfer->c_ata_c.r_lba = page = WDCC_LOG_PAGE_NCQ;
+ xfer->c_ata_c.r_st_bmask = WDCS_DRDY;
+ xfer->c_ata_c.r_st_pmask = WDCS_DRDY;
+ xfer->c_ata_c.r_count = 1;
+ xfer->c_ata_c.r_device = WDSD_LBA;
+ xfer->c_ata_c.flags = AT_READ | AT_LBA | AT_LBA48 | flags;
+ xfer->c_ata_c.timeout = 1000; /* 1s */
+ xfer->c_ata_c.data = tb;
+ xfer->c_ata_c.bcount = sizeof(drvp->recovery_blk);
+
+ if ((*atac->atac_bustype_ata->ata_exec_command)(drvp,
+ xfer) != ATACMD_COMPLETE) {
+ rv = EAGAIN;
+ goto out;
+ }
+ if (xfer->c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
+ rv = EINVAL;
+ goto out;
+ }
+
+ cksum = 0;
+ for (int i = 0; i < sizeof(drvp->recovery_blk); i++)
+ cksum += tb[i];
+ if (cksum != 0) {
+ device_printf(drvp->drv_softc,
+ "invalid checksum %x for READ LOG EXT page %x\n",
+ cksum, page);
+ rv = EINVAL;
+ goto out;
+ }
+
+ if (tb[0] & WDCC_LOG_NQ) {
+ /* not queued command */
+ rv = EOPNOTSUPP;
+ goto out;
+ }
+
+ *slot = tb[0] & 0x1f;
+ *status = tb[2];
+ *err = tb[3];
+
+ if ((*status & WDCS_ERR) == 0) {
+ /*
+ * We expect error here. Normal physical drives always
+ * do, it's part of ATA standard. However, QEMU AHCI emulation
+ * mishandles READ LOG EXT in a way that the command itself
+ * returns without error, but no data is transferred.
+ */
+ device_printf(drvp->drv_softc,
+ "READ LOG EXT page %x failed to report error: "
+ "slot %d err %x status %x\n",
+ page, *slot, *err, *status);
+ rv = EOPNOTSUPP;
+ goto out;
+ }
+
+ rv = 0;
+
+out:
+ return rv;
+}
+
+/*
+ * Must be called without channel lock, and with interrupts blocked.
+ */
+void
+ata_recovery_resume(struct ata_channel *chp, int drive, int tfd, int flags)
+{
+ struct ata_drive_datas *drvp;
+ uint8_t slot, eslot, st, err;
+ int error;
+ struct ata_xfer *xfer;
+ const uint8_t ch_openings = ata_queue_openings(chp);
+
+ ata_channel_lock(chp);
+ ata_queue_hold(chp);
+ ata_channel_unlock(chp);
+
+ KASSERT(drive < chp->ch_ndrives);
+ drvp = &chp->ch_drive[drive];
+
+ /*
+ * When running NCQ commands, READ LOG EXT is necessary to clear the
+ * error condition and unblock the device.
+ */
+ error = ata_read_log_ext_ncq(drvp, flags, &eslot, &st, &err);
+
+ ata_channel_lock(chp);
+ ata_queue_unhold(chp);
+ ata_channel_unlock(chp);
+
+ switch (error) {
+ case 0:
+ /* Error out the particular NCQ xfer, then requeue the others */
+ if ((ata_queue_active(chp) & (1U << eslot)) != 0) {
+ xfer = ata_queue_hwslot_to_xfer(chp, eslot);
+ xfer->c_flags |= C_RECOVERED;
+ xfer->ops->c_intr(chp, xfer, ATACH_ERR_ST(err, st));
+ }
+ break;
+
+ case EOPNOTSUPP:
+ /*
+ * Non-NCQ command error, just find the slot and end with
+ * the error.
+ */
+ for (slot = 0; slot < ch_openings; slot++) {
+ if ((ata_queue_active(chp) & (1U << slot)) != 0) {
+ xfer = ata_queue_hwslot_to_xfer(chp, slot);
+ xfer->ops->c_intr(chp, xfer, tfd);
+ }
+ }
+ break;
+
+ case EAGAIN:
+ /*
+ * Failed to get resources to run the recovery command, must
+ * reset the drive. This will also kill all still outstanding
+ * transfers.
+ */
+ ata_channel_lock(chp);
+ ata_thread_run(chp, ATACH_TH_RESET, ATACH_NODRIVE, flags);
+ ata_channel_unlock(chp);
+ goto out;
+ /* NOTREACHED */
+
+ default:
+ /*
+ * The command to get the slot failed. Kill outstanding
+ * commands for the same drive only. No need to reset
+ * the drive, it's unblocked nevertheless.
+ */
+ break;
+ }
+
+ /* Requeue all unfinished commands for same drive as failed command */
+ for (slot = 0; slot < ch_openings; slot++) {
+ if ((ata_queue_active(chp) & (1U << slot)) == 0)
+ continue;
+
+ xfer = ata_queue_hwslot_to_xfer(chp, slot);
+ if (drive != xfer->c_drive)
+ continue;
+
+ xfer->ops->c_kill_xfer(chp, xfer,
+ (error == 0) ? KILL_REQUEUE : KILL_RESET);
+ }
+
+out:
+ /* Nothing more to do */
+ return;
+}