Fix for kern/3835: add an sd sense handler. If we get a check condition/sense

data ASC/ASCQ of 0x04/0x01 (logical unit not ready, initialization in progress),
hang out for 5 seconds and return a RETRY THE OPERATION command. If we
get a check condition/sense data ASC/ASCQ of 0x04/0x02 (logical unit not
ready, initialization command required), send a polled/nosleep START UNIT
command and return a RETRY THE OPERATION command if that succeeds.

Don't send a START UNIT to a disk quirked as SDEV_NO_START. Don't send
a START UNIT to removable media. The reason for the latter is to not
just blindly spin up new (maybe changed) media.

I should note that I've successfully made this work with the ISP host
adapter so far. Other host adapters will need some work to be able
to manage or reasonably fail NOSLEEP/POLL commands while in this
state. Alternatively, the internal SCSI midlayer structure has got
to allow for more controlled error recovery (e.g., restart queues
controlled by the target driver).
This commit is contained in:
mjacob 1998-07-15 20:21:12 +00:00
parent bf29f419e7
commit 66ea6cc018
1 changed files with 67 additions and 2 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: sd.c,v 1.126 1998/03/03 23:15:36 cgd Exp $ */
/* $NetBSD: sd.c,v 1.127 1998/07/15 20:21:12 mjacob Exp $ */
/*
* Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
@ -46,6 +46,7 @@
* Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
*/
#include "opt_scsiverbose.h"
#include "rnd.h"
#include <sys/types.h>
@ -95,13 +96,14 @@ void sdgetdisklabel __P((struct sd_softc *));
void sdstart __P((void *));
void sddone __P((struct scsipi_xfer *));
int sd_reassign_blocks __P((struct sd_softc *, u_long));
int sd_interpret_sense __P((struct scsipi_xfer *));
extern struct cfdriver sd_cd;
struct dkdriver sddkdriver = { sdstrategy };
struct scsipi_device sd_switch = {
NULL, /* Use default error handler */
sd_interpret_sense, /* check our error handler first */
sdstart, /* have a queue, served by this */
NULL, /* have no async handler */
sddone, /* deal with stats at interrupt time */
@ -865,6 +867,69 @@ sd_reassign_blocks(sd, blkno)
SCSI_DATA_OUT));
}
/*
* Check Errors
*/
int
sd_interpret_sense(xs)
struct scsipi_xfer *xs;
{
struct scsipi_link *sc_link = xs->sc_link;
struct scsipi_sense_data *sense = &xs->sense.scsi_sense;
struct sd_softc *sd = sc_link->device_softc;
int retval = SCSIRET_CONTINUE;
/*
* If the device is not open yet, let the generic code handle it.
*/
if ((sc_link->flags & SDEV_OPEN) == 0) {
return (retval);
}
/*
* If it isn't a extended or extended/deferred error, let
* the generic code handle it.
*/
if ((sense->error_code & SSD_ERRCODE) != 0x70 &&
(sense->error_code & SSD_ERRCODE) != 0x71) { /* DEFFERRED */
return (retval);
}
if ((sense->flags & SSD_KEY) == SKEY_NOT_READY &&
sense->add_sense_code == 0x4) {
if (sense->add_sense_code_qual == 0x01) {
printf("%s: ..is spinning up...waiting\n",
sd->sc_dev.dv_xname);
/*
* I really need a sdrestart function I can call here.
*/
delay(1000000 * 5); /* 5 seconds */
retval = SCSIRET_RETRY;
} else if ((sense->add_sense_code_qual == 0x2) &&
(sd->sc_link->quirks & SDEV_NOSTARTUNIT) == 0) {
if (sd->sc_link->flags & SDEV_REMOVABLE) {
printf("%s: removable disk stopped- not "
"restarting\n", sd->sc_dev.dv_xname);
retval = EIO;
} else {
printf("%s: respinning up disk\n",
sd->sc_dev.dv_xname);
retval = scsipi_start(sd->sc_link, SSS_START,
SCSI_URGENT | SCSI_NOSLEEP | SCSI_POLL);
if (retval != 0) {
printf("%s: respin of disk failed-%d\n",
sd->sc_dev.dv_xname, retval);
retval = EIO;
} else {
retval = SCSIRET_RETRY;
}
}
}
}
return (retval);
}
int
sdsize(dev)
dev_t dev;