Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Automatically build up a list of bad sectors from failed...



details:   https://anonhg.NetBSD.org/src/rev/6bd18b270f8c
branches:  trunk
changeset: 545709:6bd18b270f8c
user:      darrenr <darrenr%NetBSD.org@localhost>
date:      Tue Apr 15 14:11:00 2003 +0000

description:
Automatically build up a list of bad sectors from failed read operations and
prempt read operations on matching regions with a failure rather than waiting
for the device to return a failure.  The I/O operation must have already failed
MAXRETRIES times before being added to the list - this can generally take up
to 12 seconds.
List is made accessible to userspace via DIOCBSLIST and DIOCBSFLUSH.

diffstat:

 sys/dev/ata/wd.c    |  79 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/ata/wdvar.h |   4 ++-
 sys/sys/disk.h      |  12 +++++++-
 sys/sys/dkio.h      |   6 +++-
 4 files changed, 96 insertions(+), 5 deletions(-)

diffs (190 lines):

diff -r c130b9678151 -r 6bd18b270f8c sys/dev/ata/wd.c
--- a/sys/dev/ata/wd.c  Tue Apr 15 13:59:35 2003 +0000
+++ b/sys/dev/ata/wd.c  Tue Apr 15 14:11:00 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: wd.c,v 1.240 2003/04/03 22:18:23 fvdl Exp $ */
+/*     $NetBSD: wd.c,v 1.241 2003/04/15 14:11:00 darrenr 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.240 2003/04/03 22:18:23 fvdl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.241 2003/04/15 14:11:00 darrenr Exp $");
 
 #ifndef WDCDEBUG
 #define WDCDEBUG
@@ -288,6 +288,7 @@
 #else
        bufq_alloc(&wd->sc_q, BUFQ_DISKSORT|BUFQ_SORT_RAWBLOCK);
 #endif
+       SLIST_INIT(&wd->sc_bslist);
 
        wd->atabus = adev->adev_bustype;
        wd->openings = adev->adev_openings;
@@ -423,6 +424,13 @@
        struct buf *bp;
        int s, bmaj, cmaj, i, mn;
 
+       /* Clean out the bad sector list */
+       while (!SLIST_EMPTY(&sc->sc_bslist)) {
+               void *head = SLIST_FIRST(&sc->sc_bslist);
+               SLIST_REMOVE_HEAD(&sc->sc_bslist, dbs_next);
+               free(head, M_TEMP);
+       }
+
        /* locate the major number */
        bmaj = bdevsw_lookup_major(&wd_bdevsw);
        cmaj = cdevsw_lookup_major(&wd_cdevsw);
@@ -525,6 +533,23 @@
 
        bp->b_rawblkno = blkno;
 
+       /*
+        * If the transfer about to be attempted contains only a block that
+        * is known to be bad then return an error for the transfer without
+        * even attempting to start a transfer up under the premis that we
+        * will just end up doing more retries for a transfer that will end
+        * up failing again.
+        * XXX:SMP - mutex required to protect with DIOCBSFLUSH
+        */
+       if (__predict_false(!SLIST_EMPTY(&wd->sc_bslist))) {
+               struct disk_badsectors *dbs;
+               daddr_t maxblk = blkno + bp->b_bcount;
+
+               SLIST_FOREACH(dbs, &wd->sc_bslist, dbs_next)
+                       if (dbs->dbs_min >= blkno && dbs->dbs_max < maxblk)
+                               goto bad;
+       }
+
        /* Queue transfer on drive, activate drive and controller if idle. */
        s = splbio();
        BUFQ_PUT(&wd->sc_q, bp);
@@ -744,6 +769,25 @@
                        return;
                }
                printf("\n");
+
+               /*
+                * Not all errors indicate a failed block but those that do,
+                * put the block on the bad-block list for the device.  Only
+                * do this for reads because the drive should do it for writes,
+                * itself, according to Manuel.
+                */
+               if ((b->b_flags & B_READ) &&
+                   ((wd->drvp->ata_vers >= 4 && wd->sc_wdc_bio.r_error & 64) ||
+                    (wd->drvp->ata_vers < 4 && wd->sc_wdc_bio.r_error & 192)) {
+                       struct disk_badsectors *dbs;
+
+                       dbs = malloc(sizeof *dbs, M_TEMP, M_WAITOK);
+                       dbs->dbs_min = bp->b_rawblkno;
+                       dbs->dbs_max = dbs->dbs_min + bp->b_bcount - 1;
+                       microtime(&dbs->dbs_failedat);
+                       SLIST_INSERT_HEAD(&wd->sc_bslist, dbs, dbs_next);
+               }
+
                bp->b_flags |= B_ERROR;
                bp->b_error = EIO;
                break;
@@ -1137,6 +1181,37 @@
                return 0;
 #endif
 
+       case DIOCBSLIST :
+       {
+               caddr_t laddr = *(caddr_t *)addr;
+               struct disk_badsectors *dbs;
+               size_t available;
+               u_int32_t count;
+
+               count = 0;
+               copyin(laddr, &available, sizeof(available));
+               laddr += sizeof(count);
+               SLIST_FOREACH(dbs, &wd->sc_bslist, dbs_next) {
+                       if (available < sizeof(dbs))
+                               break;
+                       available -= sizeof(dbs);
+                       copyout(dbs, laddr, sizeof(*dbs));
+                       laddr += sizeof(*dbs);
+                       count++;
+               }
+               copyout(&count, *(caddr_t *)addr, sizeof(count));
+               return 0;
+       }
+
+       case DIOCBSFLUSH :
+               /* Clean out the bad sector list */
+               while (!SLIST_EMPTY(&wd->sc_bslist)) {
+                       void *head = SLIST_FIRST(&wd->sc_bslist);
+                       SLIST_REMOVE_HEAD(&wd->sc_bslist, dbs_next);
+                       free(head, M_TEMP);
+               }
+               return 0;
+
        case DIOCGDINFO:
                *(struct disklabel *)addr = *(wd->sc_dk.dk_label);
                return 0;
diff -r c130b9678151 -r 6bd18b270f8c sys/dev/ata/wdvar.h
--- a/sys/dev/ata/wdvar.h       Tue Apr 15 13:59:35 2003 +0000
+++ b/sys/dev/ata/wdvar.h       Tue Apr 15 14:11:00 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: wdvar.h,v 1.15 2003/04/03 22:18:23 fvdl Exp $  */
+/*     $NetBSD: wdvar.h,v 1.16 2003/04/15 14:11:00 darrenr Exp $       */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -136,6 +136,8 @@
 
        void *sc_sdhook;                /* our shutdown hook */
 
+       SLIST_HEAD(, disk_badsectors)   sc_bslist;
+
 #if NRND > 0
        rndsource_element_t     rnd_source;
 #endif
diff -r c130b9678151 -r 6bd18b270f8c sys/sys/disk.h
--- a/sys/sys/disk.h    Tue Apr 15 13:59:35 2003 +0000
+++ b/sys/sys/disk.h    Tue Apr 15 14:11:00 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: disk.h,v 1.20 2002/11/01 11:32:02 mrg Exp $    */
+/*     $NetBSD: disk.h,v 1.21 2003/04/15 14:11:00 darrenr Exp $        */
 
 /*-
  * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
@@ -182,6 +182,16 @@
  */
 TAILQ_HEAD(disklist_head, disk);       /* the disklist is a TAILQ */
 
+/*
+ * Bad sector lists per fixed disk
+ */
+struct disk_badsectors {
+       SLIST_ENTRY(disk_badsectors)    dbs_next;
+       u_int64_t       dbs_min;        /* min. sector number */
+       u_int64_t       dbs_max;        /* max. sector number */
+       struct timeval  dbs_failedat;   /* first failure at */
+};
+
 #ifdef _KERNEL
 extern int disk_count;                 /* number of disks in global disklist */
 
diff -r c130b9678151 -r 6bd18b270f8c sys/sys/dkio.h
--- a/sys/sys/dkio.h    Tue Apr 15 13:59:35 2003 +0000
+++ b/sys/sys/dkio.h    Tue Apr 15 14:11:00 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dkio.h,v 1.6 2002/01/09 04:12:13 thorpej Exp $ */
+/*     $NetBSD: dkio.h,v 1.7 2003/04/15 14:11:00 darrenr Exp $ */
 
 /*
  * Copyright (c) 1987, 1988, 1993
@@ -88,4 +88,8 @@
                /* sync disk cache */
 #define        DIOCCACHESYNC   _IOW('d', 118, int)     /* sync cache (force?) */
 
+               /* bad sector list */
+#define        DIOCBSLIST      _IOWR('d', 119, caddr_t)        /* get list */
+#define        DIOCBSFLUSH     _IO('d', 120)                   /* flush list */
+
 #endif /* _SYS_DKIO_H_ */



Home | Main Index | Thread Index | Old Index