summaryrefslogtreecommitdiff
path: root/sys/dev/pci/ahci.c
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-02 05:14:53 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-02 05:14:53 +0000
commit7c69c2d5259e7705d49d27fce9648fc3ad3bad24 (patch)
tree417f140429c7a6a3ad7e4d06db99704a461e60ed /sys/dev/pci/ahci.c
parent7fdc8bc3ca14cf5ad3f72b356813aaf44417f458 (diff)
Read log page 10h to determine the NCQ error, instead of aborting all
active commands when an error is encountered. ok dlg@
Diffstat (limited to 'sys/dev/pci/ahci.c')
-rw-r--r--sys/dev/pci/ahci.c106
1 files changed, 103 insertions, 3 deletions
diff --git a/sys/dev/pci/ahci.c b/sys/dev/pci/ahci.c
index 39be0f1cc8d..c30108c1f24 100644
--- a/sys/dev/pci/ahci.c
+++ b/sys/dev/pci/ahci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ahci.c,v 1.108 2007/03/31 10:14:53 jasper Exp $ */
+/* $OpenBSD: ahci.c,v 1.109 2007/04/02 05:14:52 pascoe Exp $ */
/*
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
@@ -357,6 +357,8 @@ struct ahci_port {
u_int32_t ap_err_saved_active;
u_int32_t ap_err_saved_active_cnt;
+ u_int8_t ap_err_scratch[512];
+
#ifdef AHCI_DEBUG
char ap_name[16];
#define PORTNAME(_ap) ((_ap)->ap_name)
@@ -469,6 +471,8 @@ void ahci_put_ccb(struct ahci_ccb *);
struct ahci_ccb *ahci_get_err_ccb(struct ahci_port *);
void ahci_put_err_ccb(struct ahci_ccb *);
+int ahci_port_read_ncq_error(struct ahci_port *, int *);
+
struct ahci_dmamem *ahci_dmamem_alloc(struct ahci_softc *, size_t);
void ahci_dmamem_free(struct ahci_softc *,
struct ahci_dmamem *);
@@ -1632,8 +1636,14 @@ ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
/* Had to reset device, can't gather extended info. */
} else if (ap->ap_sactive) {
/* Recover the NCQ error from log page 10h. */
+ ahci_port_read_ncq_error(ap, &err_slot);
+ if (err_slot < 0)
+ goto failall;
+
+ DPRINTF(AHCI_D_VERBOSE, "%s: NCQ errored slot %d\n",
+ PORTNAME(ap), err_slot);
- /* XXX recover NCQ error here */
+ ccb = &ap->ap_ccbs[err_slot];
} else {
/* Didn't reset, could gather extended info from log. */
}
@@ -1649,7 +1659,7 @@ ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
"disabling device.\n", PORTNAME(ap));
goto fatal;
}
- printf("%s: couldn't recover from NCQ error, failing "
+ printf("%s: couldn't recover NCQ error, failing "
"all outstanding commands.\n", PORTNAME(ap));
goto failall;
}
@@ -1880,6 +1890,96 @@ ahci_put_err_ccb(struct ahci_ccb *ccb)
#endif
}
+int
+ahci_port_read_ncq_error(struct ahci_port *ap, int *err_slotp)
+{
+ struct ahci_ccb *ccb;
+ struct ahci_cmd_hdr *cmd_slot;
+ u_int32_t cmd;
+ struct ata_fis_h2d *fis;
+ int rc = EIO;
+
+ DPRINTF(AHCI_D_VERBOSE, "%s: read log page\n", PORTNAME(ap));
+
+ /* Save command register state. */
+ cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
+
+ /* Port should have been idled already. Start it. */
+ KASSERT((cmd & AHCI_PREG_CMD_CR) == 0);
+ ahci_port_start(ap, 0);
+
+ /* Prep error CCB for READ LOG EXT, page 10h, 1 sector. */
+ ccb = ahci_get_err_ccb(ap);
+ ccb->ccb_xa.flags = ATA_F_NOWAIT | ATA_F_READ | ATA_F_POLL;
+ ccb->ccb_xa.data = ap->ap_err_scratch;
+ ccb->ccb_xa.datalen = 512;
+ cmd_slot = ccb->ccb_cmd_hdr;
+ bzero(ccb->ccb_cmd_table, sizeof(struct ahci_cmd_table));
+
+ fis = (struct ata_fis_h2d *)ccb->ccb_cmd_table->cfis;
+ fis->type = ATA_FIS_TYPE_H2D;
+ fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->command = ATA_C_READ_LOG_EXT;
+ fis->lba_low = 0x10; /* queued error log page (10h) */
+ fis->sector_count = 1; /* number of sectors (1) */
+ fis->sector_count_exp = 0;
+ fis->lba_mid = 0; /* starting offset */
+ fis->lba_mid_exp = 0;
+ fis->device = 0;
+
+ cmd_slot->flags = htole16(5); /* FIS length: 5 DWORDS */
+
+ if (ahci_load_prdt(ccb) != 0) {
+ rc = ENOMEM; /* XXX caller must abort all commands */
+ goto err;
+ }
+
+ ccb->ccb_xa.state = ATA_S_PENDING;
+ if (ahci_poll(ccb, 1000, NULL) != 0)
+ goto err;
+
+ rc = 0;
+err:
+ /* Abort our command, if it failed, by stopping command DMA. */
+ if (rc != 0 && ISSET(ap->ap_active, 1 << ccb->ccb_slot)) {
+ printf("%s: log page read failed, slot %d was still active.\n",
+ PORTNAME(ap), ccb->ccb_slot);
+ ahci_port_stop(ap, 0);
+ }
+
+ /* Done with the error CCB now. */
+ ahci_put_err_ccb(ccb);
+
+ /* Extract failed register set and tags from the scratch space. */
+ if (rc == 0) {
+ struct ata_log_page_10h *log;
+ int err_slot;
+
+ log = (struct ata_log_page_10h *)ap->ap_err_scratch;
+ if (ISSET(log->err_regs.type, ATA_LOG_10H_TYPE_NOTQUEUED)) {
+ /* Not queued bit was set - wasn't an NCQ error? */
+ printf("%s: read NCQ error page, but not an NCQ "
+ "error?\n", PORTNAME(ap));
+ rc = ESRCH;
+ } else {
+ /* Copy back the log record as a D2H register FIS. */
+ *err_slotp = err_slot = log->err_regs.type &
+ ATA_LOG_10H_TYPE_TAG_MASK;
+
+ ccb = &ap->ap_ccbs[err_slot];
+ memcpy(&ccb->ccb_xa.rfis, &log->err_regs,
+ sizeof(struct ata_fis_d2h));
+ ccb->ccb_xa.rfis.type = ATA_FIS_TYPE_D2H;
+ ccb->ccb_xa.rfis.flags = 0;
+ }
+ }
+
+ /* Restore saved CMD register state */
+ ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
+
+ return (rc);
+}
+
struct ahci_dmamem *
ahci_dmamem_alloc(struct ahci_softc *sc, size_t size)
{