diff options
| author | bouyer <bouyer@NetBSD.org> | 2004-07-31 21:26:42 +0000 |
|---|---|---|
| committer | bouyer <bouyer@NetBSD.org> | 2004-07-31 21:26:42 +0000 |
| commit | 7ffa35d1a22116956594de8f808c7abf31912a98 (patch) | |
| tree | 285bb74d8dd06591e5f2ad168be1a18315fca8d4 /sys/dev/ata | |
| parent | 1fe630831fca7a6764e893910c062a2930cfb546 (diff) | |
Implement asynchronous channel reset.
Use this to reset the channel before doing a dump, instead of the hack in
wdc_exec_xfer() based on C_POLL. This hack was causing problems on
controllers with a shared queue, because we now can have C_POLL set during
concurent channels probes (problem found and analysed on sparc64 by
Martin Husemann).
This should even make core dumps marginally more reliable on ATA drives.
Diffstat (limited to 'sys/dev/ata')
| -rw-r--r-- | sys/dev/ata/ata_wdc.c | 23 | ||||
| -rw-r--r-- | sys/dev/ata/atavar.h | 18 | ||||
| -rw-r--r-- | sys/dev/ata/wd.c | 9 |
3 files changed, 40 insertions, 10 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c index 4a6bc577bff..02da34e06e5 100644 --- a/sys/dev/ata/ata_wdc.c +++ b/sys/dev/ata/ata_wdc.c @@ -1,4 +1,4 @@ -/* $NetBSD: ata_wdc.c,v 1.57 2004/06/22 19:20:56 mycroft Exp $ */ +/* $NetBSD: ata_wdc.c,v 1.58 2004/07/31 21:26:43 bouyer Exp $ */ /* * Copyright (c) 1998, 2001, 2003 Manuel Bouyer. @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.57 2004/06/22 19:20:56 mycroft Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.58 2004/07/31 21:26:43 bouyer Exp $"); #ifndef WDCDEBUG #define WDCDEBUG @@ -119,7 +119,8 @@ static void wdc_ata_bio_start(struct wdc_channel *,struct ata_xfer *); static void _wdc_ata_bio_start(struct wdc_channel *,struct ata_xfer *); static int wdc_ata_bio_intr(struct wdc_channel *, struct ata_xfer *, int); -static void wdc_ata_bio_kill_xfer(struct wdc_channel *,struct ata_xfer *); +static void wdc_ata_bio_kill_xfer(struct wdc_channel *, + struct ata_xfer *, int); static void wdc_ata_bio_done(struct wdc_channel *, struct ata_xfer *); static int wdc_ata_err(struct ata_drive_datas *, struct ata_bio *); #define WDC_ATA_NOERR 0x00 /* Drive doesn't report an error */ @@ -740,7 +741,8 @@ wdc_ata_kill_pending(struct ata_drive_datas *drvp) } static void -wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer) +wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer, + int reason) { struct ata_bio *ata_bio = xfer->c_cmd; int drive = xfer->c_drive; @@ -750,7 +752,18 @@ wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer) wdc_free_xfer(chp, xfer); ata_bio->flags |= ATA_ITSDONE; - ata_bio->error = ERR_NODEV; + switch (reason) { + case KILL_GONE: + ata_bio->error = ERR_NODEV; + break; + case KILL_RESET: + ata_bio->error = ERR_RESET; + break; + default: + printf("wdc_ata_bio_kill_xfer: unknown reason %d\n", + reason); + panic("wdc_ata_bio_kill_xfer"); + } ata_bio->r_error = WDCE_ABRT; WDCDEBUG_PRINT(("wdc_ata_done: drv_done\n"), DEBUG_XFERS); (*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc); diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h index ac922ca7865..0844268b652 100644 --- a/sys/dev/ata/atavar.h +++ b/sys/dev/ata/atavar.h @@ -1,4 +1,4 @@ -/* $NetBSD: atavar.h,v 1.43 2004/06/01 19:32:30 mycroft Exp $ */ +/* $NetBSD: atavar.h,v 1.44 2004/07/31 21:26:43 bouyer Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. @@ -58,14 +58,19 @@ struct ata_xfer { /* Low-level protocol handlers. */ void (*c_start)(struct wdc_channel *, struct ata_xfer *); int (*c_intr)(struct wdc_channel *, struct ata_xfer *, int); - void (*c_kill_xfer)(struct wdc_channel *, struct ata_xfer *); + void (*c_kill_xfer)(struct wdc_channel *, struct ata_xfer *, int); }; +/* vlags in c_flags */ #define C_ATAPI 0x0001 /* xfer is ATAPI request */ #define C_TIMEOU 0x0002 /* xfer processing timed out */ #define C_POLL 0x0004 /* command is polled */ #define C_DMA 0x0008 /* command uses DMA */ +/* reasons for c_kill_xfer() */ +#define KILL_GONE 1 /* device is gone */ +#define KILL_RESET 2 /* xfer was reset */ + /* Per-channel queue of ata_xfers. May be shared by multiple channels. */ struct ata_queue { TAILQ_HEAD(, ata_xfer) queue_xfer; @@ -192,6 +197,7 @@ struct ata_bio { #define ERR_DMA 3 /* DMA error */ #define TIMEOUT 4 /* device timed out */ #define ERR_NODEV 5 /* device has been gone */ +#define ERR_RESET 6 /* command was terminated by channel reset */ u_int8_t r_error;/* copy of error register */ daddr_t badsect[127];/* 126 plus trailing -1 marker */ }; @@ -232,7 +238,9 @@ struct wdc_command { #define AT_ERROR 0x0080 /* command is done with error */ #define AT_TIMEOU 0x0100 /* command timed out */ #define AT_DF 0x0200 /* Drive fault */ -#define AT_READREG 0x0400 /* Read registers on completion */ +#define AT_RESET 0x0400 /* command terminated by channel reset */ +#define AT_GONE 0x0800 /* command terminated because device is gone */ +#define AT_READREG 0x1000 /* Read registers on completion */ int timeout; /* timeout (in ms) */ void *data; /* Data buffer address */ @@ -249,6 +257,10 @@ struct ata_bustype { int bustype_type; /* symbolic name of type */ int (*ata_bio)(struct ata_drive_datas *, struct ata_bio *); void (*ata_reset_channel)(struct ata_drive_datas *, int); +/* extra flags for ata_reset_channel(), in addition to AT_* */ +#define AT_RST_EMERG 0x10000 /* emergency - e.g. for a dump */ +#define AT_RST_NOCMD 0x20000 /* XXX has to go - temporary until we have tagged queuing */ + int (*ata_exec_command)(struct ata_drive_datas *, struct wdc_command *); diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c index 7198f2ff256..a949e820e69 100644 --- a/sys/dev/ata/wd.c +++ b/sys/dev/ata/wd.c @@ -1,4 +1,4 @@ -/* $NetBSD: wd.c,v 1.280 2004/06/22 19:20:56 mycroft Exp $ */ +/* $NetBSD: wd.c,v 1.281 2004/07/31 21:26:43 bouyer Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.280 2004/06/22 19:20:56 mycroft Exp $"); +__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.281 2004/07/31 21:26:43 bouyer Exp $"); #ifndef WDCDEBUG #define WDCDEBUG @@ -742,6 +742,9 @@ wddone(void *v) case TIMEOUT: errmsg = "device timeout"; goto retry; + case ERR_RESET: + errmsg = "channel reset"; + goto retry2; case ERROR: /* Don't care about media change bits */ if (wd->sc_wdc_bio.r_error != 0 && @@ -751,6 +754,7 @@ wddone(void *v) do_perror = 1; retry: /* Just reset and retry. Can we do more ? */ wd->atabus->ata_reset_channel(wd->drvp, 0); +retry2: diskerr(bp, "wd", errmsg, LOG_PRINTF, wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); if (wd->retries < WDIORETRIES) @@ -1449,6 +1453,7 @@ wddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) if (wddumprecalibrated == 0) { wddumpmulti = wd->sc_multi; wddumprecalibrated = 1; + wd->atabus->ata_reset_channel(wd->drvp, AT_POLL | AT_RST_EMERG); wd->drvp->state = RESET; } |
