3132 lines
79 KiB
C
3132 lines
79 KiB
C
#ident "@(#)sd.c 1.1 94/10/31 Copyr 1990 Sun Micro"
|
|
|
|
#include "sd.h"
|
|
#if NSD > 0
|
|
|
|
/*#define SDDEBUG /* Allow compiling of debug code */
|
|
|
|
/*
|
|
* SCSI driver for SCSI disks.
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/dk.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/user.h>
|
|
#include <sys/map.h>
|
|
#include <sys/vmmac.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/dkbad.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/file.h>
|
|
#include <sys/syslog.h>
|
|
|
|
#include <machine/psl.h>
|
|
|
|
#include <sun/dklabel.h>
|
|
#include <sun/dkio.h>
|
|
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/screg.h>
|
|
#include <sundev/sireg.h>
|
|
#include <sundev/scsi.h>
|
|
#include <sundev/sdreg.h>
|
|
|
|
#if defined(SD_FORMAT)
|
|
#include <vm/faultcode.h>
|
|
#include <vm/hat.h>
|
|
#include <vm/seg.h>
|
|
#include <vm/as.h>
|
|
#endif defined(SD_FORMAT)
|
|
|
|
/* Driver error recovery and error message error thresholds */
|
|
#define EL_FAILS 6 /* Soft msg threshold */
|
|
#define MAX_FAILS 10 /* Soft errors before reassign */
|
|
|
|
#define EL_RETRY 2 /* Retry msg threshold */
|
|
#define EL_REST 0 /* Restore msg threshold */
|
|
#define MAX_RETRIES 4 /* Cmd retry limit */
|
|
#define MAX_RESTORES 3 /* Rezero unit limit (after retries) */
|
|
#define MAX_LABEL_RETRIES 2 /* Cmd retry limit for label */
|
|
#define MAX_LABEL_RESTORES 1 /* Restore limit for label */
|
|
#define MAX_SPINUP 30 /* Spin up delay limit */
|
|
#define MAX_BUSY 1000 /* Busy status retry limit */
|
|
|
|
#define SHORT_TIMEOUT 2 /* Short timeout (2 * minutes) */
|
|
#define NORMAL_TIMEOUT 4 /* Read/Write timeout (2 * minutes) */
|
|
#define FORMAT_TIMEOUT 150 /* Format timeout (2 * minutes) */
|
|
|
|
#define LPART(dev) (dev & (NLPART -1))
|
|
#define SDUNIT(dev) (minor(dev) >> 3)
|
|
#define SDNUM(un) (un-sdunits)
|
|
#define LONG (sizeof(u_long))
|
|
#define LONG_ALIGN(arg) (((u_int)arg + LONG -1) & ~(LONG -1))
|
|
#define CDB6_LIMIT (-1 << 21)
|
|
|
|
/* un_present definitions */
|
|
#define ONLINE 2
|
|
#define MEDIA_CHANGED 1
|
|
#define OFFLINE 0
|
|
|
|
|
|
#ifdef SDDEBUG
|
|
short sd_debug = 1; /*
|
|
* 0 = normal operation
|
|
* 1 = extended error info only
|
|
* 2 = debug and error info
|
|
* 3 = all status info
|
|
*/
|
|
/* Handy debugging 0, 1, and 2 argument printfs */
|
|
#define SD_PRINT_CMD(un) \
|
|
if (sd_debug > 1) sd_print_cmd(un)
|
|
#define DPRINTF(str) \
|
|
if (sd_debug > 1) printf(str)
|
|
#define DPRINTF1(str, arg1) \
|
|
if (sd_debug > 1) printf(str,arg1)
|
|
#define DPRINTF2(str, arg1, arg2) \
|
|
if (sd_debug > 1) printf(str,arg1,arg2)
|
|
|
|
/* Handy extended error reporting 0, 1, and 2 argument printfs */
|
|
#define EPRINTF(str) \
|
|
if (sd_debug) printf(str)
|
|
#define EPRINTF1(str, arg1) \
|
|
if (sd_debug) printf(str,arg1)
|
|
#define EPRINTF2(str, arg1, arg2) \
|
|
if (sd_debug) printf(str,arg1,arg2)
|
|
#define DEBUG_DELAY(cnt) \
|
|
if (sd_debug) DELAY(cnt)
|
|
|
|
#else SDDEBUG
|
|
#define SD_PRINT_CMD(un)
|
|
#define DPRINTF(str)
|
|
#define DPRINTF1(str, arg2)
|
|
#define DPRINTF2(str, arg1, arg2)
|
|
#define EPRINTF(str)
|
|
#define EPRINTF1(str, arg2)
|
|
#define EPRINTF2(str, arg1, arg2)
|
|
#define DEBUG_DELAY(cnt)
|
|
#endif SDDEBUG
|
|
|
|
extern char *strncpy();
|
|
|
|
extern struct scsi_unit sdunits[];
|
|
extern struct scsi_unit_subr scsi_unit_subr[];
|
|
extern struct scsi_disk sdisk[];
|
|
extern int nsdisk, sd_repair;
|
|
#ifdef DKIOCWCHK
|
|
extern u_char sdwchkmap[];
|
|
#endif DKIOCWCHK
|
|
|
|
short sd_restores = EL_REST;
|
|
short sd_max_retries = MAX_RETRIES;
|
|
short sd_max_restores = MAX_RESTORES;
|
|
short sd_max_busy = MAX_BUSY;
|
|
|
|
short sd_fails = EL_FAILS;
|
|
short sd_min_fails = EL_FAILS;
|
|
short sd_max_fails = MAX_FAILS;
|
|
|
|
static char *iopb_error = "sd%d: no iopb space for buffer\n";
|
|
static char *overflow_error= "sd%d: Excessive soft errors! Check drive.\n";
|
|
static char *sector_error = "sd%d: single sector I/O failed\n";
|
|
static char *reassign_error = "sd%d: reassigning %s block %u\n";
|
|
static char *clear_msg = "sd%d: unable to zero remapped block\n";
|
|
static char *sense_msg1 = "sd%d%c error: sense key(0x%x): %s\n";
|
|
static char *sense_msg2 = "sd%d%c error: sense key(0x%x): %s, error code(0x%x): %s\n";
|
|
static char *disk_error1 = "sd%d%c: %s %s\n";
|
|
static char *disk_error2 = "sd%d%c: %s %s, block %u (%u relative)\n";
|
|
static char *hard_error = "sd%d%c: hard error, block %u (%u relative)\n";
|
|
|
|
#ifndef SDDEBUG
|
|
short sd_retry = EL_RETRY; /*
|
|
* Error message threshold, retries.
|
|
* Make it global so manufacturing can
|
|
* override setting.
|
|
*/
|
|
|
|
#else SDDEBUG
|
|
short sd_retry = 0; /* For debug, set retries to 0. */
|
|
|
|
sd_print_buffer(y, count)
|
|
register u_char *y;
|
|
register int count;
|
|
{
|
|
register int x;
|
|
|
|
for (x = 0; x < count; x++)
|
|
printf(" %x", *y++);
|
|
printf("\n");
|
|
}
|
|
#endif SDDEBUG
|
|
|
|
|
|
/*
|
|
* Return a pointer to this unit's unit structure.
|
|
*/
|
|
sdunitptr(md)
|
|
register struct mb_device *md;
|
|
{
|
|
return ((int)&sdunits[md->md_unit]);
|
|
}
|
|
|
|
|
|
static
|
|
sdtimer(dev)
|
|
register dev_t dev;
|
|
{
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_unit *un;
|
|
register struct scsi_ctlr *c;
|
|
register u_int unit;
|
|
|
|
unit = SDUNIT(dev);
|
|
un = &sdunits[unit];
|
|
dsi = &sdisk[unit];
|
|
c = un->un_c;
|
|
#ifdef lint
|
|
c = c;
|
|
#endif lint
|
|
|
|
/* DPRINTF("sdtimer:\n"); */
|
|
if (dsi->un_openf >= OPENING) {
|
|
(*c->c_ss->scs_start)(un);
|
|
if ((dsi->un_timeout != 0) && (--dsi->un_timeout == 0)) {
|
|
|
|
/* Process command timeout for normal I/O operations */
|
|
log(LOG_CRIT,"sd%d: I/O request timeout\n", unit);
|
|
dsi->un_timeout = 4;
|
|
if ((*c->c_ss->scs_deque)(c, un)) {
|
|
/* Can't recover, reset! */
|
|
dsi->un_timeout = 0;
|
|
(*c->c_ss->scs_reset)(c, 1);
|
|
}
|
|
} else if (dsi->un_timeout != 0) {
|
|
DPRINTF1("sd%d: sttimer running ", unit);
|
|
DPRINTF2("open= %d, timeout= %d\n",
|
|
dsi->un_openf, dsi->un_timeout);
|
|
}
|
|
|
|
/* Process opening delay timeout */
|
|
} else if ((dsi->un_timeout != 0) && (--dsi->un_timeout == 0)) {
|
|
DPRINTF1("sd%d: sttimer running...\n", unit);
|
|
wakeup((caddr_t)dev);
|
|
}
|
|
timeout(sdtimer, (caddr_t)dev, 30*hz);
|
|
}
|
|
|
|
|
|
/*
|
|
* Attach device (boot time).
|
|
*/
|
|
sdattach(md)
|
|
register struct mb_device *md;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct dk_label *l;
|
|
register struct scsi_disk *dsi;
|
|
struct scsi_inquiry_data *sid, *sid_orig;
|
|
int i;
|
|
|
|
dsi = &sdisk[md->md_unit];
|
|
un = &sdunits[md->md_unit];
|
|
|
|
/*
|
|
* link in, fill in unit struct.
|
|
*/
|
|
un->un_md = md;
|
|
un->un_mc = md->md_mc;
|
|
un->un_unit = md->md_unit;
|
|
un->un_target = TARGET(md->md_slave);
|
|
un->un_lun = LUN(md->md_slave);
|
|
un->un_ss = &scsi_unit_subr[TYPE(md->md_flags)];
|
|
un->un_present = OFFLINE;
|
|
|
|
dsi->un_openf = CLOSED;
|
|
dsi->un_sense = NULL;
|
|
dsi->un_flags = 0;
|
|
dsi->un_timeout = 0;
|
|
dsi->un_timer = 0;
|
|
dsi->un_bad_index = 0;
|
|
dsi->un_status = 0;
|
|
dsi->un_retries = 0;
|
|
dsi->un_restores = 0;
|
|
dsi->un_sec_left = 0;
|
|
dsi->un_total_errors = 0;
|
|
dsi->un_err_resid = 0;
|
|
dsi->un_err_blkno = 0;
|
|
dsi->un_cyl_start = 0;
|
|
dsi->un_cyl_end = 0;
|
|
dsi->un_cylin_last = 0;
|
|
dsi->un_lp1 = NULL;
|
|
dsi->un_lp2 = NULL;
|
|
dsi->un_ctype = CTYPE_UNKNOWN;
|
|
|
|
/*
|
|
* Set default disk geometry and partition table.
|
|
* This is necessary so we can later open the device
|
|
* if we don't find it at probe time.
|
|
*/
|
|
bzero((caddr_t)&(dsi->un_g), sizeof (struct dk_geom));
|
|
bzero((caddr_t)dsi->un_map, sizeof (struct dk_allmap));
|
|
dsi->un_g.dkg_ncyl = 1;
|
|
dsi->un_g.dkg_acyl = 0;
|
|
dsi->un_g.dkg_pcyl = 1;
|
|
dsi->un_g.dkg_nhead = 1;
|
|
dsi->un_g.dkg_nsect = 1;
|
|
dsi->un_cyl_size = 1;
|
|
|
|
/*
|
|
* Test for unit ready. The first test checks for a non-existant
|
|
* device. The other tests wait for the drive to get ready.
|
|
*/
|
|
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) > 1) {
|
|
DPRINTF("sdattach: unit offline\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Temporarily allocate space for label/request sense/inquiry buffer.
|
|
* Align it to a longword boundary.
|
|
*/
|
|
sid = sid_orig = (struct scsi_inquiry_data *)rmalloc(iopbmap,
|
|
(long)(SECSIZE + LONG));
|
|
if (sid) {
|
|
sid = (struct scsi_inquiry_data *) LONG_ALIGN(sid);
|
|
dsi->un_sense = (int)sid;
|
|
} else {
|
|
log(LOG_CRIT, iopb_error, SDNUM(un));
|
|
return;
|
|
}
|
|
|
|
(void) simple(un, SC_START, 0, 0, 1); /* Spin-up disk */
|
|
for (i = 0; i < MAX_SPINUP; i++) {
|
|
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) == 0) {
|
|
goto SDATTACH_UNIT;
|
|
|
|
/* Possible device fault, maybe recoverable */
|
|
} else if (un->un_scb.chk) {
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
(void) simple(un, SC_REQUEST_SENSE,
|
|
(char *)sid - DVMA, 0,
|
|
sizeof (struct scsi_sense));
|
|
EPRINTF(" sid = ");
|
|
sd_print_buffer((u_char *)sid,
|
|
sizeof(struct scsi_sense));
|
|
}
|
|
#endif SDDEBUG
|
|
goto SDATTACH_UNIT;
|
|
|
|
/* Reservation conflict, quit */
|
|
} else if (un->un_scb.is && un->un_scb.busy) {
|
|
EPRINTF("sdattach: reservation conflict\n");
|
|
break;
|
|
|
|
/* Busy, wait for final status. */
|
|
} else if (un->un_scb.busy) {
|
|
EPRINTF("sdattach: unit busy\n");
|
|
DELAY(2000000); /* Wait 2 Sec. */
|
|
}
|
|
}
|
|
DPRINTF("sdattach: unit offline\n");
|
|
return;
|
|
|
|
SDATTACH_UNIT:
|
|
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) != 0) {
|
|
DPRINTF("sdattach: unit failed\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Only CCS controllers support Inquiry command.
|
|
* Note, dtype should be 0 for random access disks.
|
|
* Also, rdf should be 1 for CCS compatible device.
|
|
*/
|
|
bzero((caddr_t)sid, (u_int)sizeof (struct scsi_inquiry_data));
|
|
if (simple(un, SC_INQUIRY, (char *) sid - DVMA, 0,
|
|
(int)sizeof (struct scsi_inquiry_data)) == 0) {
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf("sdopen: vid= <%s>\n", sid->vid);
|
|
printf(" dtype= %d, rdf= %d\n",
|
|
sid->dtype, sid->rdf);
|
|
printf(" sid =");
|
|
sd_print_buffer((u_char *)sid, 32);
|
|
}
|
|
#endif SDDEBUG
|
|
dsi->un_flags = 0;
|
|
if (bcmp(sid->vid, "EMULEX", 6) == 0) {
|
|
EPRINTF("sdattach: Emulex found\n");
|
|
dsi->un_ctype = CTYPE_MD21;
|
|
} else if (sid->dtype == 0) {
|
|
EPRINTF("sdattach: fixed disk found\n");
|
|
dsi->un_ctype = CTYPE_CCS;
|
|
} else if (sid->dtype == 4 || sid->dtype == 8) {
|
|
EPRINTF("sdattach: disk found\n");
|
|
dsi->un_ctype = CTYPE_CCS;
|
|
} else {
|
|
EPRINTF("sdattach: no disk found\n");
|
|
return;
|
|
}
|
|
if (sid->rmb) {
|
|
EPRINTF("sdattach: removable disk\n");
|
|
dsi->un_options |= SD_REMOVABLE;
|
|
} else {
|
|
dsi->un_options &= ~SD_REMOVABLE;
|
|
}
|
|
#ifdef SDDEBUG
|
|
if (sid->rdf == 0) {
|
|
EPRINTF("sdattach: non-CCS disk\n");
|
|
}
|
|
#endif SDDEBUG
|
|
} else {
|
|
/* non-CCS, assume Adaptec ACB-4000 */
|
|
EPRINTF("sdattach: Adaptec found\n");
|
|
dsi->un_ctype = CTYPE_ACB4000;
|
|
dsi->un_flags = SC_UNF_NO_DISCON;
|
|
}
|
|
|
|
/*
|
|
* Ok, it's ready - try to read and use the label.
|
|
* Note uselabel also marks the unit as present.
|
|
*/
|
|
l = (struct dk_label *) (dsi->un_sense);
|
|
l->dkl_magic = 0;
|
|
if (getlabel(un, l)) {
|
|
(void) uselabel(un, l);
|
|
}
|
|
rmfree(iopbmap, (long)(SECSIZE + LONG), (u_long)sid_orig);
|
|
dsi->un_sense = NULL;
|
|
|
|
/* Permanently allocate request sense/inquiry buffer and align it. */
|
|
sid = (struct scsi_inquiry_data *)rmalloc(iopbmap,
|
|
(long)(SECSIZE + LONG));
|
|
if (sid) {
|
|
sid = (struct scsi_inquiry_data *) LONG_ALIGN(sid);
|
|
dsi->un_sense = (int)sid;
|
|
} else {
|
|
un->un_present = OFFLINE;
|
|
dsi->un_openf = CLOSED;
|
|
log(LOG_CRIT, iopb_error, SDNUM(un));
|
|
}
|
|
DPRINTF2("sd%d: sdattach: buffer= 0x%x, ", SDNUM(un), (int)sid);
|
|
DPRINTF2("dsi= 0x%x, un= 0x%x\n", dsi, un);
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the label from the disk.
|
|
* Return true if read was ok, false otherwise.
|
|
*/
|
|
static
|
|
getlabel(un, l)
|
|
register struct scsi_unit *un;
|
|
register struct dk_label *l;
|
|
{
|
|
register int retries, restores;
|
|
|
|
for (restores = 0; restores < MAX_LABEL_RESTORES; ++restores) {
|
|
for (retries = 0; retries < MAX_LABEL_RETRIES; retries++) {
|
|
if (simple(un, SC_READ, (char *) l - DVMA, 0, 1) == 0) {
|
|
return (1);
|
|
}
|
|
}
|
|
(void) simple(un, SC_REZERO_UNIT, 0, 0, 0);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check the label for righteousity. Returns 0 for OK, 1 for failure.
|
|
* Snarf yummos from validated label. Responsible for autoconfig
|
|
* message, interestingly enough. Also marks the unit as present.
|
|
*/
|
|
static
|
|
uselabel(un, l)
|
|
register struct scsi_unit *un;
|
|
register struct dk_label *l;
|
|
{
|
|
register struct scsi_disk *dsi;
|
|
register int intrlv;
|
|
register short *sp;
|
|
register short sum;
|
|
register short count;
|
|
|
|
/*
|
|
* Check magic number of the label
|
|
*/
|
|
if (l->dkl_magic != DKL_MAGIC) {
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Check the checksum of the label
|
|
*/
|
|
sp = (short *)l;
|
|
sum = 0;
|
|
count = sizeof (struct dk_label) / sizeof (short);
|
|
while (count--) {
|
|
sum ^= *sp++;
|
|
}
|
|
|
|
if (sum) {
|
|
EPRINTF1("sd%d: corrupt label\n", SDNUM(un));
|
|
return (1);
|
|
}
|
|
printf("sd%d: <%s>\n", SDNUM(un), l->dkl_asciilabel);
|
|
|
|
/*
|
|
* Fill in disk geometry from label.
|
|
*/
|
|
dsi = &sdisk[un->un_unit];
|
|
dsi->un_g.dkg_ncyl = l->dkl_ncyl;
|
|
dsi->un_g.dkg_acyl = l->dkl_acyl;
|
|
dsi->un_g.dkg_bcyl = 0;
|
|
dsi->un_g.dkg_nhead = l->dkl_nhead;
|
|
dsi->un_g.dkg_bhead = l->dkl_bhead;
|
|
dsi->un_g.dkg_nsect = l->dkl_nsect;
|
|
dsi->un_g.dkg_gap1 = l->dkl_gap1;
|
|
dsi->un_g.dkg_gap2 = l->dkl_gap2;
|
|
dsi->un_g.dkg_intrlv = l->dkl_intrlv;
|
|
dsi->un_g.dkg_pcyl = l->dkl_pcyl;
|
|
/*
|
|
* Old labels don't have pcyl in them, so we make a guess at it.
|
|
*/
|
|
if (dsi->un_g.dkg_pcyl == 0) {
|
|
EPRINTF1("sd%d: old label\n", SDNUM(un));
|
|
dsi->un_g.dkg_pcyl = dsi->un_g.dkg_ncyl + dsi->un_g.dkg_acyl;
|
|
}
|
|
dsi->un_cyl_size = dsi->un_g.dkg_nhead * dsi->un_g.dkg_nsect;
|
|
|
|
/*
|
|
* Fill in partition table.
|
|
*/
|
|
for (count = 0; count < NLPART; count++)
|
|
dsi->un_map[count] = l->dkl_map[count];
|
|
/*
|
|
* Diddle stats if neccessary.
|
|
*/
|
|
if (un->un_md->md_dk >= 0) {
|
|
intrlv = dsi->un_g.dkg_intrlv;
|
|
if (intrlv <= 0 || intrlv >= dsi->un_g.dkg_nsect) {
|
|
intrlv = 1;
|
|
}
|
|
dk_bps[un->un_md->md_dk] =
|
|
(SECSIZE * 60 * dsi->un_g.dkg_nsect) / intrlv;
|
|
}
|
|
un->un_present = ONLINE; /* "it's here..." */
|
|
dsi->un_openf = OPEN_PROBE;
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Run a command in polled mode.
|
|
* Return true if successful, false otherwise.
|
|
*/
|
|
static
|
|
simple(un, cmd, dma_addr, secno, nsect)
|
|
register struct scsi_unit *un;
|
|
int cmd, dma_addr, secno, nsect;
|
|
{
|
|
register struct scsi_disk *dsi = &sdisk[un->un_unit];
|
|
register struct scsi_cdb *cdb;
|
|
register struct scsi_ctlr *c;
|
|
register int err;
|
|
|
|
/*
|
|
* Grab and clear the command block.
|
|
*/
|
|
dsi->un_openf = OPENING;
|
|
c = un->un_c;
|
|
cdb = &un->un_cdb;
|
|
bzero((caddr_t)cdb, CDB_GROUP1);
|
|
|
|
/*
|
|
* Plug in command block values.
|
|
*/
|
|
cdb->cmd = cmd;
|
|
if (SCSI_RECV_DATA_CMD(cmd))
|
|
un->un_flags |= SC_UNF_RECV_DATA;
|
|
else
|
|
un->un_flags &= ~SC_UNF_RECV_DATA;
|
|
|
|
c->c_un = un;
|
|
cdb->lun = un->un_lun;
|
|
FORMG0ADDR(cdb, secno);
|
|
FORMG0COUNT(cdb, nsect);
|
|
un->un_dma_addr = dma_addr;
|
|
if (cmd == SC_START)
|
|
un->un_dma_count = 0;
|
|
else if (cmd == SC_INQUIRY || cmd == SC_REQUEST_SENSE)
|
|
un->un_dma_count = nsect;
|
|
else
|
|
un->un_dma_count = nsect << SECDIV;
|
|
|
|
/*
|
|
* Run the command and check the return status for errors.
|
|
* Note, err = 1, recoverable failure; >1, hard failure.
|
|
*/
|
|
if (err = (*c->c_ss->scs_cmd)(c, un, 0)) {
|
|
if (err > 1)
|
|
err = 2; /* Hard failure */
|
|
}
|
|
dsi->un_openf = CLOSED;
|
|
return (err); /* No failure */
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine opens a disk. Note that we can handle disks
|
|
* that make an appearance after boot time.
|
|
*/
|
|
/*ARGSUSED*/
|
|
sdopen(dev, flag)
|
|
dev_t dev;
|
|
int flag;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct dk_label *l, *l_orig = NULL;
|
|
register struct dk_map *lp;
|
|
register int unit = SDUNIT(dev);
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_inquiry_data *sid = NULL;
|
|
int i, s, error = 0;
|
|
|
|
if (unit >= nsdisk) {
|
|
DPRINTF("sdopen: illegal unit\n");
|
|
return (ENXIO);
|
|
}
|
|
un = &sdunits[unit];
|
|
dsi = &sdisk[unit];
|
|
if (un->un_mc == 0) {
|
|
DPRINTF("sdopen: disk not attached\n");
|
|
return (ENXIO);
|
|
}
|
|
|
|
/*
|
|
* If command timeouts not activated yet, switch it on.
|
|
*/
|
|
if (dsi->un_timer == 0) {
|
|
DPRINTF("sdopen: starting timer\n");
|
|
dsi->un_timer++;
|
|
timeout(sdtimer, (caddr_t)dev, 30*hz);
|
|
}
|
|
|
|
/*
|
|
* If already found on autoconfig probe, use it.
|
|
*/
|
|
if (dsi->un_openf == OPEN_PROBE) {
|
|
dsi->un_openf = OPEN;
|
|
return (0);
|
|
}
|
|
/*
|
|
* If normal open, check if drive/media changed.
|
|
*/
|
|
if (un->un_present == ONLINE) {
|
|
EPRINTF("sdopen: checking device\n");
|
|
if (sdcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0) == 0)
|
|
return (0);
|
|
}
|
|
/*
|
|
* If open in progress, wait for completion to prevent
|
|
* race conditions during open.
|
|
*/
|
|
if (dsi->un_openf == OPENING) {
|
|
for(i = 0; i < MAX_SPINUP; i++) {
|
|
DPRINTF1("sd%d: sdopen: waiting...\n", unit);
|
|
(void) sleep((caddr_t) &lbolt, PRIBIO);
|
|
if (dsi->un_openf != OPENING)
|
|
break;
|
|
}
|
|
if (dsi->un_openf == OPEN && un->un_present == ONLINE) {
|
|
DPRINTF1("sd%d: sdopen: open delay ok\n", unit);
|
|
return (0);
|
|
} else if (dsi->un_openf == OPENING) {
|
|
EPRINTF1("sd%d: sdopen: open wait timeout\n", unit);
|
|
return (EBUSY);
|
|
} else if (dsi->un_openf == CLOSED) {
|
|
EPRINTF1("sd%d: sdopen: prior open failed\n", unit);
|
|
return (ENXIO);
|
|
}
|
|
}
|
|
/*
|
|
* Didn't see it at autoconfig time? Let's look again..
|
|
* Note, raise priority until we've figure out whether
|
|
* to enable or disable disconnect/reconnect to prevent
|
|
* collisions with multiple luns of same target. Otherwise
|
|
* serious target failures tend to occur (some targets
|
|
* hang).
|
|
*/
|
|
DPRINTF1("sd%d: sdopen: opening device\n", unit);
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
dsi->un_openf = OPENING;
|
|
|
|
lp = &dsi->un_map[LPART(dev)];
|
|
lp->dkl_nblk = 2;
|
|
if ((error=sdcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0)) &&
|
|
error == ENXIO) {
|
|
EPRINTF1("sd%d: sdopen: not online\n", unit);
|
|
error = ENXIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
error = 0;
|
|
|
|
/* Spin-up disk */
|
|
(void) sdcmd(dev, SC_START, 0, 1, (caddr_t)0, 0);
|
|
|
|
if (sdcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0) &&
|
|
sdcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0)) {
|
|
EPRINTF1("sd%d: sdopen: not ready\n", unit);
|
|
error = ENXIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
/*
|
|
* If request sense/inquiry buffer not already allocated,
|
|
* allocate it and align it to a longword boundary.
|
|
*/
|
|
sid = (struct scsi_inquiry_data *) dsi->un_sense;
|
|
if ((int) sid == NULL) {
|
|
sid = (struct scsi_inquiry_data *)rmalloc(iopbmap,
|
|
(long)(sizeof (struct scsi_inquiry_data) + LONG));
|
|
if (sid) {
|
|
sid = (struct scsi_inquiry_data *) LONG_ALIGN(sid);
|
|
dsi->un_sense = (int)sid;
|
|
} else {
|
|
log(LOG_CRIT, iopb_error, SDNUM(un));
|
|
error = ENXIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Only CCS controllers support Inquiry command.
|
|
* Note, dtype should be 0 for random access disks.
|
|
* Also, rdf should be 1 for CCS compatible device.
|
|
*/
|
|
bzero((caddr_t)sid, (u_int)sizeof (struct scsi_inquiry_data));
|
|
if (sdcmd(dev, SC_INQUIRY, 0,
|
|
sizeof (struct scsi_inquiry_data), (caddr_t)sid, 0)) {
|
|
/* non-CCS, assume Adaptec */
|
|
EPRINTF("sdopen: Adaptec found\n");
|
|
dsi->un_ctype = CTYPE_ACB4000;
|
|
dsi->un_flags = SC_UNF_NO_DISCON;
|
|
} else {
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf("sdopen: vid= <%s>\n", sid->vid);
|
|
printf(" dtype= %d, rdf= %d\n",
|
|
sid->dtype, sid->rdf);
|
|
printf(" sid =");
|
|
sd_print_buffer((u_char *)sid, 32);
|
|
}
|
|
#endif SDDEBUG
|
|
dsi->un_flags = 0;
|
|
if (bcmp(sid->vid, "EMULEX", 6) == 0) {
|
|
EPRINTF("sdopen: Emulex found\n");
|
|
dsi->un_ctype = CTYPE_MD21;
|
|
} else if (sid->dtype == 0) {
|
|
EPRINTF("sdopen: fixed disk found\n");
|
|
dsi->un_ctype = CTYPE_CCS;
|
|
} else if (sid->dtype == 4 || sid->dtype == 8) {
|
|
EPRINTF("sdattach: disk found\n");
|
|
dsi->un_ctype = CTYPE_CCS;
|
|
} else {
|
|
EPRINTF("sdopen: no disk found\n");
|
|
error = ENXIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
if (sid->rmb) {
|
|
EPRINTF("sdopen: removable disk\n");
|
|
dsi->un_options |= SD_REMOVABLE;
|
|
} else {
|
|
dsi->un_options &= ~SD_REMOVABLE;
|
|
}
|
|
#ifdef SDDEBUG
|
|
if (sid->rdf == 0) {
|
|
EPRINTF("sdattach: non-CCS disk\n");
|
|
}
|
|
#endif SDDEBUG
|
|
}
|
|
|
|
/* Initialize error logging */
|
|
dsi->un_bad_index = 0;
|
|
dsi->un_total_errors = dsi->un_retries = 0;
|
|
dsi->un_restores = sd_max_restores;
|
|
|
|
/* Temporarily allocate volume label buffer and align it. */
|
|
l = l_orig = (struct dk_label *)rmalloc(iopbmap,
|
|
(long)(SECSIZE + LONG));
|
|
if (l) {
|
|
l = (struct dk_label *) LONG_ALIGN(l);
|
|
} else {
|
|
log(LOG_CRIT, iopb_error, SDNUM(un));
|
|
error = ENXIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
|
|
DPRINTF("sdopen: reading label\n");
|
|
dsi->un_openf = OPEN;
|
|
if (sdcmd(dev, SC_READ_LABEL, 0, 1, (caddr_t)l, 0)) {
|
|
if (flag & O_NDELAY) {
|
|
DPRINTF("sdopen: suppressing read label error\n");
|
|
} else {
|
|
DPRINTF1("sd%d: read label error\n", SDNUM(un));
|
|
error = EIO;
|
|
}
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
dsi->un_restores = 0;
|
|
|
|
if (uselabel(un, l)) {
|
|
if ((flag & O_NDELAY) == 0) {
|
|
DPRINTF("sdopen: label error\n");
|
|
error = EIO;
|
|
goto SDOPEN_EXIT;
|
|
}
|
|
}
|
|
|
|
SDOPEN_EXIT:
|
|
if (error) {
|
|
if (dsi->un_timer) { /* disable timer */
|
|
dsi->un_timer = 0;
|
|
untimeout(sdtimer, (caddr_t)dev);
|
|
}
|
|
un->un_present = OFFLINE;
|
|
dsi->un_openf = CLOSED;
|
|
} else {
|
|
dsi->un_openf = OPEN;
|
|
}
|
|
if (l_orig != NULL) /* release label buffer */
|
|
rmfree(iopbmap, (long)(SECSIZE + LONG), (u_long)l_orig);
|
|
(void) splx(s);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* stub close routine for 4c compatibility.
|
|
*/
|
|
/*ARGSUSED*/
|
|
sdclose(dev, flag)
|
|
dev_t dev;
|
|
int flag;
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* This routine returns the size of a logical partition. It is called
|
|
* from the device switch at normal priority.
|
|
*/
|
|
sdsize(dev)
|
|
register dev_t dev;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct dk_map *lp;
|
|
register struct scsi_disk *dsi;
|
|
register int unit;
|
|
|
|
/* DPRINTF("sdsize:\n"); */
|
|
unit = SDUNIT(dev);
|
|
if (unit >= nsdisk) {
|
|
DPRINTF("sdsize: illegal unit\n");
|
|
return (-1);
|
|
}
|
|
|
|
un = &sdunits[unit];
|
|
if (un->un_present == ONLINE) {
|
|
DPRINTF("sdsize: getting info\n");
|
|
dsi = &sdisk[SDUNIT(dev)];
|
|
lp = &dsi->un_map[LPART(dev)];
|
|
return (lp->dkl_nblk);
|
|
} else {
|
|
/*DPRINTF("sdsize: unit not present\n");*/
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine is the focal point of internal commands to the controller.
|
|
* NOTE: this routine assumes that all operations done before the disk's
|
|
* geometry is defined.
|
|
*/
|
|
sdcmd(dev, cmd, block, count, addr, options)
|
|
dev_t dev;
|
|
int cmd, block, count;
|
|
caddr_t addr;
|
|
int options;
|
|
{
|
|
register struct scsi_disk *dsi;
|
|
register struct mb_ctlr *mc;
|
|
register struct scsi_unit *un;
|
|
register struct buf *bp;
|
|
int s, old_options, b_error;
|
|
|
|
un = &sdunits[SDUNIT(dev)];
|
|
dsi = &sdisk[SDUNIT(dev)];
|
|
bp = &un->un_sbuf;
|
|
#ifdef lint
|
|
mc = (struct mb_ctlr *) 0; mc = mc;
|
|
dsi= (struct scsi_disk *) 0; dsi = dsi;
|
|
old_options = 0; old_options = old_options;
|
|
#endif lint
|
|
|
|
/*DPRINTF("sdcmd:\n");*/
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
while (bp->b_flags & B_BUSY) {
|
|
bp->b_flags |= B_WANTED;
|
|
/* DPRINTF1("sdcmd: sleeping..., bp= 0x%x\n", bp); */
|
|
(void) sleep((caddr_t) bp, PRIBIO);
|
|
}
|
|
|
|
bp->b_flags = B_BUSY | B_READ;
|
|
(void) splx(s);
|
|
/* DPRINTF1("sdcmd: waking..., bp= 0x%x\n", bp); */
|
|
/* DPRINTF1("sdcmd: waking up with addr=0x%x\n", addr); */
|
|
|
|
un->un_scmd = cmd;
|
|
bp->b_dev = dev;
|
|
bp->b_bcount = count;
|
|
bp->b_blkno = block;
|
|
bp->b_un.b_addr = addr;
|
|
old_options = dsi->un_options;
|
|
dsi->un_options |= options;
|
|
|
|
#ifdef SD_FORMAT
|
|
if (options & (SD_DVMA_RD | SD_DVMA_WR)) {
|
|
|
|
DPRINTF2("sdcmd: addr= 0x%x size= 0x%x\n", addr, count);
|
|
mc = un->un_mc;
|
|
bp->b_flags |= B_PHYS;
|
|
bp->b_proc = u.u_procp;
|
|
u.u_procp->p_flag |= SPHYSIO;
|
|
/*
|
|
* Fault lock the address range of the buffer.
|
|
*/
|
|
if (as_fault(u.u_procp->p_as, bp->b_un.b_addr,
|
|
(u_int)count, F_SOFTLOCK,
|
|
(options & SD_DVMA_WR) ? S_WRITE : S_READ)) {
|
|
|
|
EPRINTF("sdcmd: as_fault error1\n");
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_error = EIO;
|
|
goto SDCMD_EXIT;
|
|
}
|
|
}
|
|
#endif SD_FORMAT
|
|
/*
|
|
* Execute the I/O request.
|
|
*/
|
|
sdstrategy(bp);
|
|
(void) iowait(bp);
|
|
|
|
#ifdef SD_FORMAT
|
|
/*
|
|
* Release memory and DVMA resources.
|
|
*/
|
|
if (options & (SD_DVMA_RD | SD_DVMA_WR)) {
|
|
if (as_fault(u.u_procp->p_as, bp->b_un.b_addr,
|
|
(u_int)bp->b_bcount, F_SOFTUNLOCK,
|
|
(options & SD_DVMA_WR) ? S_WRITE : S_READ)) {
|
|
|
|
EPRINTF("sdcmd: as_fault error2\n");
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_error = EIO;
|
|
goto SDCMD_EXIT;
|
|
}
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
u.u_procp->p_flag &= ~SPHYSIO;
|
|
bp->b_flags &= ~(B_BUSY | B_PHYS);
|
|
(void) splx(s);
|
|
}
|
|
|
|
SDCMD_EXIT:
|
|
#endif SD_FORMAT
|
|
|
|
/* s = splr(pritospl(un->un_mc->mc_intpri)); */
|
|
dsi->un_options = old_options;
|
|
b_error = geterror(bp);
|
|
bp->b_flags &= ~B_BUSY;
|
|
if (bp->b_flags & B_WANTED) {
|
|
/* DPRINTF1("sdcmd: waking..., bp= 0x%x\n", bp); */
|
|
wakeup((caddr_t)bp);
|
|
}
|
|
/* (void) splx(s); */
|
|
return (b_error);
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine is the high level interface to the disk. It performs
|
|
* reads and writes on the disk using the buf as the method of communication.
|
|
* It is called from the device switch for block operations and via physio()
|
|
* for raw operations. It is called at normal priority.
|
|
*/
|
|
|
|
int (*sdstrategy_tstpoint)();
|
|
|
|
sdstrategy(bp)
|
|
register struct buf *bp;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct mb_device *md;
|
|
register struct dk_map *lp;
|
|
register u_int bn, cyl;
|
|
register struct diskhd *dh;
|
|
register struct scsi_disk *dsi;
|
|
register int s;
|
|
int unit = dkunit(bp);
|
|
|
|
if (sdstrategy_tstpoint != NULL) {
|
|
if ((*sdstrategy_tstpoint)(bp)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/* DPRINTF("sdstrategy:\n"); */
|
|
if (unit >= nsdisk) {
|
|
EPRINTF("sdstrategy: invalid unit\n");
|
|
bp->b_flags |= B_ERROR;
|
|
iodone(bp);
|
|
return;
|
|
}
|
|
un = &sdunits[unit];
|
|
md = un->un_md;
|
|
dsi = &sdisk[unit];
|
|
lp = &dsi->un_map[LPART(bp->b_dev)];
|
|
bn = bp->b_blkno;
|
|
|
|
if (bp != &un->un_sbuf) {
|
|
if ((un->un_present < ONLINE) || (lp->dkl_nblk == 0)) {
|
|
DPRINTF("sdstrategy: unit not present\n");
|
|
DPRINTF2("sdstrategy: bn= 0x%x nblk= 0x%x\n",
|
|
bn, lp->dkl_nblk);
|
|
bp->b_flags |= B_ERROR;
|
|
iodone(bp);
|
|
return;
|
|
}
|
|
/* If EOM, set residue but don't report error */
|
|
if (un->un_present == ONLINE && (bn >= lp->dkl_nblk)) {
|
|
EPRINTF1("sdstrategy: invalid block, %u\n", bn);
|
|
bp->b_resid = bp->b_bcount;
|
|
iodone(bp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((bn >= dsi->un_cyl_start) && (bn < dsi->un_cyl_end) &&
|
|
( (int)lp == dsi->un_lp1)) {
|
|
/*DPRINTF("WIN ");*/
|
|
bp->b_cylin = dsi->un_cylin_last;
|
|
|
|
} else {
|
|
/*DPRINTF("LOSE\n");*/
|
|
dsi->un_lp1 = (int)lp;
|
|
cyl = bn / dsi->un_cyl_size;
|
|
|
|
dsi->un_cyl_start = cyl * dsi->un_cyl_size;
|
|
dsi->un_cyl_end = dsi->un_cyl_start + dsi->un_cyl_size;
|
|
|
|
cyl += lp->dkl_cylno + dsi->un_g.dkg_bcyl;
|
|
bp->b_cylin = dsi->un_cylin_last = cyl;
|
|
}
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
dh = &md->md_utab;
|
|
disksort(dh, bp);
|
|
|
|
/*
|
|
* Call unit start routine to queue up device, if it
|
|
* currently isn't queued.
|
|
*/
|
|
(*un->un_c->c_ss->scs_ustart)(un);
|
|
(*un->un_c->c_ss->scs_start)(un);
|
|
(void) splx(s);
|
|
}
|
|
|
|
|
|
/*
|
|
* Set up a transfer for the controller
|
|
*/
|
|
sdstart(bp, un)
|
|
register struct buf *bp;
|
|
register struct scsi_unit *un;
|
|
{
|
|
register struct dk_map *lp;
|
|
register struct scsi_disk *dsi;
|
|
register int nblk;
|
|
|
|
/* DPRINTF("sdstart:\n"); */
|
|
|
|
dsi = &sdisk[dkunit(bp)];
|
|
lp = &dsi->un_map[LPART(bp->b_dev)];
|
|
|
|
/* Process internal I/O requests */
|
|
if (bp == &un->un_sbuf) {
|
|
DPRINTF("sdstart: internal I/O\n");
|
|
un->un_cmd = un->un_scmd;
|
|
un->un_count = bp->b_bcount;
|
|
un->un_blkno = bp->b_blkno;
|
|
un->un_flags = 0;
|
|
if (dsi->un_options & (SD_DVMA_RD | SD_DVMA_WR))
|
|
un->un_flags = SC_UNF_SPECIAL_DVMA;
|
|
return (1);
|
|
}
|
|
|
|
/* Process file system I/O requests */
|
|
if (bp->b_flags & B_READ) {
|
|
DPRINTF("sdstart: read\n");
|
|
un->un_cmd = SC_READ;
|
|
} else {
|
|
#ifdef DKIOCWCHK
|
|
register u_char i;
|
|
if ((i = sdwchkmap[dkunit(bp)]) &&
|
|
(i & (1<<LPART(bp->b_dev)))) {
|
|
DPRINTF("sdstart: writev\n");
|
|
un->un_cmd = SC_WRITE_VERIFY;
|
|
} else {
|
|
DPRINTF("sdstart: write\n");
|
|
un->un_cmd = SC_WRITE;
|
|
}
|
|
#else DKIOCWCHK
|
|
DPRINTF("sdstart: write\n");
|
|
un->un_cmd = SC_WRITE;
|
|
#endif DKIOCWCHK
|
|
}
|
|
|
|
/* Compute absolute block location */
|
|
if ((int)lp == dsi->un_lp2) {
|
|
un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
|
|
} else {
|
|
dsi->un_lp2 = (int)lp;
|
|
dsi->un_last_cyl_size = lp->dkl_cylno * dsi->un_cyl_size;
|
|
un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
|
|
}
|
|
|
|
/* Finish processing read/write request */
|
|
nblk = (bp->b_bcount + (SECSIZE - 1)) >> SECDIV;
|
|
un->un_count = MIN(nblk, (lp->dkl_nblk - bp->b_blkno));
|
|
un->un_flags = SC_UNF_DVMA;
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make a cdb for disk I/O.
|
|
*/
|
|
sdmkcdb(un)
|
|
struct scsi_unit *un;
|
|
{
|
|
register struct scsi_cdb *cdb;
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_reassign_blk *srb;
|
|
register struct scsi_defect_list *sd;
|
|
register short cmd;
|
|
/*DPRINTF("sdmkcdb:\n");*/
|
|
|
|
dsi = &sdisk[un->un_unit];
|
|
cdb = &un->un_cdb;
|
|
bzero((caddr_t)cdb, CDB_GROUP1);
|
|
cmd = cdb->cmd = un->un_cmd;
|
|
cdb->lun = un->un_lun;
|
|
|
|
switch (un->un_cmd) {
|
|
case SC_READ:
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count << SECDIV;
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
DPRINTF1("sd%d: sdmkcdb: eread\n", un-sdunits);
|
|
cdb->cmd = SC_EREAD;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
} else {
|
|
DPRINTF1("sd%d: sdmkcdb: read\n", un-sdunits);
|
|
cdb->cmd = SC_READ;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
}
|
|
break;
|
|
|
|
case SC_WRITE:
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count << SECDIV;
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
DPRINTF1("sd%d: sdmkcdb: ewrite\n", un-sdunits);
|
|
cdb->cmd = SC_EWRITE;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
} else {
|
|
DPRINTF1("sd%d: sdmkcdb: write\n", un-sdunits);
|
|
cdb->cmd = SC_WRITE;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
}
|
|
break;
|
|
|
|
case SC_WRITE_VERIFY:
|
|
DPRINTF1("sd%d: sdmkcdb: write verify\n", un-sdunits);
|
|
cdb->cmd = un->un_cmd;
|
|
cdb->lun = un->un_lun;
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count << SECDIV;
|
|
break;
|
|
|
|
case SC_REQUEST_SENSE:
|
|
EPRINTF1("sd%d: sdmkcdb: request sense\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
FORMG0COUNT(cdb, sizeof (struct scsi_sense));
|
|
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
|
un->un_dma_count = sizeof (struct scsi_sense);
|
|
bzero((caddr_t)(dsi->un_sense), sizeof (struct scsi_sense));
|
|
return;
|
|
|
|
case SC_SPECIAL_READ:
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count;
|
|
un->un_cmd = SC_READ;
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
DPRINTF1("sdmkcdb: special eread blk= 0x%x\n",
|
|
un->un_blkno);
|
|
cdb->cmd = SC_EREAD;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count >> SECDIV);
|
|
} else {
|
|
DPRINTF1("sdmkcdb: special read blk= 0x%x\n",
|
|
un->un_blkno);
|
|
cdb->cmd = SC_READ;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count >> SECDIV);
|
|
}
|
|
break;
|
|
|
|
case SC_SPECIAL_WRITE:
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count;
|
|
un->un_cmd = SC_WRITE;
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
DPRINTF1("sdmkcdb: special ewrite blk= 0x%x\n",
|
|
un->un_blkno);
|
|
cdb->cmd = SC_EWRITE;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count >> SECDIV);
|
|
} else {
|
|
DPRINTF1("sdmkcdb: special write blk= 0x%x\n",
|
|
un->un_blkno);
|
|
cdb->cmd = SC_WRITE;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count >> SECDIV);
|
|
}
|
|
break;
|
|
|
|
case SC_READ_LABEL:
|
|
DPRINTF1("sd%d: sdmkcdb: read label\n", un-sdunits);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
un->un_dma_addr = (int)un->un_sbuf.b_un.b_addr - (int)DVMA;
|
|
un->un_dma_count = SECSIZE;
|
|
un->un_cmd = SC_READ;
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
DPRINTF1("sd%d: sdmkcdb: eread label\n", un-sdunits);
|
|
cdb->cmd = SC_EREAD;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
} else {
|
|
DPRINTF1("sd%d: sdmkcdb: read label\n", un-sdunits);
|
|
cdb->cmd = SC_READ;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
}
|
|
break;
|
|
|
|
case SC_ZERO_WRITE:
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
|
un->un_dma_count = SECSIZE;
|
|
un->un_cmd = SC_WRITE;
|
|
bzero((caddr_t)(dsi->un_sense), SECSIZE);
|
|
if (un->un_blkno & CDB6_LIMIT) {
|
|
EPRINTF1("sd%d: sdmkcdb: zero ewrite\n", un-sdunits);
|
|
cdb->cmd = SC_EWRITE;
|
|
FORMG1ADDR(cdb, un->un_blkno);
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
} else {
|
|
EPRINTF1("sd%d: sdmkcdb: zero write\n", un-sdunits);
|
|
cdb->cmd = SC_WRITE;
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
}
|
|
break;
|
|
|
|
case SC_INQUIRY:
|
|
EPRINTF1("sd%d: sdmkcdb: inquiry\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
FORMG0COUNT(cdb, sizeof (struct scsi_inquiry_data));
|
|
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
|
un->un_dma_count = sizeof (struct scsi_inquiry_data);
|
|
bzero((caddr_t)(dsi->un_sense),
|
|
sizeof (struct scsi_inquiry_data));
|
|
break;
|
|
|
|
case SC_REZERO_UNIT:
|
|
EPRINTF1("sd%d: sdmkcdb: rezero unit\n", un-sdunits);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_count = un->un_dma_addr = 0;
|
|
return;
|
|
|
|
case SC_REASSIGN_BLOCK:
|
|
EPRINTF2("sd%d: sdmkcdb: reassign blk %u\n", un-sdunits, un->un_blkno);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
|
un->un_dma_count = sizeof (struct scsi_reassign_blk);
|
|
srb = (struct scsi_reassign_blk *)dsi->un_sense;
|
|
srb->reserved = 0;
|
|
srb->length = 4; /* Only 1 defect */
|
|
srb->defect = un->un_blkno;
|
|
return;
|
|
|
|
case SC_FORMAT:
|
|
EPRINTF1("sd%d: sdmkcdb: format\n", un-sdunits);
|
|
dsi->un_timeout = FORMAT_TIMEOUT;
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
cdb->fmt_parm_bits = FPB_DATA | FPB_CMPLT | FPB_BFI;
|
|
un->un_dma_count = un->un_count;
|
|
cdb->fmt_interleave = 1;
|
|
break;
|
|
|
|
case SC_READ_DEFECT_LIST:
|
|
EPRINTF1("sd%d: sdmkcdb: read defect list\n", un-sdunits);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
un->un_dma_count = un->un_count;
|
|
un->un_dma_addr = un->un_baddr;
|
|
FORMG1COUNT(cdb, un->un_count);
|
|
|
|
sd = (struct scsi_defect_list *) un->un_sbuf.b_un.b_addr;
|
|
cdb->defect_list_descrip = sd->descriptor;
|
|
break;
|
|
|
|
case SC_MODE_SELECT:
|
|
EPRINTF1("sd%d: sdmkcdb: mode select\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
un->un_dma_count = un->un_count;
|
|
un->un_dma_addr = un->un_baddr;
|
|
if (un->un_blkno & 0x80) {
|
|
EPRINTF("sdmkcdb: savable mode select\n");
|
|
cdb->tag = 1; /* Save parameters */
|
|
}
|
|
break;
|
|
|
|
case SC_MODE_SENSE:
|
|
EPRINTF1("sd%d: sdmkcdb: mode sense\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
cdb->g0_addr1 = un->un_blkno;
|
|
un->un_dma_count = un->un_count;
|
|
un->un_dma_addr = un->un_baddr;
|
|
break;
|
|
|
|
case SC_TEST_UNIT_READY:
|
|
#ifdef SDDEBUG
|
|
DPRINTF1("sd%d: sdmkcdb: test unit ready\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = un->un_dma_count = 0;
|
|
dsi->un_options |= SD_NORETRY;
|
|
break;
|
|
#endif SDDEBUG
|
|
|
|
case SC_START:
|
|
DPRINTF1("sd%d: sdmkcdb: start\n", un-sdunits);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
FORMG0COUNT(cdb, un->un_count);
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = un->un_dma_count = 0;
|
|
dsi->un_options |= SD_NORETRY;
|
|
break;
|
|
|
|
case SC_RESERVE:
|
|
case SC_RELEASE:
|
|
EPRINTF1("sd%d: sdmkcdb: reserve/release\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
un->un_dma_addr = un->un_dma_count = 0;
|
|
break;
|
|
|
|
case SC_TRANSLATE:
|
|
EPRINTF1("sd%d: sdmkcdb: translate\n", un-sdunits);
|
|
dsi->un_timeout = SHORT_TIMEOUT;
|
|
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
|
FORMG0ADDR(cdb, un->un_blkno);
|
|
FORMG0COUNT(cdb, 0);
|
|
un->un_dma_addr = un->un_baddr;
|
|
un->un_dma_count = un->un_count;
|
|
break;
|
|
|
|
default:
|
|
EPRINTF1("sd%d: sdmkcdb: unknown command\n", un-sdunits);
|
|
dsi->un_timeout = NORMAL_TIMEOUT;
|
|
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
|
dsi->un_flags;
|
|
break;
|
|
}
|
|
dsi->un_last_cmd = cmd;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine handles controller interrupts.
|
|
* It is always called at disk interrupt priority.
|
|
*/
|
|
typedef enum sdintr_error_resolution {
|
|
real_error, /* Hard error */
|
|
psuedo_error, /* What looked like an error is actually OK */
|
|
more_processing /* Recoverable error */
|
|
} sdintr_error_resolution;
|
|
sdintr_error_resolution sderror(), sd_fix_block(), sdintr_ran_reassign();
|
|
|
|
sdintr(c, resid, error)
|
|
register struct scsi_ctlr *c;
|
|
int resid;
|
|
int error;
|
|
{
|
|
register struct scsi_unit *un = c->c_un;
|
|
register struct scsi_disk *dsi;
|
|
register struct buf *bp;
|
|
register struct mb_device *md;
|
|
int status = 0;
|
|
int error_code = 0;
|
|
u_char severe = DK_NOERROR;
|
|
|
|
/* DPRINTF("sdintr:\n"); */
|
|
md = un->un_md;
|
|
bp = md->md_utab.b_forw;
|
|
if (bp == NULL) {
|
|
EPRINTF("sdintr: bp = NULL\n");
|
|
return;
|
|
}
|
|
|
|
dsi = &sdisk[SDUNIT(bp->b_dev)];
|
|
dsi->un_timeout = 0; /* Disable time-outs */
|
|
#ifdef lint
|
|
un = un;
|
|
#endif lint
|
|
|
|
if (md->md_dk >= 0)
|
|
dk_busy &= ~(1 << md->md_dk);
|
|
|
|
/*
|
|
* Check for error or special operation states and process.
|
|
* Otherwise, it was a normal I/O command which was successful.
|
|
*/
|
|
if (dsi->un_openf < OPEN || error || resid ) {
|
|
|
|
/*
|
|
* Special processing for SCSI bus failures.
|
|
*/
|
|
if (error == SE_FATAL) {
|
|
if (dsi->un_openf == OPEN) {
|
|
log(LOG_ERR, "sd%d: scsi bus failure\n",
|
|
SDNUM(un));
|
|
}
|
|
dsi->un_retries = dsi->un_restores = 0;
|
|
dsi->un_err_severe = DK_FATAL;
|
|
un->un_present = OFFLINE;
|
|
dsi->un_openf = CLOSED;
|
|
bp->b_error = ENXIO;
|
|
bp->b_flags |= B_ERROR;
|
|
goto SDINTR_WRAPUP;
|
|
}
|
|
|
|
/*
|
|
* Opening disk, check if command failed. If it failed
|
|
* close device. Otherwise, it's open.
|
|
*/
|
|
if (dsi->un_openf == OPENING) {
|
|
if (error == SE_NO_ERROR) {
|
|
/*DPRINTF("sdintr: open ok\n");*/
|
|
goto SDINTR_SUCCESS;
|
|
} else if (error == SE_RETRYABLE) {
|
|
DPRINTF("sdintr: open failed\n");
|
|
bp->b_error = EIO;
|
|
} else {
|
|
DPRINTF("sdintr: hardware failure\n");
|
|
bp->b_error = ENXIO;
|
|
}
|
|
un->un_present = OFFLINE;
|
|
bp->b_flags |= B_ERROR;
|
|
goto SDINTR_WRAPUP;
|
|
}
|
|
|
|
/*
|
|
* Rezero for failed command done, retry failed command
|
|
*/
|
|
if ((dsi->un_openf == RETRYING) &&
|
|
(un->un_cdb.cmd == SC_REZERO_UNIT)) {
|
|
|
|
if (error)
|
|
log(LOG_ERR, "sd%d: rezero failed\n", SDNUM(un));
|
|
|
|
DPRINTF("sdintr: rezero done\n");
|
|
sdrestore_cmd(dsi, un, &resid);
|
|
sdmkcdb(un);
|
|
(*c->c_ss->scs_cmd)(c, un, 1);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Command failed, need to run request sense command.
|
|
*/
|
|
if ((dsi->un_openf == OPEN) && error) {
|
|
sdintr_sense(dsi, un, resid);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Request sense command done, restore failed command.
|
|
*/
|
|
if (dsi->un_openf == SENSING) {
|
|
DPRINTF("sdintr: restoring sense\n");
|
|
sdintr_ran_sense(un, dsi, &resid);
|
|
}
|
|
|
|
EPRINTF2("sdintr: retries= %d restores= %d ",
|
|
dsi->un_retries, dsi->un_restores);
|
|
EPRINTF1("state= %d ", dsi->un_openf);
|
|
EPRINTF1("total= %d\n", dsi->un_total_errors);
|
|
if ((dsi->un_openf == RETRYING) && (error == 0)) {
|
|
EPRINTF("sdintr: ok\n\n");
|
|
dsi->un_openf = OPEN;
|
|
dsi->un_retries = dsi->un_restores = 0;
|
|
dsi->un_err_severe = DK_RECOVERED;
|
|
goto SDINTR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Process all other errors here
|
|
*/
|
|
EPRINTF2("sdintr: processing error, error= %x chk= %x",
|
|
error, un->un_scb.chk);
|
|
EPRINTF1(" busy= %x", un->un_scb.busy);
|
|
EPRINTF2(" resid= %d (%d)\n", resid, un->un_dma_count);
|
|
switch (sderror(c, un, dsi, bp, &resid, error)) {
|
|
case real_error:
|
|
/* This error is FATAL ! */
|
|
DPRINTF("sdintr: real error\n");
|
|
dsi->un_retries = dsi->un_restores = 0;
|
|
bp->b_flags |= B_ERROR;
|
|
goto SDINTR_WRAPUP;
|
|
|
|
case psuedo_error:
|
|
/* A psuedo-error: soft error reported by ctlr */
|
|
DPRINTF("sdintr: psuedo error\n");
|
|
status = dsi->un_status;
|
|
error_code = dsi->un_err_code;
|
|
severe = dsi->un_err_severe;
|
|
dsi->un_retries = dsi->un_restores = 0;
|
|
goto SDINTR_SUCCESS;
|
|
|
|
case more_processing:
|
|
/* real error requiring error recovery */
|
|
DPRINTF("stintr: more processing\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle successful Transfer. Also, take care of ACB-4000
|
|
* seek error problem by doing single sector I/O.
|
|
*/
|
|
SDINTR_SUCCESS:
|
|
dsi->un_status = status;
|
|
dsi->un_err_code = error_code;
|
|
dsi->un_err_severe = severe;
|
|
|
|
if (dsi->un_sec_left) {
|
|
EPRINTF("sdintr: single sector writes\n");
|
|
dsi->un_sec_left--;
|
|
un->un_baddr += SECSIZE;
|
|
un->un_blkno++;
|
|
sdmkcdb(un);
|
|
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0)
|
|
log(LOG_ERR, sector_error, SDNUM(un));
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle I/O request completion (both sucessful and failed).
|
|
*/
|
|
SDINTR_WRAPUP:
|
|
if (bp == &un->un_sbuf &&
|
|
((un->un_flags & SC_UNF_DVMA) == 0)) {
|
|
bp->b_resid = 0;
|
|
if (un->un_flags & SC_UNF_SPECIAL_DVMA)
|
|
mbudone(un->un_md);
|
|
else
|
|
(*c->c_ss->scs_done)(un->un_md);
|
|
|
|
} else {
|
|
bp->b_resid = bp->b_bcount - (un->un_count << SECDIV);
|
|
mbudone(un->un_md);
|
|
un->un_flags &= ~SC_UNF_DVMA;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Disk error decoder/handler.
|
|
*/
|
|
static sdintr_error_resolution
|
|
sderror(c, un, dsi, bp, resid, error)
|
|
register struct scsi_ctlr *c;
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
struct buf *bp;
|
|
register int *resid;
|
|
int error;
|
|
{
|
|
register struct scsi_ext_sense *ssd =
|
|
(struct scsi_ext_sense *)dsi->un_sense;
|
|
/* DPRINTF("sderror:\n"); */
|
|
|
|
/*
|
|
* Special processing for driver command timeout errors.
|
|
*/
|
|
if (error == SE_TIMEOUT) {
|
|
EPRINTF("sderror: command timeout error\n");
|
|
dsi->un_status = SC_TIMEOUT;
|
|
dsi->un_err_code = 0;
|
|
goto SDERROR_RETRY;
|
|
}
|
|
/*
|
|
* Special processing for reassign block operations
|
|
*/
|
|
if (dsi->un_openf == MAPPING || dsi->un_openf == MAPPING_BAD ||
|
|
dsi->un_openf == REWRITING) {
|
|
return (sdintr_ran_reassign(c, un, dsi, bp, resid));
|
|
}
|
|
|
|
/*
|
|
* Check for Adaptec ACB-4000 seek error problem. If found,
|
|
* transfer data one sector at a time.
|
|
*/
|
|
if (dsi->un_ctype == CTYPE_ACB4000 && un->un_scb.chk &&
|
|
ssd->error_code == 15 && un->un_count > 1) {
|
|
EPRINTF("sderror: seek error\n");
|
|
dsi->un_sec_left = un->un_count - 1;
|
|
un->un_count = 1;
|
|
sdmkcdb(un);
|
|
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0) {
|
|
log(LOG_ERR, sector_error, SDNUM(un));
|
|
return (real_error);
|
|
}
|
|
return (more_processing);
|
|
}
|
|
|
|
/*
|
|
* Check for various check condition errors.
|
|
*/
|
|
dsi->un_total_errors++;
|
|
if (un->un_scb.chk) {
|
|
|
|
switch (ssd->key) {
|
|
case SC_RECOVERABLE_ERROR:
|
|
dsi->un_err_severe = DK_CORRECTED;
|
|
if (sd_fix_block(c, dsi, un, *resid,
|
|
SD_RECOVERABLE) == more_processing) {
|
|
return (more_processing);
|
|
}
|
|
if (sd_retry == 0)
|
|
sderrmsg(un, bp, "recoverable", LOG_ERR, NULL);
|
|
|
|
if (dsi->un_options & SD_DIAGNOSE)
|
|
return (real_error);
|
|
|
|
return (psuedo_error);
|
|
|
|
case SC_MEDIUM_ERROR:
|
|
EPRINTF("sderror: media error\n");
|
|
goto SDERROR_RETRY;
|
|
|
|
case SC_HARDWARE_ERROR:
|
|
EPRINTF("sderror: hardware error\n");
|
|
goto SDERROR_RETRY;
|
|
|
|
case SC_UNIT_ATTENTION:
|
|
/*
|
|
* If fixed, must be power glitch.
|
|
* If removable, the disk has been changed.
|
|
*/
|
|
if (dsi->un_options & SD_REMOVABLE) {
|
|
EPRINTF("sderror: media changed\n");
|
|
un->un_present = MEDIA_CHANGED;
|
|
dsi->un_openf = CLOSED;
|
|
dsi->un_err_severe = DK_FATAL;
|
|
bp->b_error = ENXIO;
|
|
return (real_error);
|
|
}
|
|
EPRINTF("sderror: unit attention error\n");
|
|
goto SDERROR_RETRY;
|
|
|
|
case SC_NOT_READY:
|
|
/*
|
|
* If fixed, must be power glitch.
|
|
* If removable, the disk has been removed.
|
|
*/
|
|
if (dsi->un_options & SD_REMOVABLE) {
|
|
EPRINTF("sderror: offline\n");
|
|
un->un_present = MEDIA_CHANGED;
|
|
dsi->un_openf = CLOSED;
|
|
dsi->un_err_severe = DK_FATAL;
|
|
bp->b_error = ENXIO;
|
|
return (real_error);
|
|
}
|
|
EPRINTF("sderror: not ready\n");
|
|
goto SDERROR_RETRY;
|
|
|
|
case SC_ILLEGAL_REQUEST:
|
|
EPRINTF("sderror: illegal request\n");
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
|
|
case SC_VOLUME_OVERFLOW:
|
|
EPRINTF("sderror: volume overflow\n");
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
|
|
case SC_WRITE_PROTECT:
|
|
EPRINTF("sderror: write protected\n");
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
|
|
case SC_BLANK_CHECK:
|
|
EPRINTF("sderror: blank check\n");
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
|
|
default:
|
|
/*
|
|
* Undecoded sense key. Try retries and hope
|
|
* that will fix the problem. Otherwise, we're
|
|
* dead.
|
|
*/
|
|
DPRINTF1("sderror: sense key error (0x%x)\n",
|
|
ssd->key);
|
|
SD_PRINT_CMD(un);
|
|
dsi->un_err_severe = DK_FATAL;
|
|
goto SDERROR_RETRY;
|
|
}
|
|
|
|
/*
|
|
* Process reservation error. Abort operation.
|
|
*/
|
|
} else if (un->un_scb.busy && un->un_scb.is) {
|
|
EPRINTF1("sd%d: sderror: reservation conflict error\n",
|
|
SDNUM(un));
|
|
dsi->un_status = SC_RESERVATION;
|
|
dsi->un_err_code = 0;
|
|
bp->b_error = EPERM;
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
|
|
/*
|
|
* Process busy error. Try retries and hope that
|
|
* it'll be ready soon. Note, this test must follow
|
|
* the reservation conflict error test.
|
|
*/
|
|
} else if (un->un_scb.busy) {
|
|
EPRINTF1("sd%d: busy\n", SDNUM(un));
|
|
dsi->un_retries = 0;
|
|
if (++dsi->un_restores > sd_max_busy) {
|
|
dsi->un_retries = sd_max_retries;
|
|
dsi->un_restores = sd_max_restores;
|
|
}
|
|
dsi->un_status = SC_BUSY;
|
|
dsi->un_err_code = 0;
|
|
goto SDERROR_RETRY;
|
|
|
|
/*
|
|
* Have left over residue data from last command.
|
|
* If retries enabled, try a couple of times and see
|
|
* if it's fixable. Otherwise, return residue.
|
|
*/
|
|
} else if (*resid != 0) {
|
|
EPRINTF2("sderror: residue error %d(%d)\n",
|
|
*resid, un->un_dma_count);
|
|
dsi->un_err_resid = bp->b_resid = *resid;
|
|
dsi->un_status = SC_RESIDUE;
|
|
dsi->un_err_code = 0;
|
|
SD_PRINT_CMD(un);
|
|
goto SDERROR_RETRY;
|
|
|
|
/*
|
|
* Have an unknown error. Don't know what went wrong.
|
|
* Do retries and hope this fixes it...
|
|
*/
|
|
} else {
|
|
EPRINTF("sderror: unknown error\n");
|
|
SD_PRINT_CMD(un);
|
|
dsi->un_status = SC_FATAL;
|
|
dsi->un_err_code = 0;
|
|
dsi->un_err_severe = DK_FATAL;
|
|
goto SDERROR_RETRY;
|
|
}
|
|
|
|
/*
|
|
* Process command retries and rezeros here.
|
|
* Note, for on-line formatting, normal error
|
|
* recovery is inhibited.
|
|
*/
|
|
SDERROR_RETRY:
|
|
if (dsi->un_options & SD_NORETRY) {
|
|
EPRINTF("sderror: error recovery disabled\n");
|
|
/* If no error pending, invent one. */
|
|
if (! dsi->un_status) {
|
|
dsi->un_status = SC_FATAL;
|
|
dsi->un_err_code = 0;
|
|
}
|
|
dsi->un_options &= ~SD_NORETRY;
|
|
sderrmsg(un, bp, "failed, no retries", LOG_ERR, NULL);
|
|
if (dsi->un_status == SC_RESIDUE)
|
|
return (psuedo_error);
|
|
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
}
|
|
|
|
/*
|
|
* Command failed, retry it.
|
|
*/
|
|
if (dsi->un_retries++ < sd_max_retries) {
|
|
if (dsi->un_retries > sd_retry ||
|
|
(dsi->un_restores > 0 && !un->un_scb.busy)) {
|
|
sderrmsg(un, bp, "retry", LOG_ERR, NULL);
|
|
}
|
|
dsi->un_openf = RETRYING;
|
|
sdmkcdb(un);
|
|
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0) {
|
|
log(LOG_ERR, "sd%d: retry failed\n", SDNUM(un));
|
|
return (real_error);
|
|
}
|
|
return (more_processing);
|
|
|
|
/*
|
|
* Retries exhausted, try restore
|
|
*/
|
|
} else if (++dsi->un_restores < sd_max_restores) {
|
|
if (dsi->un_restores > sd_restores)
|
|
sderrmsg(un, bp, "restore", LOG_ERR, NULL);
|
|
|
|
sdsave_cmd(un, *resid);
|
|
dsi->un_openf = RETRYING;
|
|
dsi->un_retries = 0;
|
|
un->un_cmd = SC_REZERO_UNIT;
|
|
sdmkcdb(un);
|
|
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
|
return (more_processing);
|
|
|
|
/*
|
|
* Restores and retries exhausted, die!
|
|
*/
|
|
} else {
|
|
/* complete failure */
|
|
sderrmsg(un, bp, "failed", LOG_ERR, LOG_ALERT);
|
|
dsi->un_openf = OPEN;
|
|
dsi->un_retries = 0;
|
|
dsi->un_restores = 0;
|
|
if (ssd->key == SC_MEDIUM_ERROR && (sd_repair & 0x04) &&
|
|
sd_fix_block(c, dsi, un, *resid, SD_REMAP)
|
|
== more_processing) {
|
|
return (more_processing);
|
|
}
|
|
if (dsi->un_status == SC_RESIDUE)
|
|
return (psuedo_error);
|
|
|
|
dsi->un_err_severe = DK_FATAL;
|
|
return (real_error);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Command failed, need to run a request sense command to determine why.
|
|
*/
|
|
static
|
|
sdintr_sense(dsi, un, resid)
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_unit *un;
|
|
int resid;
|
|
{
|
|
/*
|
|
* Note that s?start will call sdmkcdb, which
|
|
* will notice that the flag is set and not do
|
|
* the copy of the cdb, doing a request sense
|
|
* rather than the normal command.
|
|
*/
|
|
DPRINTF("sdintr: getting sense\n");
|
|
sdsave_cmd(un, resid);
|
|
dsi->un_openf = SENSING;
|
|
un->un_cmd = SC_REQUEST_SENSE;
|
|
(*un->un_c->c_ss->scs_go)(un->un_md);
|
|
}
|
|
|
|
|
|
/*
|
|
* Cleanup after running request sense command to see why the real
|
|
* command failed.
|
|
*/
|
|
static
|
|
sdintr_ran_sense(un, dsi, resid)
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
int *resid;
|
|
{
|
|
register struct scsi_ext_sense *ssd =
|
|
(struct scsi_ext_sense *)dsi->un_sense;
|
|
#ifdef lint
|
|
resid = 0;
|
|
#endif lint
|
|
|
|
/* Special processing for Adaptec ACB-4000 disk controller. */
|
|
if (dsi->un_ctype == CTYPE_ACB4000 || ssd->type != 0x70)
|
|
sdintr_adaptec(dsi);
|
|
dsi->un_status = ssd->key;
|
|
dsi->un_err_code = ssd->error_code;
|
|
|
|
/*
|
|
* Check if request sense command failed. This should never happen!
|
|
*/
|
|
if (un->un_scb.chk) {
|
|
/* If no error already pending, invent one. */
|
|
if (! dsi->un_status) {
|
|
dsi->un_status = SC_FATAL;
|
|
dsi->un_err_code = 0;
|
|
}
|
|
log(LOG_ERR, "sd%d: request sense failed\n", SDNUM(un));
|
|
}
|
|
|
|
/* Log error information. */
|
|
sdrestore_cmd(dsi, un, resid);
|
|
dsi->un_err_resid = *resid;
|
|
dsi->un_openf = OPEN;
|
|
|
|
dsi->un_err_blkno = (ssd->info_1 << 24) | (ssd->info_2 << 16) |
|
|
(ssd->info_3 << 8) | ssd->info_4;
|
|
if (dsi->un_err_blkno == 0 || !(ssd->adr_val)) {
|
|
/* No supplied block number, use original value */
|
|
EPRINTF("sdintr_ran_sense: synthesizing block number\n");
|
|
dsi->un_err_blkno = un->un_blkno;
|
|
}
|
|
|
|
#ifdef SDDEBUG
|
|
/* dump sense info on screen */
|
|
if (sd_debug > 1)
|
|
sd_error_message(un, dsi);
|
|
#endif SDDEBUG
|
|
}
|
|
|
|
|
|
/*
|
|
* Cleanup after running request sense command to see why the real
|
|
* command failed.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static sdintr_error_resolution
|
|
sd_fix_block(c, dsi, un, resid, err_type)
|
|
register struct scsi_ctlr *c;
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_unit *un;
|
|
int resid;
|
|
short err_type;
|
|
{
|
|
register int i;
|
|
register int block = dsi->un_err_blkno;
|
|
short flag = err_type;
|
|
|
|
#ifdef lint
|
|
c = c; dsi = dsi;
|
|
#endif lint
|
|
|
|
/*
|
|
* If last command wasn't a read or write, don't perform
|
|
* auto-repair function. Also, if format override in
|
|
* effect, inhibit auto-repair.
|
|
*/
|
|
if ((un->un_cmd != SC_READ && un->un_cmd != SC_WRITE &&
|
|
un->un_cmd != SC_WRITE_VERIFY) ||
|
|
dsi->un_options & SD_DIAGNOSE)
|
|
return (psuedo_error);
|
|
|
|
if (err_type == SD_RECOVERABLE) {
|
|
if (dsi->un_bad_index == 0)
|
|
goto SD_SAVE;
|
|
/*
|
|
* Search bad block log for match. If found,
|
|
* increment counter. If reporting theshold passed,
|
|
* inform user. If remap threshold passed, reassign
|
|
* block. Otherwise, just log it.
|
|
*/
|
|
for (i = 0; i < dsi->un_bad_index; i++) {
|
|
|
|
/* Didn't find it, try next entry */
|
|
if (dsi->un_bad[i].block != dsi->un_err_blkno)
|
|
continue;
|
|
|
|
/* Found it, warn user of number of retries */
|
|
if (++dsi->un_bad[i].retries >= sd_fails) {
|
|
log(LOG_ERR, "sd%d: warning, block %u has failed %d times\n",
|
|
SDNUM(un), block,
|
|
dsi->un_bad[i].retries);
|
|
}
|
|
/* Found it, check if we need to rewrite block */
|
|
if (dsi->un_bad[i].retries >= sd_min_fails &&
|
|
(sd_repair & 0x01)) {
|
|
log(LOG_ERR, "sd%d: rewriting block %u\n",
|
|
SDNUM(un), block);
|
|
flag = SD_REWRITE;
|
|
}
|
|
/* Found it, check if we need to reassign block */
|
|
if (dsi->un_bad[i].retries >= sd_max_fails &&
|
|
(sd_repair & 0x02)) {
|
|
dsi->un_bad[i].retries = 0;
|
|
flag = SD_REMAP;
|
|
}
|
|
goto SD_FIX_BLK;
|
|
}
|
|
/*
|
|
* Table overflow, looks serious. Disable auto-repair.
|
|
* as the disk is failing.
|
|
*/
|
|
SD_SAVE:
|
|
if (dsi->un_bad_index == NBAD) {
|
|
log(LOG_ALERT, overflow_error, SDNUM(un));
|
|
sd_repair = 0;
|
|
return (psuedo_error);
|
|
|
|
/* Block not in table, add it */
|
|
} else {
|
|
EPRINTF("si_fix_block: logging marginal block\n");
|
|
i = dsi->un_bad_index++;
|
|
dsi->un_bad[i].block = dsi->un_err_blkno;
|
|
dsi->un_bad[i].retries = 1;
|
|
return (psuedo_error);
|
|
}
|
|
}
|
|
|
|
SD_FIX_BLK:
|
|
/* If rewrite threshold reached, rewrite block */
|
|
if (flag == SD_REWRITE) {
|
|
EPRINTF("si_fix_block: rewriting block\n");
|
|
sdsave_cmd(un, resid);
|
|
dsi->un_openf = REWRITING;
|
|
un->un_cmd = SC_WRITE;
|
|
sdmkcdb(un);
|
|
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
|
return (more_processing);
|
|
|
|
/* if hard failure, reassign bad block */
|
|
} else if (flag == SD_REMAP && dsi->un_ctype != CTYPE_ACB4000) {
|
|
sdsave_cmd(un, resid);
|
|
if (err_type == SD_REMAP) {
|
|
dsi->un_openf = MAPPING_BAD;
|
|
log(LOG_ERR, reassign_error, SDNUM(un), "defective", block);
|
|
} else {
|
|
dsi->un_openf = MAPPING;
|
|
log(LOG_ERR, reassign_error, SDNUM(un), "marginal", block);
|
|
}
|
|
un->un_cmd = SC_REASSIGN_BLOCK;
|
|
un->un_scratch_addr = (caddr_t) un->un_blkno;
|
|
un->un_blkno = block;
|
|
sdmkcdb(un);
|
|
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
|
return (more_processing);
|
|
}
|
|
return (psuedo_error);
|
|
}
|
|
|
|
|
|
/*
|
|
* Cleanup after running reassign block command.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static sdintr_error_resolution
|
|
sdintr_ran_reassign(c, un, dsi, bp, resid)
|
|
register struct scsi_ctlr *c;
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
struct buf *bp;
|
|
int *resid;
|
|
{
|
|
DPRINTF("sdintr_ran_reassign:\n");
|
|
if (dsi->un_openf == MAPPING_BAD) {
|
|
/* If reassign fails, get the reason why */
|
|
if (un->un_cmd == SC_REASSIGN_BLOCK && un->un_scb.chk) {
|
|
EPRINTF("sd_ran_reassign: reassign failed\n");
|
|
un->un_cmd = SC_REQUEST_SENSE;
|
|
sdmkcdb(un);
|
|
(*un->un_c->c_ss->scs_go)(un->un_md);
|
|
return (more_processing);
|
|
|
|
/* After getting reassign status, display it and quit */
|
|
} else if (un->un_cmd == SC_REQUEST_SENSE) {
|
|
EPRINTF("sd_ran_reassign: request sense\n");
|
|
un->un_cmd = SC_REASSIGN_BLOCK;
|
|
sderrmsg(un, bp, "failed", LOG_ALERT, NULL);
|
|
|
|
/* If reassign ok, zero out the block */
|
|
} else if (un->un_cmd == SC_REASSIGN_BLOCK) {
|
|
EPRINTF("sd_ran_reassign: zeroing block\n");
|
|
sdrestore_cmd(dsi, un, resid);
|
|
sdsave_cmd(un, *resid);
|
|
un->un_cmd = SC_ZERO_WRITE;
|
|
sdmkcdb(un);
|
|
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
|
return (more_processing);
|
|
|
|
/* If zero block failed, notify user. */
|
|
} else if (un->un_cmd == SC_WRITE && un->un_scb.chk) {
|
|
log(LOG_ERR, clear_msg, SDNUM(un));
|
|
}
|
|
dsi->un_retries = dsi->un_restores = 0;
|
|
sdrestore_cmd(dsi, un, resid);
|
|
dsi->un_openf = OPEN;
|
|
return (real_error);
|
|
|
|
} else if (dsi->un_openf == MAPPING) {
|
|
/* If reassign fails, get the reason why. */
|
|
if (un->un_cmd == SC_REASSIGN_BLOCK && un->un_scb.chk) {
|
|
EPRINTF("sd_ran_reassign: reassign failed\n");
|
|
un->un_cmd = SC_REQUEST_SENSE;
|
|
sdmkcdb(un);
|
|
(*un->un_c->c_ss->scs_go)(un->un_md);
|
|
return (more_processing);
|
|
|
|
/* After getting reassign status, display it. */
|
|
} else if (un->un_cmd == SC_REQUEST_SENSE) {
|
|
EPRINTF("sd_ran_reassign: request sense\n");
|
|
un->un_cmd = SC_REASSIGN_BLOCK;
|
|
sderrmsg(un, bp, "failed", LOG_ALERT, NULL);
|
|
}
|
|
/* After reassign, write back data. */
|
|
if (un->un_cmd == SC_REASSIGN_BLOCK && un->un_scb.chk == 0) {
|
|
EPRINTF("sd_ran_reassign: write back\n");
|
|
sdrestore_cmd(dsi, un, resid);
|
|
sdsave_cmd(un, *resid);
|
|
un->un_blkno = (int) un->un_scratch_addr;
|
|
un->un_cmd = SC_WRITE;
|
|
sdmkcdb(un);
|
|
dsi->un_openf = REWRITING;
|
|
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
|
return (more_processing);
|
|
/* continued below */
|
|
}
|
|
|
|
} else if (dsi->un_openf == REWRITING) {
|
|
/* If write back failed, notify user. */
|
|
if (un->un_cmd == SC_WRITE && un->un_scb.chk) {
|
|
EPRINTF("sd_ran_reassign: rewrite failed\n");
|
|
un->un_cmd = SC_REQUEST_SENSE;
|
|
sdmkcdb(un);
|
|
(*un->un_c->c_ss->scs_go)(un->un_md);
|
|
return (more_processing);
|
|
|
|
/* After getting rewrite status, display it. */
|
|
} else if (un->un_cmd == SC_REQUEST_SENSE) {
|
|
EPRINTF("sd_ran_reassign: request sense\n");
|
|
un->un_cmd = SC_WRITE;
|
|
sderrmsg(un, bp, "back failed", LOG_ALERT, NULL);
|
|
}
|
|
}
|
|
sdrestore_cmd(dsi, un, resid);
|
|
dsi->un_openf = OPEN;
|
|
return (psuedo_error);
|
|
}
|
|
|
|
|
|
/*
|
|
* Save the current command and state away.
|
|
*/
|
|
/*ARGSUSED*/
|
|
sdsave_cmd(un, resid)
|
|
register struct scsi_unit *un;
|
|
int resid;
|
|
{
|
|
un->un_saved_cmd.saved_scb = un->un_scb;
|
|
un->un_saved_cmd.saved_cdb = un->un_cdb;
|
|
un->un_saved_cmd.saved_resid = resid;
|
|
un->un_saved_cmd.saved_dma_addr = un->un_dma_addr;
|
|
un->un_saved_cmd.saved_dma_count = un->un_dma_count;
|
|
un->un_flags |= SC_UNF_GET_SENSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Restore the saved command and state.
|
|
*/
|
|
/*ARGSUSED*/
|
|
sdrestore_cmd(dsi, un, resid_ptr)
|
|
register struct scsi_disk *dsi;
|
|
register struct scsi_unit *un;
|
|
int *resid_ptr;
|
|
{
|
|
un->un_scb = un->un_saved_cmd.saved_scb;
|
|
un->un_cdb = un->un_saved_cmd.saved_cdb;
|
|
*resid_ptr = un->un_saved_cmd.saved_resid;
|
|
un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
|
|
un->un_dma_count = un->un_saved_cmd.saved_dma_count;
|
|
un->un_flags &= ~SC_UNF_GET_SENSE;
|
|
un->un_cmd = dsi->un_last_cmd;
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine performs raw read operations. It is called from the
|
|
* device switch at normal priority. It uses a per-unit buffer for the
|
|
* operation.
|
|
*/
|
|
/*ARGSUSED*/
|
|
sdread(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
register int unit = SDUNIT(dev);
|
|
|
|
/*DPRINTF("sdread:\n");*/
|
|
if (unit >= nsdisk) {
|
|
EPRINTF("sdread: invalid unit\n");
|
|
return (ENXIO);
|
|
}
|
|
/*
|
|
* The following tests assume a block size which is power of 2.
|
|
* This allows a bit mask operation to be used instead of a
|
|
* divide operation thus saving considerable time.
|
|
*/
|
|
if ((uio->uio_fmode & FSETBLK) == 0 &&
|
|
(uio->uio_offset & (DEV_BSIZE - 1))) {
|
|
EPRINTF1("sdread: block address not modulo %d\n", DEV_BSIZE);
|
|
return (EINVAL);
|
|
}
|
|
if (uio->uio_iov->iov_len & (DEV_BSIZE - 1)) {
|
|
EPRINTF1("sdread: block length not modulo %d\n", DEV_BSIZE);
|
|
return (EINVAL);
|
|
}
|
|
return (physio(sdstrategy, (struct buf *)NULL, dev, B_READ, minphys, uio));
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine performs raw write operations. It is called from the
|
|
* device switch at normal priority. It uses a per-unit buffer for the
|
|
* operation.
|
|
*/
|
|
sdwrite(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
register int unit = SDUNIT(dev);
|
|
|
|
/*DPRINTF("sdwrite:\n");*/
|
|
if (unit >= nsdisk) {
|
|
EPRINTF("sdwrite: invalid unit\n");
|
|
return (ENXIO);
|
|
}
|
|
if ((uio->uio_fmode & FSETBLK) == 0 &&
|
|
(uio->uio_offset & (DEV_BSIZE - 1))) {
|
|
EPRINTF1("sdwrite: block address not modulo %d\n", DEV_BSIZE);
|
|
return (EINVAL);
|
|
}
|
|
if (uio->uio_iov->iov_len & (DEV_BSIZE - 1)) {
|
|
EPRINTF1("sdwrite: block length not modulo %d\n", DEV_BSIZE);
|
|
return (EINVAL);
|
|
}
|
|
return (physio(sdstrategy, (struct buf *)NULL, dev, B_WRITE, minphys, uio));
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine implements the ioctl calls. It is called
|
|
* from the device switch at normal priority.
|
|
*/
|
|
sdioctl(dev, cmd, data, flag)
|
|
dev_t dev;
|
|
register int cmd;
|
|
register caddr_t data;
|
|
int flag;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
register struct dk_map *lp;
|
|
register struct dk_info *info;
|
|
register struct dk_conf *conf;
|
|
register struct dk_diag *diag;
|
|
register struct dk_loghdr *loghdr;
|
|
register struct dk_log *elog;
|
|
register int unit = SDUNIT(dev);
|
|
int i, j, s;
|
|
|
|
#ifdef lint
|
|
conf = (struct dk_conf *) 0;
|
|
lp = (struct dk_map *) 0;
|
|
info = (struct dk_info *) 0;
|
|
loghdr = (struct dk_loghdr *) 0;
|
|
elog = (struct dk_log *) 0;
|
|
conf = conf; lp = lp; info = info; flag = flag; j = 0; j = j;
|
|
elog = elog; loghdr = loghdr;
|
|
#endif lint
|
|
|
|
/* DPRINTF("sdioctl:\n"); */
|
|
if (unit >= nsdisk) {
|
|
EPRINTF("sdioctl: invalid unit\n");
|
|
return (ENXIO);
|
|
}
|
|
un = &sdunits[unit];
|
|
dsi = &sdisk[unit];
|
|
lp = &dsi->un_map[LPART(dev)];
|
|
switch (cmd) {
|
|
|
|
#ifdef DKIOCGLOG
|
|
/*
|
|
* Return error log info.
|
|
*/
|
|
case DKIOCGLOG:
|
|
EPRINTF("sdioctl: get log info\n");
|
|
loghdr = (struct dk_loghdr *)data;
|
|
loghdr->dkl_entries = j = dsi->un_bad_index;
|
|
|
|
/* If no space or no bad blocks, we're done */
|
|
if (loghdr->dkl_max_size == 0 || dsi->un_bad_index == 0)
|
|
return (0);
|
|
/*
|
|
* Copy error info across.
|
|
*/
|
|
if (loghdr->dkl_entries > loghdr->dkl_max_size)
|
|
j = loghdr->dkl_max_size;
|
|
elog = (struct dk_log *)loghdr->dkl_logbfr;
|
|
for (i = 0; i < j; i++) {
|
|
elog->block = dsi->un_bad[i].block;
|
|
elog->count = dsi->un_bad[i].retries;
|
|
elog->type = DKL_SOFT;
|
|
|
|
/* FIXME: need real error codes */
|
|
elog->err1 = elog->err2 = 0;
|
|
elog++;
|
|
}
|
|
return (0);
|
|
#endif DKIOCGLOG
|
|
|
|
#ifdef DKIOCWCHK
|
|
/*
|
|
* Enable/Disable write verification.
|
|
*/
|
|
case DKIOCWCHK:
|
|
/*
|
|
* The only thing that I am certain this works
|
|
* on is the ACB4000. MD-21's can't do this.
|
|
* Embedded SCSI disks need to be checked.
|
|
*
|
|
*/
|
|
if (dsi->un_ctype != CTYPE_ACB4000)
|
|
return (ENOTTY);
|
|
|
|
if(!lp || lp->dkl_nblk == 0)
|
|
return (ENXIO);
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
|
|
if ((sdwchkmap[unit] & (1<<LPART(dev))) != 0)
|
|
i = 1;
|
|
else
|
|
i = 0;
|
|
|
|
if (*(int *)data)
|
|
sdwchkmap[unit] |= (1<<LPART(dev));
|
|
else
|
|
sdwchkmap[unit] &= ~(1<<LPART(dev));
|
|
(void) splr(s);
|
|
DPRINTF1("sdioctl: write check %s\n",
|
|
((*(int *) data)?"enabled": "disabled"));
|
|
*(int *) data = i;
|
|
return (0);
|
|
#endif DKIOCWCHK
|
|
|
|
/*
|
|
* Return info concerning the controller.
|
|
*/
|
|
case DKIOCINFO:
|
|
DPRINTF("sdioctl: get info\n");
|
|
info = (struct dk_info *)data;
|
|
info->dki_ctlr = getdevaddr(un->un_mc->mc_addr);
|
|
info->dki_unit = un->un_md->md_slave;
|
|
|
|
switch (dsi->un_ctype) {
|
|
case CTYPE_MD21:
|
|
info->dki_ctype = DKC_MD21;
|
|
break;
|
|
case CTYPE_ACB4000:
|
|
info->dki_ctype = DKC_ACB4000;
|
|
break;
|
|
default:
|
|
info->dki_ctype = DKC_MD21;
|
|
break;
|
|
}
|
|
info->dki_flags = DKI_FMTVOL;
|
|
return (0);
|
|
|
|
/*
|
|
* Return the geometry of the specified unit.
|
|
*/
|
|
case DKIOCGGEOM:
|
|
DPRINTF("sdioctl: get geometry\n");
|
|
*(struct dk_geom *)data = dsi->un_g;
|
|
return (0);
|
|
|
|
/*
|
|
* Set the geometry of the specified unit.
|
|
*/
|
|
case DKIOCSGEOM:
|
|
EPRINTF("sdioctl: set geometry\n");
|
|
dsi->un_g = *(struct dk_geom *)data;
|
|
return (0);
|
|
|
|
/*
|
|
* Return the map for the specified logical partition.
|
|
* This has been made obsolete by the get all partitions
|
|
* command.
|
|
*/
|
|
case DKIOCGPART:
|
|
DPRINTF("sdioctl: get partitions\n");
|
|
*(struct dk_map *)data = *lp;
|
|
return (0);
|
|
|
|
/*
|
|
* Set the map for the specified logical partition.
|
|
* This has been made obsolete by the set all partitions
|
|
* command. We raise the priority just to make sure
|
|
* an interrupt doesn't come in while the map is
|
|
* half updated.
|
|
*/
|
|
case DKIOCSPART:
|
|
EPRINTF("sdioctl: set partitions\n");
|
|
*lp = *(struct dk_map *)data;
|
|
return (0);
|
|
|
|
/*
|
|
* Return configuration info
|
|
*/
|
|
case DKIOCGCONF:
|
|
#ifdef SD_FORMAT
|
|
DPRINTF("sdioctl: get configuration info\n");
|
|
conf = (struct dk_conf *)data;
|
|
switch (dsi->un_ctype) {
|
|
case CTYPE_MD21:
|
|
conf->dkc_ctype = DKC_MD21;
|
|
break;
|
|
case CTYPE_ACB4000:
|
|
conf->dkc_ctype = DKC_ACB4000;
|
|
break;
|
|
default:
|
|
conf->dkc_ctype = DKC_MD21;
|
|
break;
|
|
}
|
|
(void) strncpy(conf->dkc_dname, "sd", DK_DEVLEN);
|
|
conf->dkc_flags = DKI_FMTVOL;
|
|
conf->dkc_cnum = un->un_mc->mc_ctlr;
|
|
conf->dkc_addr = getdevaddr(un->un_mc->mc_addr);
|
|
conf->dkc_space = un->un_mc->mc_space;
|
|
conf->dkc_prio = un->un_mc->mc_intpri;
|
|
if (un->un_mc->mc_intr)
|
|
conf->dkc_vec = un->un_mc->mc_intr->v_vec;
|
|
else
|
|
conf->dkc_vec = 0;
|
|
(void) strncpy(conf->dkc_cname, un->un_c->c_name, DK_DEVLEN);
|
|
conf->dkc_unit = un->un_md->md_unit;
|
|
conf->dkc_slave = un->un_md->md_slave;
|
|
#endif SD_FORMAT
|
|
return (0);
|
|
|
|
/*
|
|
* Return the map for all logical partitions.
|
|
*/
|
|
case DKIOCGAPART:
|
|
DPRINTF("sdioctl: get all logical partitions\n");
|
|
for (i = 0; i < NLPART; i++)
|
|
((struct dk_map *)data)[i] = dsi->un_map[i];
|
|
return (0);
|
|
|
|
/*
|
|
* Set the map for all logical partitions. We raise
|
|
* the priority just to make sure an interrupt doesn't
|
|
* come in while the map is half updated.
|
|
*/
|
|
case DKIOCSAPART:
|
|
EPRINTF("sdioctl: set all logical partitions\n");
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
for (i = 0; i < NLPART; i++)
|
|
dsi->un_map[i] = ((struct dk_map *)data)[i];
|
|
(void) splx(s);
|
|
return (0);
|
|
|
|
/*
|
|
* Get error status from last command.
|
|
*/
|
|
case DKIOCGDIAG:
|
|
EPRINTF("sdioctl: get error status\n");
|
|
diag = (struct dk_diag *) data;
|
|
diag->dkd_errcmd = dsi->un_last_cmd;
|
|
diag->dkd_errsect = dsi->un_err_blkno;
|
|
diag->dkd_errno = dsi->un_status;
|
|
/* diag->dkd_errno = dsi->un_err_code; */
|
|
#ifdef SD_FORMAT
|
|
diag->dkd_severe = dsi->un_err_severe;
|
|
#endif SD_FORMAT
|
|
|
|
dsi->un_last_cmd = 0; /* Reset */
|
|
dsi->un_err_blkno = 0;
|
|
dsi->un_err_code = 0;
|
|
dsi->un_err_severe = 0;
|
|
return (0);
|
|
|
|
/*
|
|
* Run a generic command.
|
|
*/
|
|
case DKIOCSCMD:
|
|
#ifdef SD_FORMAT
|
|
/* DPRINTF("sdioctl: run special command\n"); */
|
|
return (sdioctl_cmd(dev, un, dsi, data));
|
|
#endif SD_FORMAT
|
|
|
|
/*
|
|
* Handle unknown ioctls here.
|
|
*/
|
|
default:
|
|
EPRINTF("sdioctl: unknown ioctl\n");
|
|
return (ENOTTY);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Run a command for sdioctl.
|
|
*/
|
|
#ifdef SD_FORMAT
|
|
static
|
|
sdioctl_cmd(dev, un, dsi, data)
|
|
dev_t dev;
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
caddr_t data;
|
|
{
|
|
register struct dk_cmd *com;
|
|
int s, err;
|
|
int options = SD_IOCTL;
|
|
|
|
/* DPRINTF("sdioctl_cmd:\n"); */
|
|
|
|
com = (struct dk_cmd *)data;
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 1) {
|
|
printf("sdioctl_cmd: cmd= %x blk= %x cnt= %x\n",
|
|
com->dkc_cmd, com->dkc_blkno, com->dkc_secnt);
|
|
printf(" buf addr= %x buflen= 0x%x\n",
|
|
com->dkc_bufaddr, com->dkc_buflen);
|
|
}
|
|
#endif SDDEBUG
|
|
/*
|
|
* Set options for no driver messages, no retries, and
|
|
* no auto-repair.
|
|
*/
|
|
if (com->dkc_flags & DK_SILENT) {
|
|
options |= SD_SILENT;
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 0)
|
|
options &= ~SD_SILENT;
|
|
#endif SDDEBUG
|
|
}
|
|
if (com->dkc_flags & DK_ISOLATE)
|
|
options |= SD_NORETRY;
|
|
|
|
if (com->dkc_flags & DK_DIAGNOSE)
|
|
options |= SD_DIAGNOSE;
|
|
|
|
/*
|
|
* Process the special ioctl command.
|
|
*/
|
|
switch (com->dkc_cmd) {
|
|
case SC_READ:
|
|
DPRINTF("sdioctl_cmd: read\n");
|
|
options |= SD_DVMA_RD;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_SPECIAL_READ, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen,
|
|
(caddr_t)com->dkc_bufaddr, options);
|
|
(void) splx(s);
|
|
break;
|
|
|
|
case SC_WRITE:
|
|
DPRINTF("sdioctl_cmd: write\n");
|
|
options |= SD_DVMA_WR;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_SPECIAL_WRITE, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen,
|
|
(caddr_t)com->dkc_bufaddr, options);
|
|
(void) splx(s);
|
|
break;
|
|
|
|
case SC_MODE_SELECT:
|
|
EPRINTF("sdioctl_cmd: mode select\n");
|
|
options |= SD_DVMA_WR;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_MODE_SELECT, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
|
|
options);
|
|
(void) splx(s);
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf(" bufaddr =");
|
|
sd_print_buffer((u_char *)com->dkc_bufaddr,
|
|
(int)com->dkc_buflen);
|
|
}
|
|
#endif SDDEBUG
|
|
break;
|
|
|
|
case SC_MODE_SENSE:
|
|
EPRINTF("sdioctl_cmd: mode sense\n");
|
|
options |= SD_DVMA_RD;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_MODE_SENSE, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
|
|
options);
|
|
(void) splx(s);
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf(" bufaddr =");
|
|
sd_print_buffer((u_char *)com->dkc_bufaddr,
|
|
(int)com->dkc_buflen);
|
|
}
|
|
#endif SDDEBUG
|
|
break;
|
|
|
|
case SC_FORMAT:
|
|
EPRINTF("sdioctl_cmd: format\n");
|
|
options |= SD_DVMA_WR;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_FORMAT, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
|
|
options);
|
|
(void) splx(s);
|
|
break;
|
|
|
|
case SC_READ_DEFECT_LIST:
|
|
EPRINTF("sdioctl_cmd: read defect list\n");
|
|
options |= SD_DVMA_RD;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_READ_DEFECT_LIST, 0,
|
|
(int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
|
|
options);
|
|
(void) splx(s);
|
|
break;
|
|
|
|
case SC_REASSIGN_BLOCK:
|
|
EPRINTF("sdioctl_cmd: reassign block\n");
|
|
options |= SD_NORETRY;
|
|
if (dsi->un_ctype == CTYPE_ACB4000) {
|
|
EPRINTF("sdioctl_cmd: cannot reassign block\n");
|
|
return (EIO);
|
|
}
|
|
err = sdcmd(dev, (int)com->dkc_cmd, (int)com->dkc_blkno,
|
|
(int)0, (caddr_t)0, options);
|
|
break;
|
|
|
|
case SC_INQUIRY:
|
|
EPRINTF("sdioctl_cmd: inquiry\n");
|
|
options |= SD_DVMA_RD;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_INQUIRY, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
|
|
options);
|
|
(void) splx(s);
|
|
bcopy((caddr_t)dsi->un_sense,(caddr_t)com->dkc_bufaddr,
|
|
sizeof (struct scsi_inquiry_data));
|
|
#ifdef SDDEBUG
|
|
if (sd_debug > 2) {
|
|
printf(" bufaddr =");
|
|
sd_print_buffer((u_char *)com->dkc_bufaddr,
|
|
(int)com->dkc_buflen);
|
|
}
|
|
#endif SDDEBUG
|
|
break;
|
|
|
|
case SC_RESERVE:
|
|
case SC_RELEASE:
|
|
EPRINTF("sdioctl_cmd: reserve/release\n");
|
|
options |= SD_NORETRY;
|
|
if (dsi->un_ctype == CTYPE_ACB4000)
|
|
return (0);
|
|
|
|
err = sdcmd(dev, (int)com->dkc_cmd, (int)com->dkc_blkno,
|
|
(int)0, (caddr_t)0, options);
|
|
break;
|
|
|
|
case SC_TRANSLATE:
|
|
EPRINTF("sdioctl_cmd: translate\n");
|
|
options |= SD_DVMA_RD;
|
|
s = splr(pritospl(un->un_mc->mc_intpri));
|
|
err = sdcmd(dev, SC_TRANSLATE, (int)com->dkc_blkno,
|
|
(int)com->dkc_buflen,
|
|
(caddr_t)com->dkc_bufaddr, options);
|
|
(void) splx(s);
|
|
break;
|
|
|
|
default:
|
|
EPRINTF1("sdioctl_cmd: unknown command %x\n", com->dkc_cmd);
|
|
return (EINVAL);
|
|
/* break; */
|
|
}
|
|
if (err) {
|
|
EPRINTF("sdioctl_cmd: ioctl cmd failed\n");
|
|
return (EIO);
|
|
}
|
|
return (0);
|
|
}
|
|
#endif SD_FORMAT
|
|
|
|
|
|
/*
|
|
* This routine dumps memory to the disk. It assumes that the memory has
|
|
* already been mapped into mainbus space. It is called at disk interrupt
|
|
* priority when the system is in trouble.
|
|
*/
|
|
sddump(dev, addr, blkno, nblk)
|
|
register dev_t dev;
|
|
register caddr_t addr;
|
|
/* register daddr_t blkno, nblk; */
|
|
int blkno, nblk;
|
|
{
|
|
register struct scsi_unit *un;
|
|
register struct dk_map *lp;
|
|
static int first_time = 1;
|
|
register struct scsi_disk *dsi;
|
|
|
|
#ifdef lint
|
|
first_time = 0;
|
|
#endif lint
|
|
|
|
un = &sdunits[SDUNIT(dev)];
|
|
dsi = &sdisk[SDUNIT(dev)];
|
|
lp = &dsi->un_map[LPART(dev)];
|
|
if (blkno >= lp->dkl_nblk || (blkno + nblk) > lp->dkl_nblk) {
|
|
EPRINTF("sddump: no place to dump\n");
|
|
return (EINVAL);
|
|
}
|
|
|
|
blkno += lp->dkl_cylno * dsi->un_cyl_size;
|
|
if (first_time) {
|
|
/*
|
|
* After scsi bus reset, it is necessary to
|
|
* handle a unit attention error. If the device
|
|
* fails after that, we assume it's dead and
|
|
* abort dump.
|
|
*/
|
|
(*un->un_c->c_ss->scs_reset)(un->un_c, 0);
|
|
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) &&
|
|
simple(un, SC_TEST_UNIT_READY, 0, 0, 0)) {
|
|
EPRINTF("sddump: dump device offline\n");
|
|
return (ENXIO);
|
|
}
|
|
first_time = 0;
|
|
}
|
|
/*
|
|
* If write fails, try again. After two failures,
|
|
* report failure which kills dump.
|
|
*/
|
|
addr = addr - (int)DVMA;
|
|
if (simple(un, SC_WRITE, (int)addr, blkno, nblk) &&
|
|
simple(un, SC_WRITE, (int)addr, blkno, nblk)) {
|
|
EPRINTF("sddump: write failed\n");
|
|
return (EIO);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
static u_char sd_adaptec_keys[] = {
|
|
0, 4, 4, 4, 2, 4, 4, 4, /* 0x00-0x07 */
|
|
4, 4, 4, 4, 4, 4, 4, 4, /* 0x08-0x0f */
|
|
3, 3, 3, 3, 3, 3, 3, 1, /* 0x10-0x17 */
|
|
1, 1, 5, 5, 1, 1, 1, 1, /* 0x18-0x1f */
|
|
5, 5, 5, 5, 5, 5, 5, 5, /* 0x20-0x27 */
|
|
6, 6, 6, 6, 6, 6, 6, 6, /* 0x28-0x30 */
|
|
};
|
|
#define MAX_ADAPTEC_KEYS \
|
|
(sizeof(sd_adaptec_keys))
|
|
|
|
|
|
static char *sd_adaptec_error_str[] = {
|
|
"no sense", /* 0x00 */
|
|
"no index", /* 0x01 */
|
|
"no seek complete", /* 0x02 */
|
|
"write fault", /* 0x03 */
|
|
"drive not ready", /* 0x04 */
|
|
"drive not selected", /* 0x05 */
|
|
"no track 00", /* 0x06 */
|
|
"multiple drives selected", /* 0x07 */
|
|
"no address", /* 0x08 */
|
|
"no media loaded", /* 0x09 */
|
|
"end of media", /* 0x0a */
|
|
"", /* 0x0b */
|
|
"", /* 0x0c */
|
|
"", /* 0x0d */
|
|
"", /* 0x0e */
|
|
"", /* 0x0f */
|
|
"I.D. CRC error", /* 0x10 */
|
|
"hard data error", /* 0x11 */
|
|
"no I.D. address mark", /* 0x12 */
|
|
"no data address mark", /* 0x13 */
|
|
"record not found", /* 0x14 */
|
|
"seek error", /* 0x15 */
|
|
"DMA timeout error", /* 0x16 */
|
|
"write protected", /* 0x17 */
|
|
"correctable data error", /* 0x18 */
|
|
"bad block", /* 0x19 */
|
|
"interleave error", /* 0x1a */
|
|
"data transfer failed", /* 0x1b */
|
|
"bad format", /* 0x1c */
|
|
"self test failed", /* 0x1d */
|
|
"defective track", /* 0x1e */
|
|
"", /* 0x1f */
|
|
"invalid command", /* 0x20 */
|
|
"illegal block address", /* 0x21 */
|
|
"aborted", /* 0x22 */
|
|
"volume overflow", /* 0x23 */
|
|
"bad argument", /* 0x24 */
|
|
0
|
|
};
|
|
#define MAX_ADAPTEC_ERROR_STR \
|
|
(sizeof(sd_adaptec_error_str)/sizeof(sd_adaptec_error_str[0]))
|
|
|
|
|
|
static char *sd_emulex_error_str[] = {
|
|
"", /* 0x00 */
|
|
"no index", /* 0x01 */
|
|
"seek incomplete", /* 0x02 */
|
|
"write fault", /* 0x03 */
|
|
"drive not ready", /* 0x04 */
|
|
"drive not selected", /* 0x05 */
|
|
"no track 0", /* 0x06 */
|
|
"multiple drives selected", /* 0x07 */
|
|
"lun failure", /* 0x08 */
|
|
"servo error", /* 0x09 */
|
|
"", /* 0x0a */
|
|
"", /* 0x0b */
|
|
"", /* 0x0c */
|
|
"", /* 0x0d */
|
|
"", /* 0x0e */
|
|
"", /* 0x0f */
|
|
"ID error", /* 0x10 */
|
|
"hard data error", /* 0x11 */
|
|
"no addr mark", /* 0x12 */
|
|
"no data field addr mark", /* 0x13 */
|
|
"block not found", /* 0x14 */
|
|
"seek error", /* 0x15 */
|
|
"dma timeout", /* 0x16 */
|
|
"recoverable error", /* 0x17 */
|
|
"soft data error", /* 0x18 */
|
|
"bad block", /* 0x19 */
|
|
"parameter overrun", /* 0x1a */
|
|
"synchronous xfer error", /* 0x1b */
|
|
"no primary defect list", /* 0x1c */
|
|
"compare error", /* 0x1d */
|
|
"recoverable error", /* 0x1e */
|
|
"", /* 0x1f */
|
|
"invalid command", /* 0x20 */
|
|
"invalid block", /* 0x21 */
|
|
"illegal command", /* 0x22 */
|
|
"", /* 0x23 */
|
|
"invalid cdb", /* 0x24 */
|
|
"invalid lun", /* 0x25 */
|
|
"invalid param list", /* 0x26 */
|
|
"write protected", /* 0x27 */
|
|
"media changed", /* 0x28 */
|
|
"reset", /* 0x29 */
|
|
"mode select changed", /* 0x2a */
|
|
0
|
|
};
|
|
#define MAX_EMULEX_ERROR_STR \
|
|
(sizeof(sd_emulex_error_str)/sizeof(sd_emulex_error_str[0]))
|
|
|
|
|
|
static char *sd_key_error_str[] = SENSE_KEY_INFO;
|
|
#define MAX_KEY_ERROR_STR \
|
|
(sizeof(sd_key_error_str)/sizeof(sd_key_error_str[0]))
|
|
|
|
/*
|
|
* scsi command decode table.
|
|
*/
|
|
char *sd_cmds[] = {
|
|
"\010read", /* 0x08 */
|
|
"\012write", /* 0x0a */
|
|
"\003request sense", /* 0x03 */
|
|
"\001rezero", /* 0x01 */
|
|
"\000test unit ready", /* 0x00 */
|
|
"\004format", /* 0x04 */
|
|
"\007reassign", /* 0x07 */
|
|
"\022inquiry", /* 0x12 */
|
|
"\032mode sense", /* 0x1a */
|
|
"\025mode select", /* 0x15 */
|
|
"\067read defect data", /* 0x37 */
|
|
"\026reserve", /* 0x16 */
|
|
"\027release", /* 0x17 */
|
|
"\033start/stop", /* 0x1b */
|
|
"\013seek", /* 0x0b */
|
|
"\036door lock", /* 0x1e */
|
|
"\200read", /* 0x80 */
|
|
"\201read", /* 0x81 */
|
|
"\202write", /* 0x82 */
|
|
"\202zero block", /* 0x83 */
|
|
0 /* terminates table */
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Translate Adaptec non-extended sense status in to
|
|
* extended sense format. In other words, generate
|
|
* sense key.
|
|
*/
|
|
static
|
|
sdintr_adaptec(dsi)
|
|
register struct scsi_disk *dsi;
|
|
{
|
|
register struct scsi_sense *s = (struct scsi_sense *)dsi->un_sense;
|
|
register struct scsi_ext_sense *ssd;
|
|
#ifdef lint
|
|
s = 0;
|
|
#endif lint
|
|
|
|
EPRINTF("sdintr_adaptec:\n");
|
|
/* Reposition failed block number for extended sense. */
|
|
ssd = (struct scsi_ext_sense *)dsi->un_sense;
|
|
ssd->info_1 = 0;
|
|
ssd->info_2 = s->high_addr;
|
|
ssd->info_3 = s->mid_addr;
|
|
ssd->info_4 = s->low_addr;
|
|
|
|
/* Reposition error code for extended sense. */
|
|
ssd->error_code = s->code;
|
|
|
|
/* Synthesize sense key for extended sense. */
|
|
if (s->code < MAX_ADAPTEC_KEYS)
|
|
ssd->key = sd_adaptec_keys[s->code];
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the text string associated with the sense key value.
|
|
*/
|
|
static char *
|
|
sd_print_key(key_code)
|
|
register u_char key_code;
|
|
{
|
|
static char *unknown_key = "unknown key";
|
|
if ((key_code > MAX_KEY_ERROR_STR -1) ||
|
|
sd_key_error_str[key_code] == NULL) {
|
|
|
|
return (unknown_key);
|
|
}
|
|
return (sd_key_error_str[key_code]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the text string associated with the secondary
|
|
* error code, if availiable.
|
|
*/
|
|
static char *
|
|
sd_print_error(dsi, error_code)
|
|
register struct scsi_disk *dsi;
|
|
register u_char error_code;
|
|
{
|
|
static char *unknown_error = " ";
|
|
switch (dsi->un_ctype) {
|
|
case CTYPE_MD21:
|
|
case CTYPE_CCS:
|
|
if ((MAX_EMULEX_ERROR_STR > error_code) &&
|
|
sd_emulex_error_str[error_code] != NULL)
|
|
return (sd_emulex_error_str[error_code]);
|
|
break;
|
|
|
|
case CTYPE_ACB4000:
|
|
if (MAX_ADAPTEC_ERROR_STR > error_code &&
|
|
sd_adaptec_error_str[error_code] != NULL)
|
|
return (sd_adaptec_error_str[error_code]);
|
|
break;
|
|
}
|
|
return (unknown_error);
|
|
}
|
|
|
|
|
|
/*
|
|
* Print the sense key and secondary error codes
|
|
* and dump out the sense bytes.
|
|
*/
|
|
#ifdef SDDEBUG
|
|
static
|
|
sd_error_message(un, dsi)
|
|
register struct scsi_unit *un;
|
|
register struct scsi_disk *dsi;
|
|
{
|
|
register struct scsi_ext_sense *ssd;
|
|
register u_char *cp = (u_char *)dsi->un_sense;
|
|
register int i;
|
|
|
|
/* If error messages are being suppressed, exit. */
|
|
if (dsi->un_options & SD_SILENT)
|
|
return;
|
|
|
|
if (dsi->un_err_code) {
|
|
printf(sense_msg2, un-sdunits,
|
|
dsi->un_status, sd_print_key(dsi->un_status),
|
|
dsi->un_err_code,
|
|
sd_print_error(dsi, dsi->un_err_code));
|
|
} else {
|
|
printf(sense_msg1, un-sdunits,
|
|
dsi->un_status, sd_print_key(dsi->un_status));
|
|
}
|
|
|
|
printf(" sense =");
|
|
for (i = 0; i < sizeof (struct scsi_ext_sense); i++)
|
|
printf(" %x", *cp++);
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
static
|
|
sd_print_cmd(un)
|
|
register struct scsi_unit *un;
|
|
{
|
|
register int x;
|
|
register u_char *y = (u_char *)&(un->un_cdb);
|
|
#ifdef lint
|
|
un = un; x = 0; y = NULL;
|
|
#endif lint
|
|
|
|
printf("sd%d: failed cmd =", SDUNIT(un->un_unit));
|
|
for (x = 0; x < CDB_GROUP1; x++)
|
|
printf(" %x", *y++);
|
|
printf("\n");
|
|
}
|
|
#endif SDDEBUG
|
|
|
|
|
|
static
|
|
sderrmsg(un, bp, action, log_priority, log_error)
|
|
struct scsi_unit *un;
|
|
struct buf *bp;
|
|
char *action;
|
|
int log_priority, log_error;
|
|
{
|
|
register struct dk_map *lp;
|
|
register struct scsi_disk *dsi;
|
|
char partition = NULL;
|
|
char **cmdtbl = sd_cmds;
|
|
char *cmdname = "<unknown cmd>";
|
|
u_int blkno = 0;
|
|
|
|
dsi = &sdisk[dkunit(bp)];
|
|
#ifdef lint
|
|
log_priority = log_priority;
|
|
#endif lint
|
|
|
|
/* If error messages are being suppressed, exit. */
|
|
if (dsi->un_options & SD_SILENT)
|
|
return;
|
|
|
|
/*
|
|
* If normal I/O, get relative block and partition info.
|
|
* Note, if error not a drive error (check condition),
|
|
* then the block number is invalid.
|
|
*/
|
|
if ((dsi->un_options & SD_IOCTL) == 0) {
|
|
partition = LPART(bp->b_dev) + 'a';
|
|
lp = &dsi->un_map[LPART(bp->b_dev)];
|
|
blkno = dsi->un_err_blkno;
|
|
blkno -= lp->dkl_cylno * dsi->un_cyl_size;
|
|
}
|
|
|
|
/* Decode command name */
|
|
while (*cmdtbl != (char *) NULL) {
|
|
if ((u_char)un->un_cmd == (u_char) *cmdtbl[0]) {
|
|
cmdname = (char *)((int)(*cmdtbl)+1);
|
|
break;
|
|
}
|
|
cmdtbl++;
|
|
}
|
|
|
|
/* Report command and location of failure. */
|
|
if (un->un_scb.chk && dsi->un_err_blkno != 0) {
|
|
log(log_priority, disk_error2, SDNUM(un), partition,
|
|
cmdname, action, dsi->un_err_blkno, blkno);
|
|
} else {
|
|
log(log_priority, disk_error1, SDNUM(un), partition,
|
|
cmdname, action);
|
|
}
|
|
|
|
/* Report error code (e.g. sense key and error code). */
|
|
if (un->un_scb.chk && dsi->un_err_code) {
|
|
log(log_priority, sense_msg2, un-sdunits, partition,
|
|
dsi->un_status, sd_print_key(dsi->un_status),
|
|
dsi->un_err_code,
|
|
sd_print_error(dsi, dsi->un_err_code));
|
|
} else if (un->un_scb.chk && dsi->un_err_code == 0) {
|
|
log(log_priority, sense_msg1, un-sdunits, partition,
|
|
dsi->un_status, sd_print_key(dsi->un_status));
|
|
}
|
|
|
|
/* Special hard error reporting. */
|
|
if (log_error && un->un_scb.chk && dsi->un_err_blkno != 0) {
|
|
log(log_error, hard_error, SDNUM(un), partition,
|
|
dsi->un_err_blkno, blkno);
|
|
}
|
|
}
|
|
#endif NSD > 0
|