summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authornate <nate@FreeBSD.org>1993-11-22 23:25:46 +0000
committernate <nate@FreeBSD.org>1993-11-22 23:25:46 +0000
commit73aab54861562732849f54dfc74fa04f33ea570f (patch)
treeabe8b49f698e227aca8efdd9942cb8d6efffdee3 /sys
parentf730dcb859b04033394ea8bb7d1779924096679a (diff)
downloadFreeBSD-src-73aab54861562732849f54dfc74fa04f33ea570f.zip
FreeBSD-src-73aab54861562732849f54dfc74fa04f33ea570f.tar.gz
Stop gap measure until we can get Bruce's driver debugged.
--- From: sos@login.dkuug.dk (S|ren Schmidt) Subject: IDE-disk hangs - solution/patches NetBSD/FreeBSD Summary: fixes for lost interrupts with IDE disks Keywords: hanging-disk, IDE-disk, lost-interrupt Due to "popular" demand I'm posting these patches to NetBSD/FreeBSD instead of mailing them around the world :-) As many have found out there is a problem when using IDE disks on FreeBSD. Following is a patch that fixes the problem with lost intterrupts. Both fixes is based on a patch posted here some month ago by Stefan Behrens?? (sorry I've lost the original article). But anyway it works (for me :-). Basically it does a timeout on lost interrupts, starting the operation again and logging and error message on the console. It additionally makes the allready present while loop timeouts independent of CPU speed, and adds minor numbers for easy access to dos partitions.
Diffstat (limited to 'sys')
-rw-r--r--sys/i386/isa/wd.c171
1 files changed, 101 insertions, 70 deletions
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
index 1787d32..c05c0f1 100644
--- a/sys/i386/isa/wd.c
+++ b/sys/i386/isa/wd.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
- * $Id: wd.c,v 1.13 1993/11/18 05:02:22 rgrimes Exp $
+ * $Id: wd.c,v 1.14 1993/11/19 06:30:00 davidg Exp $
*/
/* TODO:peel out buffer at low ipl, speed improvement */
@@ -62,10 +62,10 @@
#include "syslog.h"
#include "vm/vm.h"
-#define _NWD (NWD - 1) /* One is for the controller XXX 31 Jul 92*/
+#define _NWD (NWD - 1) /* One is for the controller XXX */
#ifndef WDCTIMEOUT
-#define WDCTIMEOUT 10000000 /* arbitrary timeout for drive ready waits */
+#define WDCTIMEOUT 300000 /* arbitrary timeout for drive ready waits */
#endif
#define RETRIES 5 /* number of retries before giving up */
@@ -126,6 +126,7 @@ struct buf wdtab;
struct buf wdutab[_NWD]; /* head of queue per drive */
struct buf rwdbuf[_NWD]; /* buffers for raw IO */
long wdxfer[_NWD]; /* count of transfers */
+int wdtimeout_status[_NWD]; /* timeout status */
#ifdef WDDEBUG
int wddebug;
#endif
@@ -140,6 +141,8 @@ static int wdcommand(struct disk *, int);
static int wdcontrol(struct buf *);
static int wdsetctlr(dev_t, struct disk *);
static int wdgetctlr(int, struct disk *);
+static int wdtimeout(int);
+static int wdreset(int);
/*
* Probe for controller.
@@ -151,13 +154,13 @@ wdprobe(struct isa_device *dvp)
struct disk *du;
int wdc;
- if (unit >= _NWD) /* 31 Jul 92*/
+ if (unit >= _NWD)
return(0);
if ((du = wddrives[unit]) == 0) {
du = wddrives[unit] = (struct disk *)
malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
- bzero (du, sizeof(struct disk)); /* 31 Jul 92*/
+ bzero (du, sizeof(struct disk));
du->dk_unit = unit;
}
@@ -206,6 +209,9 @@ wdattach(struct isa_device *dvp)
du->dk_unit = unit;
du->dk_port = dvp->id_iobase;
}
+ /* initialize timeout */
+ wdtimeout_status[unit] = 0;
+ wdtimeout(unit);
/* print out description of drive, suppressing multiple blanks*/
if(wdgetctlr(unit, du) == 0) {
@@ -265,7 +271,9 @@ wdstrategy(register struct buf *bp)
}
/* have partitions and want to use them? */
- if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
+ if ((du->dk_flags & DKFL_BSDLABEL) != 0
+ && wdpart(bp->b_dev) != WDRAW
+ && wddospart(bp->b_dev) == 0) {
/*
* do bounds checking, adjust transfer. if error, process.
@@ -338,6 +346,7 @@ wdstart()
register struct disk *du; /* disk unit for IO */
register struct buf *bp;
struct disklabel *lp;
+ struct dos_partition *dosp;
struct buf *dp;
register struct bt_bad *bt_ptr;
long blknum, pagcnt, cylin, head, sector;
@@ -384,9 +393,14 @@ loop:
du->dk_bc = bp->b_bcount;
lp = &du->dk_dd;
+ dosp = du->dk_dospartitions;
+ if (wddospart(bp->b_dev))
+ blknum += dosp[wdpart(bp->b_dev)].dp_start;
secpertrk = lp->d_nsectors;
secpercyl = lp->d_secpercyl;
- if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
+ if ((du->dk_flags & DKFL_BSDLABEL) != 0
+ && wdpart(bp->b_dev) != WDRAW
+ && wddospart(bp->b_dev) == 0)
blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
cylin = blknum / secpercyl;
head = (blknum % secpercyl) / secpertrk;
@@ -445,23 +459,12 @@ RETRY:
du->dk_bc += DEV_BSIZE;
/* controller idle? */
- timeout = 0;
- /* must delay 5us to conform to ATA spec */
- DELAY(5);
- while (inb(wdc+wd_status) & WDCS_BUSY)
- {
- if (++timeout > WDCTIMEOUT)
- {
- printf("wd.c: Controller busy too long!\n");
- /* reset the device */
- outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
- DELAY(1000);
- outb(wdc+wd_ctlr, WDCTL_IDS);
- DELAY(1000);
- (void) inb(wdc+wd_error); /* XXX! */
- outb(wdc+wd_ctlr, WDCTL_4BIT);
- break;
- }
+ timeout = WDCTIMEOUT;
+ while ((inb(wdc+wd_status) & WDCS_BUSY) && timeout--)
+ DELAY(10);
+ if (!timeout) {
+ printf("wd: busy timeout\n");
+ wdreset(wdc);
}
/* stuff the task file */
@@ -489,21 +492,13 @@ RETRY:
outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
/* wait for drive to become ready */
- timeout = 0;
- while ((inb(wdc+wd_status) & WDCS_READY) == 0)
- {
- if (++timeout > WDCTIMEOUT)
- {
- printf("wd.c: Drive busy too long!\n");
- /* reset the device */
- outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
- DELAY(1000);
- outb(wdc+wd_ctlr, WDCTL_IDS);
- DELAY(1000);
- (void) inb(wdc+wd_error); /* XXX! */
- outb(wdc+wd_ctlr, WDCTL_4BIT);
- goto RETRY;
- }
+ timeout = WDCTIMEOUT;
+ while ((inb(wdc+wd_status) & WDCS_READY) == 0 && timeout--)
+ DELAY(10);
+ if (!timeout) {
+ printf("wd: ready timeout\n");
+ wdreset(wdc);
+ goto RETRY;
}
/* initiate command! */
@@ -521,30 +516,26 @@ RETRY:
}
/* if this is a read operation, just go away until it's done. */
- if (bp->b_flags & B_READ) return;
+ if (bp->b_flags & B_READ) {
+ wdtimeout_status[unit] = 2;
+ return;
+ }
/* ready to send data? */
- timeout = 0;
- while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
- {
- if (++timeout > WDCTIMEOUT)
- {
- printf("wd.c: Drive not ready for too long!\n");
- /* reset the device */
- outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
- DELAY(1000);
- outb(wdc+wd_ctlr, WDCTL_IDS);
- DELAY(1000);
- (void) inb(wdc+wd_error); /* XXX! */
- outb(wdc+wd_ctlr, WDCTL_4BIT);
- goto RETRY;
- }
+ timeout = WDCTIMEOUT;
+ while ((inb(wdc+wd_status) & WDCS_DRQ) == 0 && timeout--)
+ DELAY(10);
+ if (!timeout) {
+ printf("wd: drq timeout\n");
+ wdreset(wdc);
+ goto RETRY;
}
/* then send it! */
outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
DEV_BSIZE/sizeof(short));
du->dk_bc -= DEV_BSIZE;
+ wdtimeout_status[unit] = 2;
}
/* Interrupt routine for the controller. Acknowledge the interrupt, check for
@@ -571,6 +562,7 @@ wdintr(struct intrframe wdif)
bp = dp->b_actf;
du = wddrives[wdunit(bp->b_dev)];
wdc = du->dk_port;
+ wdtimeout_status[wdunit(bp->b_dev)] = 0;
#ifdef WDDEBUG
printf("I ");
@@ -763,7 +755,8 @@ done:
* that overlaps another partition which is open
* unless one is the "raw" partition (whole disk).
*/
- if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
+ if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/
+ && part != WDRAW && wddospart(dev) == 0) {
int start, end;
pp = &du->dk_dd.d_partitions[part];
@@ -787,7 +780,8 @@ done:
pp - du->dk_dd.d_partitions + 'a');
}
}
- if (part >= du->dk_dd.d_npartitions && part != WDRAW)
+ if (part >= du->dk_dd.d_npartitions
+ && part != WDRAW && wddospart(dev) == 0)
return (ENXIO);
/* insure only one open at a time */
@@ -840,7 +834,7 @@ wdcontrol(register struct buf *bp)
/* must delay 5us to conform to ATA spec */
DELAY(5);
/* wait for drive and controller to become ready */
- for (i = WDCTIMEOUT; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY))
+ for (i=WDCTIMEOUT; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY))
!= WDCS_READY && i-- != 0; )
;
outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
@@ -891,15 +885,17 @@ badopen:
* assumes interrupts are blocked.
*/
static int
-wdcommand(struct disk *du, int cmd) {
- int timeout = WDCTIMEOUT, stat, wdc;
+wdcommand(struct disk *du, int cmd)
+{
+ int timeout, stat, wdc;
/* controller ready for command? */
wdc = du->dk_port;
/* must delay 5us to conform to ATA spec */
DELAY(5);
- while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
- timeout--;
+ timeout = WDCTIMEOUT;
+ while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout--)
+ DELAY(10);
if (timeout <= 0)
return(-1);
@@ -907,20 +903,22 @@ wdcommand(struct disk *du, int cmd) {
outb(wdc+wd_command, cmd);
/* must delay 5us to conform to ATA spec */
DELAY(5);
- while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
- timeout--;
+ timeout = WDCTIMEOUT;
+ while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout--)
+ DELAY(10);
if (timeout <= 0)
return(-1);
if (cmd != WDCC_READP)
return (stat);
/* is controller ready to return data? */
- while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
- timeout > 0)
- timeout--;
+ timeout = WDCTIMEOUT;
+ while (((stat=inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0
+ && timeout--)
+ DELAY(10);
if (timeout <= 0)
return(-1);
-
+
return (stat);
}
@@ -1178,7 +1176,7 @@ wdsize(dev_t dev)
int unit = wdunit(dev), part = wdpart(dev), val = 0;
struct disk *du;
- if (unit >= _NWD) /* 31 Jul 92*/
+ if (unit >= _NWD)
return(-1);
du = wddrives[unit];
@@ -1190,6 +1188,39 @@ wdsize(dev_t dev)
return((int)du->dk_dd.d_partitions[part].p_size);
}
+static int
+wdreset(int wdc)
+{
+ outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
+ DELAY(1000);
+ outb(wdc+wd_ctlr, WDCTL_IDS);
+ DELAY(1000);
+ (void) inb(wdc+wd_error); /* XXX! */
+ outb(wdc+wd_ctlr, WDCTL_4BIT);
+}
+
+static int
+wdtimeout(int unit)
+{
+ int x = splbio();
+
+ if (wdtimeout_status[unit]) {
+ if (--wdtimeout_status[unit] == 0) {
+ struct disk *du = wddrives[unit];
+ int wdc = du->dk_port;
+
+ printf("wd: interupt timeout\n");
+ wdreset(wdc);
+ du->dk_skip = 0;
+ du->dk_flags |= DKFL_SINGLE;
+ wdstart();
+ }
+ }
+ timeout(wdtimeout, unit, 100);
+ splx(x);
+ return (0);
+}
+
extern char *vmmap; /* poor name! */
int
@@ -1218,7 +1249,7 @@ wddump(dev_t dev) /* dump core after a system crash */
unit = wdunit(dev); /* eventually support floppies? */
part = wdpart(dev); /* file system */
/* check for acceptable drive number */
- if (unit >= _NWD) return(ENXIO); /* 31 Jul 92*/
+ if (unit >= _NWD) return(ENXIO);
du = wddrives[unit];
if (du == 0) return(ENXIO);
OpenPOWER on IntegriCloud