diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2011-04-18 04:16:15 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2011-04-18 04:16:15 +0000 |
commit | 1dd9da86643ecc787c10e8c2dcc955a035743fbd (patch) | |
tree | 621d7bd90084758b4f455ae797cc0dee88b941b2 | |
parent | 8630210480621a9c87eb7f3a8360ba1228c2a4f7 (diff) |
keep an eye out for wdc registers returning 0xff. if they return 0xff,
this probably means the controller is dead -- as in, a hot plug eject
event. in that case, mark the driver dead and try harder to return -1
in the interrupt handler.
tested by many
ok dlg, miod did not object
-rw-r--r-- | sys/dev/ata/ata_wdc.c | 9 | ||||
-rw-r--r-- | sys/dev/ata/wd.c | 13 | ||||
-rw-r--r-- | sys/dev/ic/wdc.c | 66 | ||||
-rw-r--r-- | sys/dev/ic/wdcvar.h | 4 | ||||
-rw-r--r-- | sys/dev/pci/pciide.c | 4 |
5 files changed, 74 insertions, 22 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c index 3840f43ed39..41d9a21fddc 100644 --- a/sys/dev/ata/ata_wdc.c +++ b/sys/dev/ata/ata_wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ata_wdc.c,v 1.37 2011/04/05 19:57:40 deraadt Exp $ */ +/* $OpenBSD: ata_wdc.c,v 1.38 2011/04/18 04:16:13 deraadt Exp $ */ /* $NetBSD: ata_wdc.c,v 1.21 1999/08/09 09:43:11 bouyer Exp $ */ /* @@ -420,6 +420,8 @@ intr: /* Wait for IRQ (either real or polled) */ } return; timeout: + if (chp->ch_status == 0xff) + return; printf("%s:%d:%d: not ready, st=0x%b, err=0x%02x\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, chp->ch_status, WDCS_BITS, chp->ch_error); @@ -756,6 +758,11 @@ wdc_ata_err(struct ata_drive_datas *drvp, struct ata_bio *ata_bio) { struct channel_softc *chp = drvp->chnl_softc; ata_bio->error = 0; + + if (chp->ch_status == 0xff) { + ata_bio->error = ERR_NODEV; + return WDC_ATA_ERR; + } if (chp->ch_status & WDCS_BSY) { ata_bio->error = TIMEOUT; return WDC_ATA_ERR; diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c index eedfac0c256..bf473917e88 100644 --- a/sys/dev/ata/wd.c +++ b/sys/dev/ata/wd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wd.c,v 1.99 2011/04/15 20:53:28 miod Exp $ */ +/* $OpenBSD: wd.c,v 1.100 2011/04/18 04:16:13 deraadt Exp $ */ /* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */ /* @@ -375,6 +375,8 @@ wddetach(struct device *self, int flags) struct buf *bp; int s, bmaj, cmaj, mn; + timeout_del(&sc->sc_restart_timeout); + /* Remove unprocessed buffers from queue */ s = splbio(); while ((bp = bufq_dequeue(&sc->sc_bufq)) != NULL) { @@ -612,10 +614,15 @@ wdrestart(void *v) { struct wd_softc *wd = v; struct buf *bp = wd->sc_bp; + struct channel_softc *chnl; int s; WDCDEBUG_PRINT(("wdrestart %s\n", wd->sc_dev.dv_xname), DEBUG_XFERS); + chnl = (struct channel_softc *)(wd->drvp->chnl_softc); + if (chnl->dying) + return; + s = splbio(); disk_unbusy(&wd->sc_dk, 0, (bp->b_flags & B_READ)); __wdstart(v, bp); @@ -642,6 +649,7 @@ int wdopen(dev_t dev, int flag, int fmt, struct proc *p) { struct wd_softc *wd; + struct channel_softc *chnl; int unit, part; int error; @@ -651,6 +659,9 @@ wdopen(dev_t dev, int flag, int fmt, struct proc *p) wd = wdlookup(unit); if (wd == NULL) return ENXIO; + chnl = (struct channel_softc *)(wd->drvp->chnl_softc); + if (chnl->dying) + return (ENXIO); /* * If this is the first open of this device, add a reference diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index a43eb881501..2900586cd81 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc.c,v 1.112 2011/04/15 20:53:28 miod Exp $ */ +/* $OpenBSD: wdc.c,v 1.113 2011/04/18 04:16:13 deraadt Exp $ */ /* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. @@ -702,7 +702,7 @@ wdcattach(struct channel_softc *chp) { int channel_flags, ctrl_flags, i; struct ata_atapi_attach aa_link; - static int inited = 0; + static int inited = 0, s; #ifdef WDCDEBUG int savedmask = wdcdebug_mask; #endif @@ -743,6 +743,7 @@ wdcattach(struct channel_softc *chp) #endif /* WDCDEBUG */ /* initialise global data */ + s = splbio(); if (inited == 0) { /* Initialize the wdc_xfer pool. */ pool_init(&wdc_xfer_pool, sizeof(struct wdc_xfer), 0, @@ -750,6 +751,7 @@ wdcattach(struct channel_softc *chp) inited++; } TAILQ_INIT(&chp->ch_queue->sc_xfer); + splx(s); for (i = 0; i < 2; i++) { struct ata_drive_datas *drvp = &chp->ch_drive[i]; @@ -879,7 +881,10 @@ wdcdetach(struct channel_softc *chp, int flags) int s, rv; s = splbio(); + chp->dying = 1; + wdc_kill_pending(chp); + timeout_del(&chp->ch_timo); rv = config_detach_children((struct device *)chp->wdc, flags); splx(s); @@ -898,19 +903,21 @@ wdcintr(void *arg) { struct channel_softc *chp = arg; struct wdc_xfer *xfer; - int ret; + u_int8_t st = 0; + int ret = 0; - if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) { - /* Acknowledge interrupt by reading status */ - if (chp->_vtbl == 0) { - bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, - wdr_status & _WDC_REGMASK); - } else { - CHP_READ_REG(chp, wdr_status); - } + /* Acknowledge interrupt by reading status */ + if (chp->_vtbl == 0) + st = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + wdr_status & _WDC_REGMASK); + else + st = CHP_READ_REG(chp, wdr_status); + if (st == 0xff) + return (-1); + if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) { WDCDEBUG_PRINT(("wdcintr: inactive controller\n"), DEBUG_INTR); - return 0; + return ret; } WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR); @@ -919,12 +926,15 @@ wdcintr(void *arg) chp->wdc->dma_status = (*chp->wdc->dma_finish)(chp->wdc->dma_arg, chp->channel, xfer->drive, 0); + if (chp->wdc->dma_status == 0xff) + return (-1); if (chp->wdc->dma_status & WDC_DMAST_NOIRQ) { /* IRQ not for us, not detected by DMA engine */ return 0; } chp->ch_flags &= ~WDCF_DMA_WAIT; } + chp->ch_flags &= ~WDCF_IRQ_WAIT; ret = xfer->c_intr(chp, xfer, 1); if (ret == 0) /* irq was not for us, still waiting for irq */ @@ -1061,11 +1071,16 @@ wdc_wait_for_status(struct channel_softc *chp, int mask, int bits, int timeout) chp->ch_status = status = CHP_READ_REG(chp, wdr_status); WDC_LOG_STATUS(chp, chp->ch_status); - if (status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) { - wdc_set_drive(chp, 1); - chp->ch_status = status = - CHP_READ_REG(chp, wdr_status); - WDC_LOG_STATUS(chp, chp->ch_status); + if (status == 0xff) { + if ((chp->ch_flags & WDCF_ONESLAVE)) { + wdc_set_drive(chp, 1); + chp->ch_status = status = + CHP_READ_REG(chp, wdr_status); + WDC_LOG_STATUS(chp, chp->ch_status); + } else { + chp->dying = 1; + return -1; + } } if ((status & WDCS_BSY) == 0 && (status & mask) == bits) break; @@ -1117,6 +1132,10 @@ wdc_dmawait(struct channel_softc *chp, struct wdc_xfer *xfer, int timeout) chp->channel, xfer->drive, 0); if ((chp->wdc->dma_status & WDC_DMAST_NOIRQ) == 0) return 0; + if (chp->wdc->dma_status == 0xff) { + chp->dying = 1; + return -1; + } delay(WDCDELAY); } /* timeout, force a DMA halt */ @@ -1700,6 +1719,10 @@ __wdccommand_intr(struct channel_softc *chp, struct wdc_xfer *xfer, int irq) chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_INTR); if (wdcwait(chp, wdc_c->r_st_pmask, wdc_c->r_st_pmask, (irq == 0) ? wdc_c->timeout : 0)) { + if (chp->dying) { + __wdccommand_done(chp, xfer); + return -1; + } if (irq && (xfer->c_flags & C_TIMEOU) == 0) return 0; /* IRQ was not for us */ wdc_c->flags |= AT_TIMEOU; @@ -1729,6 +1752,8 @@ __wdccommand_done(struct channel_softc *chp, struct wdc_xfer *xfer) WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d %02x\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, chp->ch_status), DEBUG_FUNCS); + if (chp->dying) + goto killit; if (chp->ch_status & WDCS_DWF) wdc_c->flags |= AT_DF; if (chp->ch_status & WDCS_ERR) { @@ -1749,13 +1774,18 @@ __wdccommand_done(struct channel_softc *chp, struct wdc_xfer *xfer) isn't a readable register */ } +killit: if (xfer->c_flags & C_POLL) { wdc_enable_intr(chp); - } + } else + timeout_del(&chp->ch_timo); wdc_free_xfer(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_done before callback\n"), DEBUG_INTR); + if (chp->dying) + return; + if (wdc_c->flags & AT_WAIT) wakeup(wdc_c); else diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h index 7e67e635ec7..80247362390 100644 --- a/sys/dev/ic/wdcvar.h +++ b/sys/dev/ic/wdcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdcvar.h,v 1.48 2011/04/15 20:53:28 miod Exp $ */ +/* $OpenBSD: wdcvar.h,v 1.49 2011/04/18 04:16:14 deraadt Exp $ */ /* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */ /*- @@ -81,6 +81,8 @@ struct channel_softc { /* Per channel data */ */ struct channel_queue *ch_queue; struct timeout ch_timo; + + int dying; }; /* diff --git a/sys/dev/pci/pciide.c b/sys/dev/pci/pciide.c index a53c91d76e3..3f75e73a852 100644 --- a/sys/dev/pci/pciide.c +++ b/sys/dev/pci/pciide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pciide.c,v 1.326 2011/04/14 22:24:34 jsg Exp $ */ +/* $OpenBSD: pciide.c,v 1.327 2011/04/18 04:16:12 deraadt Exp $ */ /* $NetBSD: pciide.c,v 1.127 2001/08/03 01:31:08 tsutsui Exp $ */ /* @@ -2073,6 +2073,8 @@ pciide_dma_finish(void *v, int channel, int drive, int force) status = PCIIDE_DMACTL_READ(sc, channel); WDCDEBUG_PRINT(("pciide_dma_finish: status 0x%x\n", status), DEBUG_XFERS); + if (status == 0xff) + return (status); if (force == 0 && (status & IDEDMA_CTL_INTR) == 0) { error = WDC_DMAST_NOIRQ; |