Commit fd0a1c61 authored by James Bottomley's avatar James Bottomley

Merge mulgrave.(none):/home/jejb/BK/linux-2.5

into mulgrave.(none):/home/jejb/BK/scsi-for-linus-2.5
parents 1c6232c3 9b46c836
...@@ -284,6 +284,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt, ...@@ -284,6 +284,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt,
tpnt->use_clustering = DISABLE_CLUSTERING; tpnt->use_clustering = DISABLE_CLUSTERING;
tpnt->proc_info = NCR_700_proc_directory_info; tpnt->proc_info = NCR_700_proc_directory_info;
tpnt->use_blk_tcq = 1; tpnt->use_blk_tcq = 1;
tpnt->highmem_io = 1;
if(tpnt->name == NULL) if(tpnt->name == NULL)
tpnt->name = "53c700"; tpnt->name = "53c700";
......
...@@ -259,6 +259,48 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j) ...@@ -259,6 +259,48 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j)
return retval; return retval;
} }
void scsi_host_busy_inc(struct Scsi_Host *shost, Scsi_Device *sdev)
{
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_busy++;
sdev->device_busy++;
spin_unlock_irqrestore(shost->host_lock, flags);
}
void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev)
{
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_busy--;
sdev->device_busy--;
if (shost->in_recovery && (shost->host_busy == shost->host_failed)) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
"thread (%d)\n",
atomic_read(&shost->eh_wait->count)));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
void scsi_host_failed_inc_and_test(struct Scsi_Host *shost)
{
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->in_recovery = 1;
shost->host_failed++;
if (shost->host_busy == shost->host_failed) {
up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
"thread (%d)\n",
atomic_read(&shost->eh_wait->count)));
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
/* /*
* Overrides for Emacs so that we follow Linus's tabbing style. * Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically * Emacs will notice this stuff at the end of the file and automatically
......
...@@ -543,6 +543,13 @@ extern int scsi_unregister_device(struct Scsi_Device_Template *); ...@@ -543,6 +543,13 @@ extern int scsi_unregister_device(struct Scsi_Device_Template *);
extern int scsi_register_host(Scsi_Host_Template *); extern int scsi_register_host(Scsi_Host_Template *);
extern int scsi_unregister_host(Scsi_Host_Template *); extern int scsi_unregister_host(Scsi_Host_Template *);
/*
* host_busy inc/dec/test functions
*/
extern void scsi_host_busy_inc(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_busy_dec_and_test(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_failed_inc_and_test(struct Scsi_Host *);
/* /*
* This is an ugly hack. If we expect to be able to load devices at run time, * This is an ugly hack. If we expect to be able to load devices at run time,
......
...@@ -566,22 +566,6 @@ inline void __scsi_release_command(Scsi_Cmnd * SCpnt) ...@@ -566,22 +566,6 @@ inline void __scsi_release_command(Scsi_Cmnd * SCpnt)
SCpnt->target, SCpnt->target,
atomic_read(&SCpnt->host->host_active), atomic_read(&SCpnt->host->host_active),
SCpnt->host->host_failed)); SCpnt->host->host_failed));
if (SCpnt->host->host_failed != 0) {
SCSI_LOG_ERROR_RECOVERY(5, printk("Error handler thread %d %d\n",
SCpnt->host->in_recovery,
SCpnt->host->eh_active));
}
/*
* If the host is having troubles, then look to see if this was the last
* command that might have failed. If so, wake up the error handler.
*/
if (SCpnt->host->in_recovery
&& !SCpnt->host->eh_active
&& SCpnt->host->host_busy == SCpnt->host->host_failed) {
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
atomic_read(&SCpnt->host->eh_wait->count)));
up(SCpnt->host->eh_wait);
}
spin_unlock_irqrestore(&device_request_lock, flags); spin_unlock_irqrestore(&device_request_lock, flags);
...@@ -1217,28 +1201,11 @@ void scsi_done(Scsi_Cmnd * SCpnt) ...@@ -1217,28 +1201,11 @@ void scsi_done(Scsi_Cmnd * SCpnt)
* etc, etc. * etc, etc.
*/ */
if (!tstatus) { if (!tstatus) {
SCpnt->done_late = 1;
return; return;
} }
/* Set the serial numbers back to zero */ /* Set the serial numbers back to zero */
SCpnt->serial_number = 0; SCpnt->serial_number = 0;
/*
* First, see whether this command already timed out. If so, we ignore
* the response. We treat it as if the command never finished.
*
* Since serial_number is now 0, the error handler cound detect this
* situation and avoid to call the low level driver abort routine.
* (DB)
*
* FIXME(eric) - I believe that this test is now redundant, due to
* the test of the return status of del_timer().
*/
if (SCpnt->state == SCSI_STATE_TIMEOUT) {
SCSI_LOG_MLCOMPLETE(1, printk("Ignoring completion of %p due to timeout status", SCpnt));
return;
}
SCpnt->serial_number_at_timeout = 0; SCpnt->serial_number_at_timeout = 0;
SCpnt->state = SCSI_STATE_BHQUEUE; SCpnt->state = SCSI_STATE_BHQUEUE;
SCpnt->owner = SCSI_OWNER_BH_HANDLER; SCpnt->owner = SCSI_OWNER_BH_HANDLER;
...@@ -1349,21 +1316,11 @@ static void scsi_softirq(struct softirq_action *h) ...@@ -1349,21 +1316,11 @@ static void scsi_softirq(struct softirq_action *h)
SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt)); SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt));
} }
if (SCpnt->host->eh_wait != NULL) { if (SCpnt->host->eh_wait != NULL) {
SCpnt->host->host_failed++; scsi_eh_eflags_set(SCpnt, SCSI_EH_CMD_FAILED | SCSI_EH_CMD_ERR);
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
SCpnt->state = SCSI_STATE_FAILED; SCpnt->state = SCSI_STATE_FAILED;
SCpnt->host->in_recovery = 1;
/* scsi_host_failed_inc_and_test(SCpnt->host);
* If the host is having troubles, then
* look to see if this was the last
* command that might have failed. If
* so, wake up the error handler.
*/
if (SCpnt->host->host_busy == SCpnt->host->host_failed) {
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
atomic_read(&SCpnt->host->eh_wait->count)));
up(SCpnt->host->eh_wait);
}
} else { } else {
/* /*
* We only get here if the error * We only get here if the error
...@@ -1418,7 +1375,6 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) ...@@ -1418,7 +1375,6 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt)
struct Scsi_Host *host; struct Scsi_Host *host;
Scsi_Device *device; Scsi_Device *device;
Scsi_Request * SRpnt; Scsi_Request * SRpnt;
unsigned long flags;
host = SCpnt->host; host = SCpnt->host;
device = SCpnt->device; device = SCpnt->device;
...@@ -1432,10 +1388,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) ...@@ -1432,10 +1388,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt)
* one execution context, but the device and host structures are * one execution context, but the device and host structures are
* shared. * shared.
*/ */
spin_lock_irqsave(host->host_lock, flags); scsi_host_busy_dec_and_test(host, device);
host->host_busy--; /* Indicate that we are free */
device->device_busy--; /* Decrement device usage counter. */
spin_unlock_irqrestore(host->host_lock, flags);
/* /*
* Clear the flags which say that the device/host is no longer * Clear the flags which say that the device/host is no longer
...@@ -1450,7 +1403,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) ...@@ -1450,7 +1403,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt)
* If we have valid sense information, then some kind of recovery * If we have valid sense information, then some kind of recovery
* must have taken place. Make a note of this. * must have taken place. Make a note of this.
*/ */
if (scsi_sense_valid(SCpnt)) { if (SCSI_SENSE_VALID(SCpnt)) {
SCpnt->result |= (DRIVER_SENSE << 24); SCpnt->result |= (DRIVER_SENSE << 24);
} }
SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n", SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n",
......
...@@ -428,7 +428,6 @@ extern void scsi_add_timer(Scsi_Cmnd * SCset, int timeout, ...@@ -428,7 +428,6 @@ extern void scsi_add_timer(Scsi_Cmnd * SCset, int timeout,
void (*complete) (Scsi_Cmnd *)); void (*complete) (Scsi_Cmnd *));
extern int scsi_delete_timer(Scsi_Cmnd * SCset); extern int scsi_delete_timer(Scsi_Cmnd * SCset);
extern void scsi_error_handler(void *host); extern void scsi_error_handler(void *host);
extern int scsi_sense_valid(Scsi_Cmnd *);
extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt); extern int scsi_decide_disposition(Scsi_Cmnd * SCpnt);
extern int scsi_block_when_processing_errors(Scsi_Device *); extern int scsi_block_when_processing_errors(Scsi_Device *);
extern void scsi_sleep(int); extern void scsi_sleep(int);
...@@ -701,6 +700,7 @@ struct scsi_cmnd { ...@@ -701,6 +700,7 @@ struct scsi_cmnd {
struct scsi_cmnd *reset_chain; struct scsi_cmnd *reset_chain;
int eh_state; /* Used for state tracking in error handlr */ int eh_state; /* Used for state tracking in error handlr */
int eh_eflags; /* Used by error handlr */
void (*done) (struct scsi_cmnd *); /* Mid-level done function */ void (*done) (struct scsi_cmnd *); /* Mid-level done function */
/* /*
A SCSI Command is assigned a nonzero serial_number when internal_cmnd A SCSI Command is assigned a nonzero serial_number when internal_cmnd
...@@ -940,4 +940,26 @@ static inline Scsi_Cmnd *scsi_find_tag(Scsi_Device *SDpnt, int tag) { ...@@ -940,4 +940,26 @@ static inline Scsi_Cmnd *scsi_find_tag(Scsi_Device *SDpnt, int tag) {
return (Scsi_Cmnd *)req->special; return (Scsi_Cmnd *)req->special;
} }
#define scsi_eh_eflags_chk(scp, flags) (scp->eh_eflags & flags)
#define scsi_eh_eflags_set(scp, flags) do { \
scp->eh_eflags |= flags; \
} while(0)
#define scsi_eh_eflags_clr(scp, flags) do { \
scp->eh_eflags &= ~flags; \
} while(0)
#define scsi_eh_eflags_clr_all(scp) (scp->eh_eflags = 0)
/*
* Scsi Error Handler Flags
*/
#define SCSI_EH_CMD_ERR 0x0001 /* Orig cmd error'd */
#define SCSI_EH_CMD_FAILED 0x0002 /* Orig cmd error type failed */
#define SCSI_EH_CMD_TIMEOUT 0x0004 /* Orig cmd error type timeout */
#define SCSI_EH_REC_TIMEOUT 0x0008 /* Recovery cmd timeout */
#define SCSI_SENSE_VALID(scmd) ((scmd->sense_buffer[0] & 0x70) == 0x70)
#endif #endif
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* Leonard Zubkoff and David Miller at Linux Expo, * Leonard Zubkoff and David Miller at Linux Expo,
* ideas originating from all over the place. * ideas originating from all over the place.
* *
* Restructured scsi_unjam_host and associated functions.
* September 04, 2002 Mike Anderson (andmike@us.ibm.com)
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -49,16 +51,10 @@ ...@@ -49,16 +51,10 @@
#ifdef DEBUG #ifdef DEBUG
#define SENSE_TIMEOUT SCSI_TIMEOUT #define SENSE_TIMEOUT SCSI_TIMEOUT
#define ABORT_TIMEOUT SCSI_TIMEOUT
#define RESET_TIMEOUT SCSI_TIMEOUT
#else #else
#define SENSE_TIMEOUT (10*HZ) #define SENSE_TIMEOUT (10*HZ)
#define RESET_TIMEOUT (2*HZ)
#define ABORT_TIMEOUT (15*HZ)
#endif #endif
#define STATIC
/* /*
* These should *probably* be handled by the host itself. * These should *probably* be handled by the host itself.
* Since it is allowed to sleep, it probably should. * Since it is allowed to sleep, it probably should.
...@@ -66,47 +62,21 @@ ...@@ -66,47 +62,21 @@
#define BUS_RESET_SETTLE_TIME 5*HZ #define BUS_RESET_SETTLE_TIME 5*HZ
#define HOST_RESET_SETTLE_TIME 10*HZ #define HOST_RESET_SETTLE_TIME 10*HZ
/**
static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_error.c,v 1.10 1997/12/08 04:50:35 eric Exp $"; * scsi_add_timer - Start timeout timer for a single scsi command.
* @scmd: scsi command that is about to start running.
STATIC int scsi_check_sense(Scsi_Cmnd * SCpnt); * @timeout: amount of time to allow this command to run.
STATIC int scsi_request_sense(Scsi_Cmnd *); * @complete: timeout function to call if timer isn't canceled.
STATIC void scsi_send_eh_cmnd(Scsi_Cmnd * SCpnt, int timeout);
STATIC int scsi_try_to_abort_command(Scsi_Cmnd *, int);
STATIC int scsi_test_unit_ready(Scsi_Cmnd *);
STATIC int scsi_try_bus_device_reset(Scsi_Cmnd *, int timeout);
STATIC int scsi_try_bus_reset(Scsi_Cmnd *);
STATIC int scsi_try_host_reset(Scsi_Cmnd *);
STATIC int scsi_unit_is_ready(Scsi_Cmnd *);
STATIC void scsi_eh_action_done(Scsi_Cmnd *, int);
STATIC int scsi_eh_retry_command(Scsi_Cmnd *);
STATIC int scsi_eh_completed_normally(Scsi_Cmnd * SCpnt);
STATIC void scsi_restart_operations(struct Scsi_Host *);
STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt);
/*
* Function: scsi_add_timer()
*
* Purpose: Start timeout timer for a single scsi command.
*
* Arguments: SCset - command that is about to start running.
* timeout - amount of time to allow this command to run.
* complete - timeout function to call if timer isn't
* canceled.
* *
* Returns: Nothing * Notes:
* * This should be turned into an inline function. Each scsi command
* Notes: This should be turned into an inline function. * has it's own timer, and as it is added to the queue, we set up the
* * timer. When the command completes, we cancel the timer. Pretty
* More Notes: Each scsi command has it's own timer, and as it is added to * simple, really, especially compared to the old way of handling this
* the queue, we set up the timer. When the command completes, * crap.
* we cancel the timer. Pretty simple, really, especially **/
* compared to the old way of handling this crap. void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete)
*/ (Scsi_Cmnd *))
void scsi_add_timer(Scsi_Cmnd * SCset,
int timeout,
void (*complete) (Scsi_Cmnd *))
{ {
/* /*
...@@ -114,320 +84,541 @@ void scsi_add_timer(Scsi_Cmnd * SCset, ...@@ -114,320 +84,541 @@ void scsi_add_timer(Scsi_Cmnd * SCset,
* first delete the timer. The timer handling code gets rather * first delete the timer. The timer handling code gets rather
* confused if we don't do this. * confused if we don't do this.
*/ */
if (SCset->eh_timeout.function != NULL) { if (scmd->eh_timeout.function != NULL) {
del_timer(&SCset->eh_timeout); del_timer(&scmd->eh_timeout);
} }
SCset->eh_timeout.data = (unsigned long) SCset; scmd->eh_timeout.data = (unsigned long) scmd;
SCset->eh_timeout.expires = jiffies + timeout; scmd->eh_timeout.expires = jiffies + timeout;
SCset->eh_timeout.function = (void (*)(unsigned long)) complete; scmd->eh_timeout.function = (void (*)(unsigned long)) complete;
SCset->done_late = 0; SCSI_LOG_ERROR_RECOVERY(5, printk("Adding timer for command %p at"
"%d (%p)\n", scmd, timeout,
complete));
SCSI_LOG_ERROR_RECOVERY(5, printk("Adding timer for command %p at %d (%p)\n", SCset, timeout, complete)); add_timer(&scmd->eh_timeout);
add_timer(&SCset->eh_timeout);
} }
/* /**
* Function: scsi_delete_timer() * scsi_delete_timer - Delete/cancel timer for a given function.
* * @scmd: Cmd that we are canceling timer for
* Purpose: Delete/cancel timer for a given function.
*
* Arguments: SCset - command that we are canceling timer for.
* *
* Returns: 1 if we were able to detach the timer. 0 if we * Notes:
* blew it, and the timer function has already started * This should be turned into an inline function.
* to run.
* *
* Notes: This should be turned into an inline function. * Return value:
*/ * 1 if we were able to detach the timer. 0 if we blew it, and the
int scsi_delete_timer(Scsi_Cmnd * SCset) * timer function has already started to run.
**/
int scsi_delete_timer(Scsi_Cmnd *scmd)
{ {
int rtn; int rtn;
rtn = del_timer(&SCset->eh_timeout); rtn = del_timer(&scmd->eh_timeout);
SCSI_LOG_ERROR_RECOVERY(5, printk("Clearing timer for command %p %d\n", SCset, rtn)); SCSI_LOG_ERROR_RECOVERY(5, printk("Clearing timer for command %p"
" %d\n", scmd, rtn));
SCset->eh_timeout.data = (unsigned long) NULL; scmd->eh_timeout.data = (unsigned long) NULL;
SCset->eh_timeout.function = NULL; scmd->eh_timeout.function = NULL;
return rtn; return rtn;
} }
/* /**
* Function: scsi_times_out() * scsi_times_out - Timeout function for normal scsi commands.
* @scmd: Cmd that is timing out.
* *
* Purpose: Timeout function for normal scsi commands.. * Notes:
* We do not need to lock this. There is the potential for a race
* only in that the normal completion handling might run, but if the
* normal completion function determines that the timer has already
* fired, then it mustn't do anything.
**/
void scsi_times_out(Scsi_Cmnd *scmd)
{
/* Set the serial_number_at_timeout to the current serial_number */
scmd->serial_number_at_timeout = scmd->serial_number;
scsi_eh_eflags_set(scmd, SCSI_EH_CMD_TIMEOUT | SCSI_EH_CMD_ERR);
if( scmd->host->eh_wait == NULL ) {
panic("Error handler thread not present at %p %p %s %d",
scmd, scmd->host, __FILE__, __LINE__);
}
scsi_host_failed_inc_and_test(scmd->host);
SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d "
"failed=%d\n",
atomic_read(&scmd->host->host_active),
scmd->host->host_busy,
scmd->host->host_failed));
}
/**
* scsi_block_when_processing_errors - Prevent cmds from being queued.
* @sdev: Device on which we are performing recovery.
* *
* Arguments: SCpnt - command that is timing out. * Description:
* We block until the host is out of error recovery, and then check to
* see whether the host or the device is offline.
* *
* Returns: Nothing. * Return value:
* FALSE when dev was taken offline by error recovery. TRUE OK to
* proceed.
**/
int scsi_block_when_processing_errors(Scsi_Device *sdev)
{
SCSI_SLEEP(&sdev->host->host_wait, sdev->host->in_recovery);
SCSI_LOG_ERROR_RECOVERY(5, printk("Open returning %d\n",
sdev->online));
return sdev->online;
}
#if CONFIG_SCSI_LOGGING
/**
* scsi_eh_prt_fail_stats - Log info on failures.
* @sc_list: List for failed cmds.
* @shost: scsi host being recovered.
**/
static void scsi_eh_prt_fail_stats(Scsi_Cmnd *sc_list, struct Scsi_Host *shost)
{
Scsi_Cmnd *scmd;
Scsi_Device *sdev;
int total_failures = 0;
int cmd_failed = 0;
int cmd_timed_out = 0;
int devices_failed = 0;
for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
for (scmd = sc_list; scmd; scmd = scmd->bh_next) {
if (scmd->device == sdev) {
++total_failures;
if (scsi_eh_eflags_chk(scmd,
SCSI_EH_CMD_TIMEOUT))
++cmd_timed_out;
else
++cmd_failed;
}
}
if (cmd_timed_out || cmd_failed) {
SCSI_LOG_ERROR_RECOVERY(3,
printk("scsi_eh: %d:%d:%d:%d cmds failed: %d,"
"timedout: %d\n",
shost->host_no, sdev->channel,
sdev->id, sdev->lun,
cmd_failed, cmd_timed_out));
cmd_timed_out = 0;
cmd_failed = 0;
++devices_failed;
}
}
SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d commands on %d "
"devices require eh work\n",
total_failures, devices_failed));
}
#endif
/**
* scsi_eh_get_failed - Gather failed cmds.
* @sc_list: A pointer to a list for failed cmds.
* @shost: Scsi host being recovered.
* *
* Notes: We do not need to lock this. There is the potential for * XXX Add opaque interator for device / shost. Investigate direct
* a race only in that the normal completion handling might * addition to per eh list on error allowing skipping of this step.
* run, but if the normal completion function determines **/
* that the timer has already fired, then it mustn't do static void scsi_eh_get_failed(Scsi_Cmnd **sc_list, struct Scsi_Host *shost)
* anything.
*/
void scsi_times_out(Scsi_Cmnd * SCpnt)
{ {
/* int found;
* Notify the low-level code that this operation failed and we are Scsi_Device *sdev;
* reposessing the command. Scsi_Cmnd *scmd;
*/
#ifdef ERIC_neverdef for (found = 0, sdev = shost->host_queue; sdev; sdev = sdev->next) {
/* for (scmd = sdev->device_queue; scmd; scmd = scmd->next) {
* FIXME(eric) if (scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) {
* Allow the host adapter to push a queue ordering tag scmd->bh_next = *sc_list;
* out to the bus to force the command in question to complete. *sc_list = scmd;
* If the host wants to do this, then we just restart the timer found++;
* for the command. Before we really do this, some real thought } else {
* as to the optimum way to handle this should be done. We *do* /*
* need to force ordering every so often to ensure that all requests * FIXME Verify how this can happen and if
* do eventually complete, but I am not sure if this is the best way * this is still needed??
* to actually go about it. */
* if (scmd->state != SCSI_STATE_INITIALIZING
* Better yet, force a sync here, but don't block since we are in an && scmd->state != SCSI_STATE_UNUSED) {
* interrupt. /*
*/ * Rats. Something is still floating
if (SCpnt->host->hostt->eh_ordered_queue_tag) { * around out there This could be the
if ((*SCpnt->host->hostt->eh_ordered_queue_tag) (SCpnt)) { * result of the fact that the upper level
scsi_add_timer(SCpnt, SCpnt->internal_timeout, * drivers are still frobbing commands
scsi_times_out); * that might have succeeded. There are
return; * two outcomes. One is that the command
* block will eventually be freed, and the
* other one is that the command will be
* queued and will be finished along the
* way.
*/
SCSI_LOG_ERROR_RECOVERY(1, printk("Error hdlr "
"prematurely woken "
"cmds still active "
"(%p %x %d)\n",
scmd, scmd->state,
scmd->target));
}
}
} }
} }
/*
* FIXME(eric) - add a second special interface to handle this SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(*sc_list, shost));
* case. Ideally that interface can also be used to request
* a queu BUG_ON(shost->host_failed != found);
*/ }
if (SCpnt->host->can_queue) {
SCpnt->host->hostt->queuecommand(SCpnt, NULL); /**
* scsi_check_sense - Examine scsi cmd sense
* @scmd: Cmd to have sense checked.
**/
static int scsi_check_sense(Scsi_Cmnd *scmd)
{
if (!SCSI_SENSE_VALID(scmd)) {
return FAILED;
} }
#endif if (scmd->sense_buffer[2] & 0xe0)
return SUCCESS;
/* Set the serial_number_at_timeout to the current serial_number */ switch (scmd->sense_buffer[2] & 0xf) {
SCpnt->serial_number_at_timeout = SCpnt->serial_number; case NO_SENSE:
return SUCCESS;
case RECOVERED_ERROR:
return /* soft_error */ SUCCESS;
SCpnt->eh_state = FAILED; case ABORTED_COMMAND:
SCpnt->state = SCSI_STATE_TIMEOUT; return NEEDS_RETRY;
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; case NOT_READY:
case UNIT_ATTENTION:
/*
* if we are expecting a cc/ua because of a bus reset that we
* performed, treat this just as a retry. otherwise this is
* information that we should pass up to the upper-level driver
* so that we can deal with it there.
*/
if (scmd->device->expecting_cc_ua) {
scmd->device->expecting_cc_ua = 0;
return NEEDS_RETRY;
}
/*
* if the device is in the process of becoming ready, we
* should retry.
*/
if ((scmd->sense_buffer[12] == 0x04) &&
(scmd->sense_buffer[13] == 0x01)) {
return NEEDS_RETRY;
}
return SUCCESS;
SCpnt->host->in_recovery = 1; /* these three are not supported */
SCpnt->host->host_failed++; case COPY_ABORTED:
case VOLUME_OVERFLOW:
case MISCOMPARE:
return SUCCESS;
SCSI_LOG_TIMEOUT(3, printk("Command timed out active=%d busy=%d failed=%d\n", case MEDIUM_ERROR:
atomic_read(&SCpnt->host->host_active), return NEEDS_RETRY;
SCpnt->host->host_busy,
SCpnt->host->host_failed));
/* case ILLEGAL_REQUEST:
* If the host is having troubles, then look to see if this was the last case BLANK_CHECK:
* command that might have failed. If so, wake up the error handler. case DATA_PROTECT:
*/ case HARDWARE_ERROR:
if( SCpnt->host->eh_wait == NULL ) { default:
panic("Error handler thread not present at %p %p %s %d", return SUCCESS;
SCpnt, SCpnt->host, __FILE__, __LINE__);
}
if (SCpnt->host->host_busy == SCpnt->host->host_failed) {
up(SCpnt->host->eh_wait);
} }
} }
/* /**
* Function scsi_block_when_processing_errors * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
* @scmd: SCSI cmd to examine.
* *
* Purpose: Prevent more commands from being queued while error recovery * Notes:
* is taking place. * This is *only* called when we are examining the status of commands
* * queued during error recovery. the main difference here is that we
* Arguments: SDpnt - device on which we are performing recovery. * don't allow for the possibility of retries here, and we are a lot
* * more restrictive about what we consider acceptable.
* Returns: FALSE The device was taken offline by error recovery. **/
* TRUE OK to proceed. static int scsi_eh_completed_normally(Scsi_Cmnd *scmd)
*
* Notes: We block until the host is out of error recovery, and then
* check to see whether the host or the device is offline.
*/
int scsi_block_when_processing_errors(Scsi_Device * SDpnt)
{ {
int rtn;
SCSI_SLEEP(&SDpnt->host->host_wait, SDpnt->host->in_recovery); /*
* first check the host byte, to see if there is anything in there
SCSI_LOG_ERROR_RECOVERY(5, printk("Open returning %d\n", SDpnt->online)); * that would indicate what we need to do.
*/
if (host_byte(scmd->result) == DID_RESET) {
if (scmd->flags & IS_RESETTING) {
/*
* ok, this is normal. we don't know whether in fact
* the command in question really needs to be rerun
* or not - if this was the original data command then
* the answer is yes, otherwise we just flag it as
* SUCCESS.
*/
scmd->flags &= ~IS_RESETTING;
goto maybe_retry;
}
/*
* rats. we are already in the error handler, so we now
* get to try and figure out what to do next. if the sense
* is valid, we have a pretty good idea of what to do.
* if not, we mark it as FAILED.
*/
rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY)
goto maybe_retry;
return rtn;
}
if (host_byte(scmd->result) != DID_OK) {
return FAILED;
}
/*
* next, check the message byte.
*/
if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
return FAILED;
}
/*
* now, check the status byte to see if this indicates
* anything special.
*/
switch (status_byte(scmd->result)) {
case GOOD:
case COMMAND_TERMINATED:
return SUCCESS;
case CHECK_CONDITION:
rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY)
goto maybe_retry;
return rtn;
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
/*
* who knows? FIXME(eric)
*/
return SUCCESS;
case BUSY:
case QUEUE_FULL:
case RESERVATION_CONFLICT:
default:
return FAILED;
}
return FAILED;
return SDpnt->online; maybe_retry:
if ((++scmd->retries) < scmd->allowed) {
return NEEDS_RETRY;
} else {
/* no more retries - report this one back to upper level */
return SUCCESS;
}
} }
/* /**
* Function: scsi_eh_times_out() * scsi_eh_times_out - timeout function for error handling.
* * @scmd: Cmd that is timing out.
* Purpose: Timeout function for error handling.
* *
* Arguments: SCpnt - command that is timing out. * Notes:
* * During error handling, the kernel thread will be sleeping waiting
* Returns: Nothing. * for some action to complete on the device. our only job is to
* * record that it timed out, and to wake up the thread.
* Notes: During error handling, the kernel thread will be sleeping **/
* waiting for some action to complete on the device. Our only static void scsi_eh_times_out(Scsi_Cmnd *scmd)
* job is to record that it timed out, and to wake up the
* thread.
*/
STATIC
void scsi_eh_times_out(Scsi_Cmnd * SCpnt)
{ {
SCpnt->eh_state = SCSI_STATE_TIMEOUT; scsi_eh_eflags_set(scmd, SCSI_EH_REC_TIMEOUT);
SCSI_LOG_ERROR_RECOVERY(5, printk("In scsi_eh_times_out %p\n", SCpnt)); SCSI_LOG_ERROR_RECOVERY(3, printk("in scsi_eh_times_out %p\n", scmd));
if (SCpnt->host->eh_action != NULL) if (scmd->host->eh_action != NULL)
up(SCpnt->host->eh_action); up(scmd->host->eh_action);
else else
printk("Missing scsi error handler thread\n"); printk("missing scsi error handler thread\n");
} }
/**
/* * scsi_eh_done - Completion function for error handling.
* Function: scsi_eh_done() * @scmd: Cmd that is done.
* **/
* Purpose: Completion function for error handling. static void scsi_eh_done(Scsi_Cmnd *scmd)
*
* Arguments: SCpnt - command that is timing out.
*
* Returns: Nothing.
*
* Notes: During error handling, the kernel thread will be sleeping
* waiting for some action to complete on the device. Our only
* job is to record that the action completed, and to wake up the
* thread.
*/
STATIC
void scsi_eh_done(Scsi_Cmnd * SCpnt)
{ {
int rtn; int rtn;
/* /*
* If the timeout handler is already running, then just set the * if the timeout handler is already running, then just set the
* flag which says we finished late, and return. We have no * flag which says we finished late, and return. we have no
* way of stopping the timeout handler from running, so we must * way of stopping the timeout handler from running, so we must
* always defer to it. * always defer to it.
*/ */
rtn = del_timer(&SCpnt->eh_timeout); rtn = del_timer(&scmd->eh_timeout);
if (!rtn) { if (!rtn) {
SCpnt->done_late = 1;
return; return;
} }
SCpnt->request->rq_status = RQ_SCSI_DONE; scmd->request->rq_status = RQ_SCSI_DONE;
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; scmd->owner = SCSI_OWNER_ERROR_HANDLER;
SCpnt->eh_state = SUCCESS;
SCSI_LOG_ERROR_RECOVERY(5, printk("In eh_done %p result:%x\n", SCpnt, SCSI_LOG_ERROR_RECOVERY(3, printk("in eh_done %p result:%x\n", scmd,
SCpnt->result)); scmd->result));
if (SCpnt->host->eh_action != NULL) if (scmd->host->eh_action != NULL)
up(SCpnt->host->eh_action); up(scmd->host->eh_action);
} }
/* /**
* Function: scsi_eh_action_done() * scsi_send_eh_cmnd - send a cmd to a device as part of error recovery.
* @scmd: SCSI Cmd to send.
* @timeout: Timeout for cmd.
* *
* Purpose: Completion function for error handling. * Notes:
* * The initialization of the structures is quite a bit different in
* Arguments: SCpnt - command that is timing out. * this case, and furthermore, there is a different completion handler
* answer - boolean that indicates whether operation succeeded. * vs scsi_dispatch_cmd.
* * Return value:
* Returns: Nothing. * SUCCESS/FAILED
* **/
* Notes: This callback is only used for abort and reset operations. static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout)
*/
STATIC
void scsi_eh_action_done(Scsi_Cmnd * SCpnt, int answer)
{ {
SCpnt->request->rq_status = RQ_SCSI_DONE; unsigned long flags;
struct Scsi_Host *host = scmd->host;
int rtn = SUCCESS;
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; ASSERT_LOCK(host->host_lock, 0);
SCpnt->eh_state = (answer ? SUCCESS : FAILED);
if (SCpnt->host->eh_action != NULL) retry:
up(SCpnt->host->eh_action); /*
} * we will use a queued command if possible, otherwise we will
* emulate the queuing and calling of completion function ourselves.
*/
scmd->owner = SCSI_OWNER_LOWLEVEL;
/* if (host->can_queue) {
* Function: scsi_sense_valid() DECLARE_MUTEX_LOCKED(sem);
*
* Purpose: Determine whether a host has automatically obtained sense
* information or not. If we have it, then give a recommendation
* as to what we should do next.
*/
int scsi_sense_valid(Scsi_Cmnd * SCpnt)
{
if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
return FALSE;
}
return TRUE;
}
/* scsi_add_timer(scmd, timeout, scsi_eh_times_out);
* Function: scsi_eh_retry_command()
* /*
* Purpose: Retry the original command * set up the semaphore so we wait for the command to complete.
* */
* Returns: SUCCESS - we were able to get the sense data. scmd->host->eh_action = &sem;
* FAILED - we were not able to get the sense data. scmd->request->rq_status = RQ_SCSI_BUSY;
*
* Notes: This function will *NOT* return until the command either spin_lock_irqsave(scmd->host->host_lock, flags);
* times out, or it completes. host->hostt->queuecommand(scmd, scsi_eh_done);
*/ spin_unlock_irqrestore(scmd->host->host_lock, flags);
STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt)
{ down(&sem);
memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
sizeof(SCpnt->data_cmnd)); scmd->host->eh_action = NULL;
SCpnt->request_buffer = SCpnt->buffer;
SCpnt->request_bufflen = SCpnt->bufflen; /*
SCpnt->use_sg = SCpnt->old_use_sg; * see if timeout. if so, tell the host to forget about it.
SCpnt->cmd_len = SCpnt->old_cmd_len; * in other words, we don't want a callback any more.
SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; */
SCpnt->underflow = SCpnt->old_underflow; if (scsi_eh_eflags_chk(scmd, SCSI_EH_REC_TIMEOUT)) {
scsi_eh_eflags_clr(scmd, SCSI_EH_REC_TIMEOUT);
scmd->owner = SCSI_OWNER_LOWLEVEL;
/*
* as far as the low level driver is
* concerned, this command is still active, so
* we must give the low level driver a chance
* to abort it. (db)
*
* FIXME(eric) - we are not tracking whether we could
* abort a timed out command or not. not sure how
* we should treat them differently anyways.
*/
spin_lock_irqsave(scmd->host->host_lock, flags);
if (scmd->host->hostt->eh_abort_handler)
scmd->host->hostt->eh_abort_handler(scmd);
spin_unlock_irqrestore(scmd->host->host_lock, flags);
scmd->request->rq_status = RQ_SCSI_DONE;
scmd->owner = SCSI_OWNER_ERROR_HANDLER;
rtn = FAILED;
}
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: %p rtn:%x\n",
__FUNCTION__, scmd,
rtn));
} else {
int temp;
scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); /*
* we damn well had better never use this code. there is no
* timeout protection here, since we would end up waiting in
* the actual low level driver, we don't know how to wake it up.
*/
spin_lock_irqsave(host->host_lock, flags);
temp = host->hostt->command(scmd);
spin_unlock_irqrestore(host->host_lock, flags);
scmd->result = temp;
/* fall through to code below to examine status. */
}
/* /*
* Hey, we are done. Let's look to see what happened. * now examine the actual status codes to see whether the command
* actually did complete normally.
*/ */
return SCpnt->eh_state; if (rtn == SUCCESS) {
int ret = scsi_eh_completed_normally(scmd);
SCSI_LOG_ERROR_RECOVERY(3,
printk("%s: scsi_eh_completed_normally %x\n",
__FUNCTION__, ret));
switch (ret) {
case SUCCESS:
break;
case NEEDS_RETRY:
goto retry;
case FAILED:
default:
rtn = FAILED;
break;
}
}
return rtn;
} }
/* /**
* Function: scsi_request_sense() * scsi_request_sense - Request sense data from a particular target.
* * @scmd: SCSI cmd for request sense.
* Purpose: Request sense data from a particular target.
* *
* Returns: SUCCESS - we were able to get the sense data. * Notes:
* FAILED - we were not able to get the sense data. * Some hosts automatically obtain this information, others require
* * that we obtain it on our own. This function will *not* return until
* Notes: Some hosts automatically obtain this information, others * the command either times out, or it completes.
* require that we obtain it on our own. **/
* static int scsi_request_sense(Scsi_Cmnd *scmd)
* This function will *NOT* return until the command either
* times out, or it completes.
*/
STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt)
{ {
static unsigned char generic_sense[6] = static unsigned char generic_sense[6] =
{REQUEST_SENSE, 0, 0, 0, 255, 0}; {REQUEST_SENSE, 0, 0, 0, 255, 0};
unsigned char scsi_result0[256], *scsi_result = NULL; unsigned char scsi_result0[256], *scsi_result = NULL;
int saved_result; int saved_result;
int rtn;
memcpy((void *) SCpnt->cmnd, (void *) generic_sense, memcpy((void *) scmd->cmnd, (void *) generic_sense,
sizeof(generic_sense)); sizeof(generic_sense));
if (SCpnt->device->scsi_level <= SCSI_2) if (scmd->device->scsi_level <= SCSI_2)
SCpnt->cmnd[1] = SCpnt->lun << 5; scmd->cmnd[1] = scmd->lun << 5;
scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) scsi_result = (!scmd->host->hostt->unchecked_isa_dma)
? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA); ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA);
if (scsi_result == NULL) { if (scsi_result == NULL) {
...@@ -435,267 +626,275 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) ...@@ -435,267 +626,275 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt)
return FAILED; return FAILED;
} }
/* /*
* Zero the sense buffer. Some host adapters automatically always request * zero the sense buffer. some host adapters automatically always
* sense, so it is not a good idea that SCpnt->request_buffer and * request sense, so it is not a good idea that
* SCpnt->sense_buffer point to the same address (DB). * scmd->request_buffer and scmd->sense_buffer point to the same
* 0 is not a valid sense code. * address (db). 0 is not a valid sense code.
*/ */
memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); memset((void *) scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
memset((void *) scsi_result, 0, 256); memset((void *) scsi_result, 0, 256);
saved_result = SCpnt->result; saved_result = scmd->result;
SCpnt->request_buffer = scsi_result; scmd->request_buffer = scsi_result;
SCpnt->request_bufflen = 256; scmd->request_bufflen = 256;
SCpnt->use_sg = 0; scmd->use_sg = 0;
SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
SCpnt->sc_data_direction = SCSI_DATA_READ; scmd->sc_data_direction = SCSI_DATA_READ;
SCpnt->underflow = 0; scmd->underflow = 0;
scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
/* Last chance to have valid sense data */ /* last chance to have valid sense data */
if (!scsi_sense_valid(SCpnt)) if (!SCSI_SENSE_VALID(scmd))
memcpy((void *) SCpnt->sense_buffer, memcpy((void *) scmd->sense_buffer,
SCpnt->request_buffer, scmd->request_buffer,
sizeof(SCpnt->sense_buffer)); sizeof(scmd->sense_buffer));
if (scsi_result != &scsi_result0[0] && scsi_result != NULL) if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
kfree(scsi_result); kfree(scsi_result);
/* /*
* When we eventually call scsi_finish, we really wish to complete * when we eventually call scsi_finish, we really wish to complete
* the original request, so let's restore the original data. (DB) * the original request, so let's restore the original data. (db)
*/ */
memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, memcpy((void *) scmd->cmnd, (void *) scmd->data_cmnd,
sizeof(SCpnt->data_cmnd)); sizeof(scmd->data_cmnd));
SCpnt->result = saved_result; scmd->result = saved_result;
SCpnt->request_buffer = SCpnt->buffer; scmd->request_buffer = scmd->buffer;
SCpnt->request_bufflen = SCpnt->bufflen; scmd->request_bufflen = scmd->bufflen;
SCpnt->use_sg = SCpnt->old_use_sg; scmd->use_sg = scmd->old_use_sg;
SCpnt->cmd_len = SCpnt->old_cmd_len; scmd->cmd_len = scmd->old_cmd_len;
SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; scmd->sc_data_direction = scmd->sc_old_data_direction;
SCpnt->underflow = SCpnt->old_underflow; scmd->underflow = scmd->old_underflow;
/* /*
* Hey, we are done. Let's look to see what happened. * hey, we are done. let's look to see what happened.
*/ */
return SCpnt->eh_state; return rtn;
} }
/* /**
* Function: scsi_test_unit_ready() * scsi_eh_retry_cmd - Retry the original command
* @scmd: Original failed SCSI cmd.
* *
* Purpose: Run test unit ready command to see if the device is talking to us or not. * Notes:
* * This function will *not* return until the command either times out,
*/ * or it completes.
STATIC int scsi_test_unit_ready(Scsi_Cmnd * SCpnt) **/
static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd)
{ {
static unsigned char tur_command[6] = memcpy((void *) scmd->cmnd, (void *) scmd->data_cmnd,
{TEST_UNIT_READY, 0, 0, 0, 0, 0}; sizeof(scmd->data_cmnd));
scmd->request_buffer = scmd->buffer;
memcpy((void *) SCpnt->cmnd, (void *) tur_command, scmd->request_bufflen = scmd->bufflen;
sizeof(tur_command)); scmd->use_sg = scmd->old_use_sg;
scmd->cmd_len = scmd->old_cmd_len;
scmd->sc_data_direction = scmd->sc_old_data_direction;
scmd->underflow = scmd->old_underflow;
return scsi_send_eh_cmnd(scmd, scmd->timeout_per_command);
}
if (SCpnt->device->scsi_level <= SCSI_2) /**
SCpnt->cmnd[1] = SCpnt->lun << 5; * scsi_eh_finish_cmd - Handle a cmd that eh is finished with.
* @scmd: Original SCSI cmd that eh has finished.
* @shost: SCSI host that cmd originally failed on.
*
* Notes:
* We don't want to use the normal command completion while we are are
* still handling errors - it may cause other commands to be queued,
* and that would disturb what we are doing. thus we really want to
* keep a list of pending commands for final completion, and once we
* are ready to leave error handling we handle completion for real.
**/
static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost)
{
shost->host_failed--;
scmd->state = SCSI_STATE_BHQUEUE;
scsi_eh_eflags_clr_all(scmd);
/* /*
* Zero the sense buffer. The SCSI spec mandates that any * set this back so that the upper level can correctly free up
* untransferred sense data should be interpreted as being zero. * things.
*/ */
memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); scmd->use_sg = scmd->old_use_sg;
scmd->sc_data_direction = scmd->sc_old_data_direction;
scmd->underflow = scmd->old_underflow;
}
SCpnt->request_buffer = NULL; /**
SCpnt->request_bufflen = 0; * scsi_eh_get_sense - Get device sense data.
SCpnt->use_sg = 0; * @sc_todo: list of cmds that have failed.
SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); * @shost: scsi host being recovered.
SCpnt->underflow = 0; *
SCpnt->sc_data_direction = SCSI_DATA_NONE; * Description:
* See if we need to request sense information. if so, then get it
* now, so we have a better idea of what to do.
*
*
* Notes:
* This has the unfortunate side effect that if a shost adapter does
* not automatically request sense information, that we end up shutting
* it down before we request it. All shosts should be doing this
* anyways, so for now all I have to say is tough noogies if you end up
* in here. On second thought, this is probably a good idea. We
* *really* want to give authors an incentive to automatically request
* this.
*
* In 2.5 this capability will be going away.
**/
static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
{
int rtn;
Scsi_Cmnd *scmd;
scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: checking to see if we need"
" to request sense\n",
__FUNCTION__));
/* for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
* When we eventually call scsi_finish, we really wish to complete if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_FAILED) ||
* the original request, so let's restore the original data. (DB) SCSI_SENSE_VALID(scmd))
*/ continue;
memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
sizeof(SCpnt->data_cmnd));
SCpnt->request_buffer = SCpnt->buffer;
SCpnt->request_bufflen = SCpnt->bufflen;
SCpnt->use_sg = SCpnt->old_use_sg;
SCpnt->cmd_len = SCpnt->old_cmd_len;
SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
SCpnt->underflow = SCpnt->old_underflow;
/* SCSI_LOG_ERROR_RECOVERY(2, printk("%s: requesting sense"
* Hey, we are done. Let's look to see what happened. "for %d\n", __FUNCTION__,
*/ scmd->target));
SCSI_LOG_ERROR_RECOVERY(3, rtn = scsi_request_sense(scmd);
printk("scsi_test_unit_ready: SCpnt %p eh_state %x\n", if (rtn != SUCCESS)
SCpnt, SCpnt->eh_state)); continue;
return SCpnt->eh_state;
}
/* SCSI_LOG_ERROR_RECOVERY(3, printk("sense requested for %p"
* This would normally need to get the IO request lock, "- result %x\n", scmd,
* but as it doesn't actually touch anything that needs scmd->result));
* to be locked we can avoid the lock here.. SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", scmd));
*/
STATIC
void scsi_sleep_done(struct semaphore *sem)
{
if (sem != NULL) {
up(sem);
}
}
void scsi_sleep(int timeout) rtn = scsi_decide_disposition(scmd);
{
DECLARE_MUTEX_LOCKED(sem);
struct timer_list timer;
init_timer(&timer); /*
timer.data = (unsigned long) &sem; * if the result was normal, then just pass it along to the
timer.expires = jiffies + timeout; * upper level.
timer.function = (void (*)(unsigned long)) scsi_sleep_done; */
if (rtn == SUCCESS)
scsi_eh_finish_cmd(scmd, shost);
if (rtn != NEEDS_RETRY)
continue;
SCSI_LOG_ERROR_RECOVERY(5, printk("Sleeping for timer tics %d\n", timeout)); /*
* we only come in here if we want to retry a
* command. the test to see whether the command
* should be retried should be keeping track of the
* number of tries, so we don't end up looping, of
* course.
*/
scmd->state = NEEDS_RETRY;
rtn = scsi_eh_retry_cmd(scmd);
if (rtn != SUCCESS)
continue;
add_timer(&timer); /*
* we eventually hand this one back to the top level.
*/
scsi_eh_finish_cmd(scmd, shost);
}
down(&sem); return shost->host_failed;
del_timer(&timer);
} }
/* /**
* Function: scsi_send_eh_cmnd * scsi_try_to_abort_cmd - Ask host to abort a running command.
* @scmd: SCSI cmd to abort from Lower Level.
* *
* Purpose: Send a command out to a device as part of error recovery. * Notes:
* * This function will not return until the user's completion function
* Notes: The initialization of the structures is quite a bit different * has been called. there is no timeout on this operation. if the
* in this case, and furthermore, there is a different completion * author of the low-level driver wishes this operation to be timed,
* handler. * they can provide this facility themselves. helper functions in
*/ * scsi_error.c can be supplied to make this easier to do.
STATIC void scsi_send_eh_cmnd(Scsi_Cmnd * SCpnt, int timeout) **/
static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd)
{ {
int rtn = FAILED;
unsigned long flags; unsigned long flags;
struct Scsi_Host *host = SCpnt->host;
ASSERT_LOCK(host->host_lock, 0);
retry: if (scmd->host->hostt->eh_abort_handler == NULL) {
return rtn;
}
/* /*
* We will use a queued command if possible, otherwise we will * scsi_done was called just after the command timed out and before
* emulate the queuing and calling of completion function ourselves. * we had a chance to process it. (db)
*/ */
SCpnt->owner = SCSI_OWNER_LOWLEVEL; if (scmd->serial_number == 0)
return SUCCESS;
if (host->can_queue) {
DECLARE_MUTEX_LOCKED(sem);
SCpnt->eh_state = SCSI_STATE_QUEUED;
scsi_add_timer(SCpnt, timeout, scsi_eh_times_out); scmd->owner = SCSI_OWNER_LOWLEVEL;
/* spin_lock_irqsave(scmd->host->host_lock, flags);
* Set up the semaphore so we wait for the command to complete. rtn = scmd->host->hostt->eh_abort_handler(scmd);
*/ spin_unlock_irqrestore(scmd->host->host_lock, flags);
SCpnt->host->eh_action = &sem; return rtn;
SCpnt->request->rq_status = RQ_SCSI_BUSY; }
spin_lock_irqsave(SCpnt->host->host_lock, flags); /**
host->hostt->queuecommand(SCpnt, scsi_eh_done); * scsi_eh_tur - Send TUR to device.
spin_unlock_irqrestore(SCpnt->host->host_lock, flags); * @scmd: Scsi cmd to send TUR
*
* Return value:
* 0 - Device is ready. 1 - Device NOT ready.
**/
static int scsi_eh_tur(Scsi_Cmnd *scmd)
{
static unsigned char tur_command[6] =
{TEST_UNIT_READY, 0, 0, 0, 0, 0};
int rtn;
down(&sem); memcpy((void *) scmd->cmnd, (void *) tur_command,
sizeof(tur_command));
SCpnt->host->eh_action = NULL; if (scmd->device->scsi_level <= SCSI_2)
scmd->cmnd[1] = scmd->lun << 5;
/* /*
* See if timeout. If so, tell the host to forget about it. * zero the sense buffer. the scsi spec mandates that any
* In other words, we don't want a callback any more. * untransferred sense data should be interpreted as being zero.
*/ */
if (SCpnt->eh_state == SCSI_STATE_TIMEOUT) { memset((void *) scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
SCpnt->owner = SCSI_OWNER_LOWLEVEL;
/*
* As far as the low level driver is
* concerned, this command is still active, so
* we must give the low level driver a chance
* to abort it. (DB)
*
* FIXME(eric) - we are not tracking whether we could
* abort a timed out command or not. Not sure how
* we should treat them differently anyways.
*/
spin_lock_irqsave(SCpnt->host->host_lock, flags);
if (SCpnt->host->hostt->eh_abort_handler)
SCpnt->host->hostt->eh_abort_handler(SCpnt);
spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
SCpnt->request->rq_status = RQ_SCSI_DONE;
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
SCpnt->eh_state = FAILED;
}
SCSI_LOG_ERROR_RECOVERY(5, printk("send_eh_cmnd: %p eh_state:%x\n",
SCpnt, SCpnt->eh_state));
} else {
int temp;
/* scmd->request_buffer = NULL;
* We damn well had better never use this code. There is no scmd->request_bufflen = 0;
* timeout protection here, since we would end up waiting in scmd->use_sg = 0;
* the actual low level driver, we don't know how to wake it up. scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
*/ scmd->underflow = 0;
spin_lock_irqsave(host->host_lock, flags); scmd->sc_data_direction = SCSI_DATA_NONE;
temp = host->hostt->command(SCpnt);
spin_unlock_irqrestore(host->host_lock, flags);
SCpnt->result = temp; rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
/* Fall through to code below to examine status. */
SCpnt->eh_state = SUCCESS;
}
/* /*
* Now examine the actual status codes to see whether the command * when we eventually call scsi_finish, we really wish to complete
* actually did complete normally. * the original request, so let's restore the original data. (db)
*/ */
if (SCpnt->eh_state == SUCCESS) { memcpy((void *) scmd->cmnd, (void *) scmd->data_cmnd,
int ret = scsi_eh_completed_normally(SCpnt); sizeof(scmd->data_cmnd));
SCSI_LOG_ERROR_RECOVERY(3, scmd->request_buffer = scmd->buffer;
printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret)); scmd->request_bufflen = scmd->bufflen;
switch (ret) { scmd->use_sg = scmd->old_use_sg;
case SUCCESS: scmd->cmd_len = scmd->old_cmd_len;
SCpnt->eh_state = SUCCESS; scmd->sc_data_direction = scmd->sc_old_data_direction;
break; scmd->underflow = scmd->old_underflow;
case NEEDS_RETRY:
goto retry;
case FAILED:
default:
SCpnt->eh_state = FAILED;
break;
}
} else {
SCpnt->eh_state = FAILED;
}
}
/* /*
* Function: scsi_unit_is_ready() * hey, we are done. let's look to see what happened.
* */
* Purpose: Called after TEST_UNIT_READY is run, to test to see if SCSI_LOG_ERROR_RECOVERY(3,
* the unit responded in a way that indicates it is ready. printk("%s: scmd %p rtn %x\n",
*/ __FUNCTION__, scmd, rtn));
STATIC int scsi_unit_is_ready(Scsi_Cmnd * SCpnt) if ((rtn == SUCCESS) && scmd->result) {
{ if (((driver_byte(scmd->result) & DRIVER_SENSE) ||
if (SCpnt->result) { (status_byte(scmd->result) & CHECK_CONDITION)) &&
if (((driver_byte(SCpnt->result) & DRIVER_SENSE) || (SCSI_SENSE_VALID(scmd))) {
(status_byte(SCpnt->result) & CHECK_CONDITION)) && if (((scmd->sense_buffer[2] & 0xf) != NOT_READY) &&
((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) { ((scmd->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) && ((scmd->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST)) {
((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST)) {
return 0; return 0;
} }
} }
...@@ -703,275 +902,385 @@ STATIC int scsi_unit_is_ready(Scsi_Cmnd * SCpnt) ...@@ -703,275 +902,385 @@ STATIC int scsi_unit_is_ready(Scsi_Cmnd * SCpnt)
return 1; return 1;
} }
/* /**
* Function: scsi_eh_finish_command * scsi_eh_abort_cmd - abort a timed-out cmd.
* * @sc_todo: A list of cmds that have failed.
* Purpose: Handle a command that we are finished with WRT error handling. * @shost: scsi host being recovered.
* *
* Arguments: SClist - pointer to list into which we are putting completed commands. * Decription:
* SCpnt - command that is completing * Try and see whether or not it makes sense to try and abort the
* * running command. this only works out to be the case if we have one
* Notes: We don't want to use the normal command completion while we are * command that has timed out. if the command simply failed, it makes
* are still handling errors - it may cause other commands to be queued, * no sense to try and abort the command, since as far as the shost
* and that would disturb what we are doing. Thus we really want to keep * adapter is concerned, it isn't running.
* a list of pending commands for final completion, and once we **/
* are ready to leave error handling we handle completion for real. static int scsi_eh_abort_cmd(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
*/
STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt)
{ {
SCpnt->state = SCSI_STATE_BHQUEUE;
SCpnt->bh_next = *SClist; int rtn;
/* Scsi_Cmnd *scmd;
* Set this back so that the upper level can correctly free up
* things. SCSI_LOG_ERROR_RECOVERY(3, printk("%s: checking to see if we need"
*/ " to abort cmd\n", __FUNCTION__));
SCpnt->use_sg = SCpnt->old_use_sg;
SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
SCpnt->underflow = SCpnt->old_underflow; if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_TIMEOUT))
*SClist = SCpnt; continue;
rtn = scsi_try_to_abort_cmd(scmd);
if (rtn == SUCCESS) {
if (scsi_eh_tur(scmd)) {
rtn = scsi_eh_retry_cmd(scmd);
if (rtn == SUCCESS)
scsi_eh_finish_cmd(scmd, shost);
}
}
}
return shost->host_failed;
} }
/* /**
* Function: scsi_try_to_abort_command * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
* @scmd: SCSI cmd used to send BDR
* *
* Purpose: Ask host adapter to abort a running command. * Notes:
* * There is no timeout for this operation. if this operation is
* Returns: FAILED Operation failed or not supported. * unreliable for a given host, then the host itself needs to put a
* SUCCESS Succeeded. * timer on it, and set the host back to a consistent state prior to
* * returning.
* Notes: This function will not return until the user's completion **/
* function has been called. There is no timeout on this static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd)
* operation. If the author of the low-level driver wishes
* this operation to be timed, they can provide this facility
* themselves. Helper functions in scsi_error.c can be supplied
* to make this easier to do.
*
* Notes: It may be possible to combine this with all of the reset
* handling to eliminate a lot of code duplication. I don't
* know what makes more sense at the moment - this is just a
* prototype.
*/
STATIC int scsi_try_to_abort_command(Scsi_Cmnd * SCpnt, int timeout)
{ {
int rtn;
unsigned long flags; unsigned long flags;
int rtn = FAILED;
SCpnt->eh_state = FAILED; /* Until we come up with something better */ if (scmd->host->hostt->eh_device_reset_handler == NULL) {
return rtn;
if (SCpnt->host->hostt->eh_abort_handler == NULL) {
return FAILED;
} }
/* scmd->owner = SCSI_OWNER_LOWLEVEL;
* scsi_done was called just after the command timed out and before
* we had a chance to process it. (DB)
*/
if (SCpnt->serial_number == 0)
return SUCCESS;
SCpnt->owner = SCSI_OWNER_LOWLEVEL; spin_lock_irqsave(scmd->host->host_lock, flags);
rtn = scmd->host->hostt->eh_device_reset_handler(scmd);
spin_unlock_irqrestore(scmd->host->host_lock, flags);
spin_lock_irqsave(SCpnt->host->host_lock, flags);
rtn = SCpnt->host->hostt->eh_abort_handler(SCpnt);
spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
return rtn; return rtn;
} }
/* /**
* Function: scsi_try_bus_device_reset * scsi_eh_bus_device_reset - send bdr is needed
* * @sc_todo: a list of cmds that have failed.
* Purpose: Ask host adapter to perform a bus device reset for a given * @shost: scsi host being recovered.
* device.
* *
* Returns: FAILED Operation failed or not supported. * Notes:
* SUCCESS Succeeded. * Try a bus device reset. still, look to see whether we have multiple
* * devices that are jammed or not - if we have multiple devices, it
* Notes: There is no timeout for this operation. If this operation is * makes no sense to try bus_device_reset - we really would need to try
* unreliable for a given host, then the host itself needs to put a * a bus_reset instead.
* timer on it, and set the host back to a consistent state prior **/
* to returning. static int scsi_eh_bus_device_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
*/
STATIC int scsi_try_bus_device_reset(Scsi_Cmnd * SCpnt, int timeout)
{ {
unsigned long flags;
int rtn; int rtn;
Scsi_Cmnd *scmd;
Scsi_Device *sdev;
SCpnt->eh_state = FAILED; /* Until we come up with something better */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Trying BDR\n", __FUNCTION__));
if (SCpnt->host->hostt->eh_device_reset_handler == NULL) { for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
return FAILED; for (scmd = sc_todo; scmd; scmd = scmd->bh_next)
} if ((scmd->device == sdev) &&
SCpnt->owner = SCSI_OWNER_LOWLEVEL; scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
break;
spin_lock_irqsave(SCpnt->host->host_lock, flags); if (!scmd)
rtn = SCpnt->host->hostt->eh_device_reset_handler(SCpnt); continue;
spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
if (rtn == SUCCESS) /*
SCpnt->eh_state = SUCCESS; * ok, we have a device that is having problems. try and send
* a bus device reset to it.
*/
rtn = scsi_try_bus_device_reset(scmd);
if ((rtn == SUCCESS) && (scsi_eh_tur(scmd)))
for (scmd = sc_todo; scmd; scmd = scmd->bh_next)
if ((scmd->device == sdev) &&
scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) {
rtn = scsi_eh_retry_cmd(scmd);
if (rtn == SUCCESS)
scsi_eh_finish_cmd(scmd, shost);
}
}
return SCpnt->eh_state; return shost->host_failed;
} }
/* /**
* Function: scsi_try_bus_reset * scsi_try_bus_reset - ask host to perform a bus reset
* * @scmd: SCSI cmd to send bus reset.
* Purpose: Ask host adapter to perform a bus reset for a host. **/
* static int scsi_try_bus_reset(Scsi_Cmnd *scmd)
* Returns: FAILED Operation failed or not supported.
* SUCCESS Succeeded.
*
* Notes:
*/
STATIC int scsi_try_bus_reset(Scsi_Cmnd * SCpnt)
{ {
unsigned long flags; unsigned long flags;
int rtn; int rtn;
Scsi_Device *sdev;
SCpnt->eh_state = FAILED; /* Until we come up with something better */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
SCpnt->owner = SCSI_OWNER_LOWLEVEL; __FUNCTION__));
SCpnt->serial_number_at_timeout = SCpnt->serial_number; scmd->owner = SCSI_OWNER_LOWLEVEL;
scmd->serial_number_at_timeout = scmd->serial_number;
if (SCpnt->host->hostt->eh_bus_reset_handler == NULL) { if (scmd->host->hostt->eh_bus_reset_handler == NULL)
return FAILED; return FAILED;
}
spin_lock_irqsave(SCpnt->host->host_lock, flags); spin_lock_irqsave(scmd->host->host_lock, flags);
rtn = SCpnt->host->hostt->eh_bus_reset_handler(SCpnt); rtn = scmd->host->hostt->eh_bus_reset_handler(scmd);
spin_unlock_irqrestore(SCpnt->host->host_lock, flags); spin_unlock_irqrestore(scmd->host->host_lock, flags);
if (rtn == SUCCESS) if (rtn == SUCCESS) {
SCpnt->eh_state = SUCCESS; scsi_sleep(BUS_RESET_SETTLE_TIME);
/*
/* * Mark all affected devices to expect a unit attention.
* If we had a successful bus reset, mark the command blocks to expect */
* a condition code of unit attention. for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
*/ if (scmd->channel == sdev->channel) {
scsi_sleep(BUS_RESET_SETTLE_TIME); sdev->was_reset = 1;
if (SCpnt->eh_state == SUCCESS) { sdev->expecting_cc_ua = 1;
Scsi_Device *SDloop;
for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) {
if (SCpnt->channel == SDloop->channel) {
SDloop->was_reset = 1;
SDloop->expecting_cc_ua = 1;
} }
}
} }
return SCpnt->eh_state; return rtn;
} }
/* /**
* Function: scsi_try_host_reset * scsi_try_host_reset - ask host adapter to reset itself
* * @scmd: SCSI cmd to send hsot reset.
* Purpose: Ask host adapter to reset itself, and the bus. **/
* static int scsi_try_host_reset(Scsi_Cmnd *scmd)
* Returns: FAILED Operation failed or not supported.
* SUCCESS Succeeded.
*
* Notes:
*/
STATIC int scsi_try_host_reset(Scsi_Cmnd * SCpnt)
{ {
unsigned long flags; unsigned long flags;
int rtn; int rtn;
Scsi_Device *sdev;
SCpnt->eh_state = FAILED; /* Until we come up with something better */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
SCpnt->owner = SCSI_OWNER_LOWLEVEL; __FUNCTION__));
SCpnt->serial_number_at_timeout = SCpnt->serial_number; scmd->owner = SCSI_OWNER_LOWLEVEL;
scmd->serial_number_at_timeout = scmd->serial_number;
if (SCpnt->host->hostt->eh_host_reset_handler == NULL) { if (scmd->host->hostt->eh_host_reset_handler == NULL)
return FAILED; return FAILED;
spin_lock_irqsave(scmd->host->host_lock, flags);
rtn = scmd->host->hostt->eh_host_reset_handler(scmd);
spin_unlock_irqrestore(scmd->host->host_lock, flags);
if (rtn == SUCCESS) {
scsi_sleep(HOST_RESET_SETTLE_TIME);
/*
* Mark all affected devices to expect a unit attention.
*/
for (sdev = scmd->host->host_queue; sdev; sdev = sdev->next)
if (scmd->channel == sdev->channel) {
sdev->was_reset = 1;
sdev->expecting_cc_ua = 1;
}
} }
spin_lock_irqsave(SCpnt->host->host_lock, flags); return rtn;
rtn = SCpnt->host->hostt->eh_host_reset_handler(SCpnt); }
spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
if (rtn == SUCCESS) /**
SCpnt->eh_state = SUCCESS; * scsi_eh_bus_host_reset - send a bus reset and on failure try host reset
* @sc_todo: a list of cmds that have failed.
* @shost: scsi host being recovered.
**/
static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
{
int rtn;
Scsi_Cmnd *scmd;
Scsi_Cmnd *chan_scmd;
unsigned int channel;
/* /*
* If we had a successful host reset, mark the command blocks to expect * if we ended up here, we have serious problems. the only thing left
* a condition code of unit attention. * to try is a full bus reset. if someone has grabbed the bus and isn't
* letting go, then perhaps this will help.
*/
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Try Bus/Host RST\n",
__FUNCTION__));
/*
* we really want to loop over the various channels, and do this on
* a channel by channel basis. we should also check to see if any
* of the failed commands are on soft_reset devices, and if so, skip
* the reset.
*/ */
scsi_sleep(HOST_RESET_SETTLE_TIME);
if (SCpnt->eh_state == SUCCESS) { for (channel = 0; channel <= shost->max_channel; channel++) {
Scsi_Device *SDloop; for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) { if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
SDloop->was_reset = 1; continue;
SDloop->expecting_cc_ua = 1; if (channel == scmd->channel) {
chan_scmd = scmd;
break;
/*
* FIXME add back in some support for
* soft_reset devices.
*/
}
} }
if (!scmd)
continue;
/*
* we now know that we are able to perform a reset for the
* channel that scmd points to.
*/
rtn = scsi_try_bus_reset(scmd);
if (rtn != SUCCESS)
rtn = scsi_try_host_reset(scmd);
if (rtn == SUCCESS) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)
|| channel != scmd->channel)
continue;
if (scsi_eh_tur(scmd)) {
rtn = scsi_eh_retry_cmd(scmd);
if (rtn == SUCCESS)
scsi_eh_finish_cmd(scmd, shost);
}
}
}
} }
return SCpnt->eh_state; return shost->host_failed;
} }
/* /**
* Function: scsi_decide_disposition * scsi_eh_offline_sdevs - offline scsi devices that fail to recover
* @sc_todo: a list of cmds that have failed.
* @shost: scsi host being recovered.
* *
* Purpose: Examine a command block that has come back from the low-level **/
* and figure out what to do next. static void scsi_eh_offline_sdevs(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
{
Scsi_Cmnd *scmd;
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
continue;
printk(KERN_INFO "%s: Device set offline - not"
"ready or command retry failed"
"after error recovery: host"
"%d channel %d id %d lun %d\n",
__FUNCTION__, shost->host_no,
scmd->device->channel,
scmd->device->id,
scmd->device->lun);
scmd->device->online = FALSE;
scsi_eh_finish_cmd(scmd, shost);
}
return;
}
/**
* scsi_sleep_done - timer function for scsi_sleep
* @sem: semphore to signal
* *
* Returns: SUCCESS - pass on to upper level. **/
* FAILED - pass on to error handler thread. static
* RETRY - command should be retried. void scsi_sleep_done(struct semaphore *sem)
* SOFTERR - command succeeded, but we need to log {
* a soft error. if (sem != NULL) {
up(sem);
}
}
/**
* scsi_sleep - sleep for specified timeout
* @timeout: timeout value
* *
* Notes: This is *ONLY* called when we are examining the status **/
* after sending out the actual data command. Any commands void scsi_sleep(int timeout)
* that are queued for error recovery (i.e. TEST_UNIT_READY) {
* do *NOT* come through here. DECLARE_MUTEX_LOCKED(sem);
struct timer_list timer;
init_timer(&timer);
timer.data = (unsigned long) &sem;
timer.expires = jiffies + timeout;
timer.function = (void (*)(unsigned long)) scsi_sleep_done;
SCSI_LOG_ERROR_RECOVERY(5, printk("sleeping for timer tics %d\n",
timeout));
add_timer(&timer);
down(&sem);
del_timer(&timer);
}
/**
* scsi_decide_disposition - Disposition a cmd on return from LLD.
* @scmd: SCSI cmd to examine.
* *
* NOTE - When this routine returns FAILED, it means the error * Notes:
* handler thread is woken. In cases where the error code * This is *only* called when we are examining the status after sending
* indicates an error that doesn't require the error handler * out the actual data command. any commands that are queued for error
* thread (i.e. we don't need to abort/reset), then this function * recovery (i.e. test_unit_ready) do *not* come through here.
* should return SUCCESS. *
*/ * When this routine returns failed, it means the error handler thread
int scsi_decide_disposition(Scsi_Cmnd * SCpnt) * is woken. in cases where the error code indicates an error that
* doesn't require the error handler read (i.e. we don't need to
* abort/reset), then this function should return SUCCESS.
**/
int scsi_decide_disposition(Scsi_Cmnd *scmd)
{ {
int rtn; int rtn;
/* /*
* If the device is offline, then we clearly just pass the result back * if the device is offline, then we clearly just pass the result back
* up to the top level. * up to the top level.
*/ */
if (SCpnt->device->online == FALSE) { if (scmd->device->online == FALSE) {
SCSI_LOG_ERROR_RECOVERY(5, printk("scsi_error.c: device offline - report as SUCCESS\n")); SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
"as SUCCESS\n",
__FUNCTION__));
return SUCCESS; return SUCCESS;
} }
/* /*
* First check the host byte, to see if there is anything in there * first check the host byte, to see if there is anything in there
* that would indicate what we need to do. * that would indicate what we need to do.
*/ */
switch (host_byte(SCpnt->result)) { switch (host_byte(scmd->result)) {
case DID_PASSTHROUGH: case DID_PASSTHROUGH:
/* /*
* No matter what, pass this through to the upper layer. * no matter what, pass this through to the upper layer.
* Nuke this special code so that it looks like we are saying * nuke this special code so that it looks like we are saying
* DID_OK. * did_ok.
*/ */
SCpnt->result &= 0xff00ffff; scmd->result &= 0xff00ffff;
return SUCCESS; return SUCCESS;
case DID_OK: case DID_OK:
/* /*
* Looks good. Drop through, and check the next byte. * looks good. drop through, and check the next byte.
*/ */
break; break;
case DID_NO_CONNECT: case DID_NO_CONNECT:
case DID_BAD_TARGET: case DID_BAD_TARGET:
case DID_ABORT: case DID_ABORT:
/* /*
* Note - this means that we just report the status back to the * note - this means that we just report the status back
* top level driver, not that we actually think that it indicates * to the top level driver, not that we actually think
* success. * that it indicates SUCCESS.
*/ */
return SUCCESS; return SUCCESS;
/* /*
* When the low level driver returns DID_SOFT_ERROR, * when the low level driver returns did_soft_error,
* it is responsible for keeping an internal retry counter * it is responsible for keeping an internal retry counter
* in order to avoid endless loops (DB) * in order to avoid endless loops (db)
* *
* Actually this is a bug in this function here. We should * actually this is a bug in this function here. we should
* be mindful of the maximum number of retries specified * be mindful of the maximum number of retries specified
* and not get stuck in a loop. * and not get stuck in a loop.
*/ */
...@@ -979,896 +1288,252 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt) ...@@ -979,896 +1288,252 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt)
goto maybe_retry; goto maybe_retry;
case DID_ERROR: case DID_ERROR:
if (msg_byte(SCpnt->result) == COMMAND_COMPLETE && if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
status_byte(SCpnt->result) == RESERVATION_CONFLICT) status_byte(scmd->result) == RESERVATION_CONFLICT)
/* /*
* execute reservation conflict processing code * execute reservation conflict processing code
* lower down * lower down
*/ */
break; break;
/* FALLTHROUGH */ /* fallthrough */
case DID_BUS_BUSY: case DID_BUS_BUSY:
case DID_PARITY: case DID_PARITY:
goto maybe_retry; goto maybe_retry;
case DID_TIME_OUT: case DID_TIME_OUT:
/* /*
* When we scan the bus, we get timeout messages for * when we scan the bus, we get timeout messages for
* these commands if there is no device available. * these commands if there is no device available.
* Other hosts report DID_NO_CONNECT for the same thing. * other hosts report did_no_connect for the same thing.
*/
if ((SCpnt->cmnd[0] == TEST_UNIT_READY ||
SCpnt->cmnd[0] == INQUIRY)) {
return SUCCESS;
} else {
return FAILED;
}
case DID_RESET:
/*
* In the normal case where we haven't initiated a reset, this is
* a failure.
*/
if (SCpnt->flags & IS_RESETTING) {
SCpnt->flags &= ~IS_RESETTING;
goto maybe_retry;
}
return SUCCESS;
default:
return FAILED;
}
/*
* Next, check the message byte.
*/
if (msg_byte(SCpnt->result) != COMMAND_COMPLETE) {
return FAILED;
}
/*
* Now, check the status byte to see if this indicates anything special.
*/
switch (status_byte(SCpnt->result)) {
case QUEUE_FULL:
/*
* The case of trying to send too many commands to a tagged queueing
* device.
*/ */
return ADD_TO_MLQUEUE; if ((scmd->cmnd[0] == TEST_UNIT_READY ||
case GOOD: scmd->cmnd[0] == INQUIRY)) {
case COMMAND_TERMINATED: return SUCCESS;
return SUCCESS; } else {
case CHECK_CONDITION: return FAILED;
rtn = scsi_check_sense(SCpnt);
if (rtn == NEEDS_RETRY) {
goto maybe_retry;
}
return rtn;
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
/*
* Who knows? FIXME(eric)
*/
return SUCCESS;
case BUSY:
goto maybe_retry;
case RESERVATION_CONFLICT:
printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n",
SCpnt->host->host_no, SCpnt->channel,
SCpnt->device->id, SCpnt->device->lun);
return SUCCESS; /* causes immediate I/O error */
default:
return FAILED;
}
return FAILED;
maybe_retry:
if ((++SCpnt->retries) < SCpnt->allowed) {
return NEEDS_RETRY;
} else {
/*
* No more retries - report this one back to upper level.
*/
return SUCCESS;
}
}
/*
* Function: scsi_eh_completed_normally
*
* Purpose: Examine a command block that has come back from the low-level
* and figure out what to do next.
*
* Returns: SUCCESS - pass on to upper level.
* FAILED - pass on to error handler thread.
* RETRY - command should be retried.
* SOFTERR - command succeeded, but we need to log
* a soft error.
*
* Notes: This is *ONLY* called when we are examining the status
* of commands queued during error recovery. The main
* difference here is that we don't allow for the possibility
* of retries here, and we are a lot more restrictive about what
* we consider acceptable.
*/
STATIC int scsi_eh_completed_normally(Scsi_Cmnd * SCpnt)
{
int rtn;
/*
* First check the host byte, to see if there is anything in there
* that would indicate what we need to do.
*/
if (host_byte(SCpnt->result) == DID_RESET) {
if (SCpnt->flags & IS_RESETTING) {
/*
* OK, this is normal. We don't know whether in fact
* the command in question really needs to be rerun
* or not - if this was the original data command then
* the answer is yes, otherwise we just flag it as
* success.
*/
SCpnt->flags &= ~IS_RESETTING;
goto maybe_retry;
} }
case DID_RESET:
/* /*
* Rats. We are already in the error handler, so we now * in the normal case where we haven't initiated a reset,
* get to try and figure out what to do next. If the sense * this is a failure.
* is valid, we have a pretty good idea of what to do.
* If not, we mark it as failed.
*/ */
rtn = scsi_check_sense(SCpnt); if (scmd->flags & IS_RESETTING) {
if (rtn == NEEDS_RETRY) scmd->flags &= ~IS_RESETTING;
goto maybe_retry; goto maybe_retry;
return rtn; }
} return SUCCESS;
if (host_byte(SCpnt->result) != DID_OK) { default:
return FAILED; return FAILED;
} }
/* /*
* Next, check the message byte. * next, check the message byte.
*/ */
if (msg_byte(SCpnt->result) != COMMAND_COMPLETE) { if (msg_byte(scmd->result) != COMMAND_COMPLETE) {
return FAILED; return FAILED;
} }
/* /*
* Now, check the status byte to see if this indicates * now, check the status byte to see if this indicates anything special.
* anything special.
*/ */
switch (status_byte(SCpnt->result)) { switch (status_byte(scmd->result)) {
case QUEUE_FULL:
/*
* the case of trying to send too many commands to a
* tagged queueing device.
*/
return ADD_TO_MLQUEUE;
case GOOD: case GOOD:
case COMMAND_TERMINATED: case COMMAND_TERMINATED:
return SUCCESS; return SUCCESS;
case CHECK_CONDITION: case CHECK_CONDITION:
rtn = scsi_check_sense(SCpnt); rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY) if (rtn == NEEDS_RETRY) {
goto maybe_retry; goto maybe_retry;
}
return rtn; return rtn;
case CONDITION_GOOD: case CONDITION_GOOD:
case INTERMEDIATE_GOOD: case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD: case INTERMEDIATE_C_GOOD:
/* /*
* Who knows? FIXME(eric) * who knows? FIXME(eric)
*/ */
return SUCCESS; return SUCCESS;
case BUSY: case BUSY:
case QUEUE_FULL: goto maybe_retry;
case RESERVATION_CONFLICT: case RESERVATION_CONFLICT:
printk("scsi%d (%d,%d,%d) : reservation conflict\n",
scmd->host->host_no, scmd->channel,
scmd->device->id, scmd->device->lun);
return SUCCESS; /* causes immediate i/o error */
default: default:
return FAILED; return FAILED;
} }
return FAILED; return FAILED;
maybe_retry: maybe_retry:
if ((++SCpnt->retries) < SCpnt->allowed) {
return NEEDS_RETRY;
} else {
/* No more retries - report this one back to upper level */
return SUCCESS;
}
}
/*
* Function: scsi_check_sense
*
* Purpose: Examine sense information - give suggestion as to what
* we should do with it.
*/
STATIC int scsi_check_sense(Scsi_Cmnd * SCpnt)
{
if (!scsi_sense_valid(SCpnt)) {
return FAILED;
}
if (SCpnt->sense_buffer[2] & 0xe0)
return SUCCESS;
switch (SCpnt->sense_buffer[2] & 0xf) {
case NO_SENSE:
return SUCCESS;
case RECOVERED_ERROR:
return /* SOFT_ERROR */ SUCCESS;
case ABORTED_COMMAND: if ((++scmd->retries) < scmd->allowed) {
return NEEDS_RETRY; return NEEDS_RETRY;
case NOT_READY: } else {
case UNIT_ATTENTION:
/*
* If we are expecting a CC/UA because of a bus reset that we
* performed, treat this just as a retry. Otherwise this is
* information that we should pass up to the upper-level driver
* so that we can deal with it there.
*/
if (SCpnt->device->expecting_cc_ua) {
SCpnt->device->expecting_cc_ua = 0;
return NEEDS_RETRY;
}
/* /*
* If the device is in the process of becoming ready, we * no more retries - report this one back to upper level.
* should retry.
*/ */
if ((SCpnt->sense_buffer[12] == 0x04) &&
(SCpnt->sense_buffer[13] == 0x01)) {
return NEEDS_RETRY;
}
return SUCCESS;
/* these three are not supported */
case COPY_ABORTED:
case VOLUME_OVERFLOW:
case MISCOMPARE:
return SUCCESS;
case MEDIUM_ERROR:
return NEEDS_RETRY;
case ILLEGAL_REQUEST:
case BLANK_CHECK:
case DATA_PROTECT:
case HARDWARE_ERROR:
default:
return SUCCESS; return SUCCESS;
} }
} }
/**
/* * scsi_restart_operations - restart io operations to the specified host.
* Function: scsi_restart_operations * @shost: Host we are restarting.
*
* Purpose: Restart IO operations to the specified host.
*
* Arguments: host - host that we are restarting
*
* Lock status: Assumed that locks are not held upon entry.
* *
* Returns: Nothing * Notes:
* * When we entered the error handler, we blocked all further i/o to
* Notes: When we entered the error handler, we blocked all further * this device. we need to 'reverse' this process.
* I/O to this device. We need to 'reverse' this process. **/
*/ static void scsi_restart_operations(struct Scsi_Host *shost)
STATIC void scsi_restart_operations(struct Scsi_Host *host)
{ {
Scsi_Device *SDpnt; Scsi_Device *sdev;
unsigned long flags; unsigned long flags;
ASSERT_LOCK(host->host_lock, 0); ASSERT_LOCK(shost->host_lock, 0);
/* /*
* Next free up anything directly waiting upon the host. This will be * next free up anything directly waiting upon the host. this
* requests for character device operations, and also for ioctls to queued * will be requests for character device operations, and also for
* block devices. * ioctls to queued block devices.
*/ */
SCSI_LOG_ERROR_RECOVERY(5, printk("scsi_error.c: Waking up host to restart\n")); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
__FUNCTION__));
wake_up(&host->host_wait); wake_up(&shost->host_wait);
/* /*
* Finally we need to re-initiate requests that may be pending. We will * finally we need to re-initiate requests that may be pending. we will
* have had everything blocked while error handling is taking place, and * have had everything blocked while error handling is taking place, and
* now that error recovery is done, we will need to ensure that these * now that error recovery is done, we will need to ensure that these
* requests are started. * requests are started.
*/ */
spin_lock_irqsave(host->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
request_queue_t *q = &SDpnt->request_queue; request_queue_t *q = &sdev->request_queue;
if ((host->can_queue > 0 && (host->host_busy >= host->can_queue)) if ((shost->can_queue > 0 &&
|| (host->host_blocked) (shost->host_busy >= shost->can_queue))
|| (host->host_self_blocked) || (shost->host_blocked)
|| (SDpnt->device_blocked)) { || (shost->host_self_blocked)
|| (sdev->device_blocked)) {
break; break;
} }
q->request_fn(q); q->request_fn(q);
} }
spin_unlock_irqrestore(host->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
} }
/* /**
* Function: scsi_unjam_host * scsi_unjam_host - Attempt to fix a host which has a cmd that failed.
* * @shost: Host to unjam.
* Purpose: Attempt to fix a host which has a command that failed for
* some reason.
*
* Arguments: host - host that needs unjamming.
*
* Returns: Nothing
*
* Notes: When we come in here, we *know* that all commands on the
* bus have either completed, failed or timed out. We also
* know that no further commands are being sent to the host,
* so things are relatively quiet and we have freedom to
* fiddle with things as we wish.
* *
* Additional note: This is only the *default* implementation. It is possible * Notes:
* for individual drivers to supply their own version of this * When we come in here, we *know* that all commands on the bus have
* function, and if the maintainer wishes to do this, it is * either completed, failed or timed out. we also know that no further
* strongly suggested that this function be taken as a template * commands are being sent to the host, so things are relatively quiet
* and modified. This function was designed to correctly handle * and we have freedom to fiddle with things as we wish.
* problems for about 95% of the different cases out there, and *
* it should always provide at least a reasonable amount of error * This is only the *default* implementation. it is possible for
* recovery. * individual drivers to supply their own version of this function, and
* * if the maintainer wishes to do this, it is strongly suggested that
* Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually * this function be taken as a template and modified. this function
* have scsi_finish_command() called for it. We do all of * was designed to correctly handle problems for about 95% of the
* the retry stuff here, so when we restart the host after we * different cases out there, and it should always provide at least a
* return it should have an empty queue. * reasonable amount of error recovery.
*/ *
STATIC int scsi_unjam_host(struct Scsi_Host *host) * Any command marked 'failed' or 'timeout' must eventually have
* scsi_finish_cmd() called for it. we do all of the retry stuff
* here, so when we restart the host after we return it should have an
* empty queue.
**/
static void scsi_unjam_host(struct Scsi_Host *shost)
{ {
int devices_failed; Scsi_Cmnd *sc_todo = NULL;
int numfailed; Scsi_Cmnd *scmd;
int ourrtn;
int rtn = FALSE;
int result;
Scsi_Cmnd *SCloop;
Scsi_Cmnd *SCpnt;
Scsi_Device *SDpnt;
Scsi_Device *SDloop;
Scsi_Cmnd *SCdone;
int timed_out;
ASSERT_LOCK(host->host_lock, 0);
SCdone = NULL;
/*
* First, protect against any sort of race condition. If any of the outstanding
* commands are in states that indicate that we are not yet blocked (i.e. we are
* not in a quiet state) then we got woken up in error. If we ever end up here,
* we need to re-examine some of the assumptions.
*/
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
if (SCpnt->state == SCSI_STATE_FAILED
|| SCpnt->state == SCSI_STATE_TIMEOUT
|| SCpnt->state == SCSI_STATE_INITIALIZING
|| SCpnt->state == SCSI_STATE_UNUSED) {
continue;
}
/*
* Rats. Something is still floating around out there. This could
* be the result of the fact that the upper level drivers are still frobbing
* commands that might have succeeded. There are two outcomes. One is that
* the command block will eventually be freed, and the other one is that
* the command will be queued and will be finished along the way.
*/
SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target));
/*
* panic("SCSI Error handler woken too early\n");
*
* This is no longer a problem, since now the code cares only about
* SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED.
* Other states are useful only to release active commands when devices are
* set offline. If (host->host_active == host->host_busy) we can safely assume
* that there are no commands in state other then TIMEOUT od FAILED. (DB)
*
* FIXME:
* It is not easy to release correctly commands according to their state when
* devices are set offline, when the state is neither TIMEOUT nor FAILED.
* When a device is set offline, we can have some command with
* rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL,
* state=SCSI_STATE_INITIALIZING and the driver module cannot be released.
* (DB, 17 May 1998)
*/
}
}
/*
* Next, see if we need to request sense information. if so,
* then get it now, so we have a better idea of what to do.
* FIXME(eric) this has the unfortunate side effect that if a host
* adapter does not automatically request sense information, that we end
* up shutting it down before we request it. All hosts should be doing this
* anyways, so for now all I have to say is tough noogies if you end up in here.
* On second thought, this is probably a good idea. We *really* want to give
* authors an incentive to automatically request this.
*/
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we need to request sense\n"));
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) {
continue;
}
SCSI_LOG_ERROR_RECOVERY(2, printk("scsi_unjam_host: Requesting sense for %d\n",
SCpnt->target));
rtn = scsi_request_sense(SCpnt);
if (rtn != SUCCESS) {
continue;
}
SCSI_LOG_ERROR_RECOVERY(3, printk("Sense requested for %p - result %x\n",
SCpnt, SCpnt->result));
SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", SCpnt));
result = scsi_decide_disposition(SCpnt);
/*
* If the result was normal, then just pass it along to the
* upper level.
*/
if (result == SUCCESS) {
SCpnt->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCpnt);
}
if (result != NEEDS_RETRY) {
continue;
}
/*
* We only come in here if we want to retry a
* command. The test to see whether the command
* should be retried should be keeping track of the
* number of tries, so we don't end up looping, of
* course.
*/
SCpnt->state = NEEDS_RETRY;
rtn = scsi_eh_retry_command(SCpnt);
if (rtn != SUCCESS) {
continue;
}
/*
* We eventually hand this one back to the top level.
*/
SCpnt->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCpnt);
}
}
/*
* Go through the list of commands and figure out where we stand and how bad things
* really are.
*/
numfailed = 0;
timed_out = 0;
devices_failed = 0;
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
unsigned int device_error = 0;
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
if (SCpnt->state == SCSI_STATE_FAILED) {
SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d failed\n",
SCpnt->target));
numfailed++;
device_error++;
}
if (SCpnt->state == SCSI_STATE_TIMEOUT) {
SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d timedout\n",
SCpnt->target));
timed_out++;
device_error++;
}
}
if (device_error > 0) {
devices_failed++;
}
}
SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d+%d commands on %d devices require eh work\n",
numfailed, timed_out, devices_failed));
if (host->host_failed == 0) {
ourrtn = TRUE;
goto leave;
}
/*
* Next, try and see whether or not it makes sense to try and abort
* the running command. This only works out to be the case if we have
* one command that has timed out. If the command simply failed, it
* makes no sense to try and abort the command, since as far as the
* host adapter is concerned, it isn't running.
*/
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try abort\n"));
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->state != SCSI_STATE_TIMEOUT) {
continue;
}
rtn = scsi_try_to_abort_command(SCloop, ABORT_TIMEOUT);
if (rtn == SUCCESS) {
rtn = scsi_test_unit_ready(SCloop);
if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
rtn = scsi_eh_retry_command(SCloop);
if (rtn == SUCCESS) {
SCloop->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
}
}
}
/*
* If we have corrected all of the problems, then we are done.
*/
if (host->host_failed == 0) {
ourrtn = TRUE;
goto leave;
}
/*
* Either the abort wasn't appropriate, or it didn't succeed.
* Now try a bus device reset. Still, look to see whether we have
* multiple devices that are jammed or not - if we have multiple devices,
* it makes no sense to try BUS_DEVICE_RESET - we really would need
* to try a BUS_RESET instead.
*
* Does this make sense - should we try BDR on each device individually?
* Yes, definitely.
*/
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try BDR\n"));
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->state == SCSI_STATE_FAILED
|| SCloop->state == SCSI_STATE_TIMEOUT) {
break;
}
}
if (SCloop == NULL) {
continue;
}
/*
* OK, we have a device that is having problems. Try and send
* a bus device reset to it.
*
* FIXME(eric) - make sure we handle the case where multiple
* commands to the same device have failed. They all must
* get properly restarted.
*/
rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT);
if (rtn == SUCCESS) {
rtn = scsi_test_unit_ready(SCloop);
if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
rtn = scsi_eh_retry_command(SCloop);
if (rtn == SUCCESS) {
SCloop->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
}
}
if (host->host_failed == 0) {
ourrtn = TRUE;
goto leave;
}
/*
* If we ended up here, we have serious problems. The only thing left
* to try is a full bus reset. If someone has grabbed the bus and isn't
* letting go, then perhaps this will help.
*/
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard bus reset\n"));
/*
* We really want to loop over the various channels, and do this on
* a channel by channel basis. We should also check to see if any
* of the failed commands are on soft_reset devices, and if so, skip
* the reset.
*/
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
next_device:
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
if (SCpnt->state != SCSI_STATE_FAILED
&& SCpnt->state != SCSI_STATE_TIMEOUT) {
continue;
}
/*
* We have a failed command. Make sure there are no other failed
* commands on the same channel that are timed out and implement a
* soft reset.
*/
for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->channel != SCpnt->channel) {
continue;
}
if (SCloop->state != SCSI_STATE_FAILED
&& SCloop->state != SCSI_STATE_TIMEOUT) {
continue;
}
if (SDloop->soft_reset && SCloop->state == SCSI_STATE_TIMEOUT) {
/*
* If this device uses the soft reset option, and this
* is one of the devices acting up, then our only
* option is to wait a bit, since the command is
* supposedly still running.
*
* FIXME(eric) - right now we will just end up falling
* through to the 'take device offline' case.
*
* FIXME(eric) - It is possible that the command completed
* *after* the error recovery procedure started, and if this
* is the case, we are worrying about nothing here.
*/
scsi_sleep(1 * HZ);
goto next_device;
}
}
}
/*
* We now know that we are able to perform a reset for the
* bus that SCpnt points to. There are no soft-reset devices
* with outstanding timed out commands.
*/
rtn = scsi_try_bus_reset(SCpnt);
if (rtn == SUCCESS) {
for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->channel != SCpnt->channel) {
continue;
}
if (SCloop->state != SCSI_STATE_FAILED
&& SCloop->state != SCSI_STATE_TIMEOUT) {
continue;
}
rtn = scsi_test_unit_ready(SCloop);
if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
rtn = scsi_eh_retry_command(SCloop);
if (rtn == SUCCESS) {
SCpnt->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
/*
* If the bus reset worked, but we are still unable to
* talk to the device, take it offline.
* FIXME(eric) - is this really the correct thing to do?
*/
if (rtn != SUCCESS) {
printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after bus reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
SDloop->online = FALSE;
SDloop->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
}
}
}
}
if (host->host_failed == 0) {
ourrtn = TRUE;
goto leave;
}
/*
* If we ended up here, we have serious problems. The only thing left
* to try is a full host reset - perhaps the firmware on the device
* crashed, or something like that.
*
* It is assumed that a succesful host reset will cause *all* information
* about the command to be flushed from both the host adapter *and* the
* device.
*
* FIXME(eric) - it isn't clear that devices that implement the soft reset
* option can ever be cleared except via cycling the power. The problem is
* that sending the host reset command will cause the host to forget
* about the pending command, but the device won't forget. For now, we
* skip the host reset option if any of the failed devices are configured
* to use the soft reset option.
*/
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
next_device2:
for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
if (SCpnt->state != SCSI_STATE_FAILED
&& SCpnt->state != SCSI_STATE_TIMEOUT) {
continue;
}
if (SDpnt->soft_reset && SCpnt->state == SCSI_STATE_TIMEOUT) {
/*
* If this device uses the soft reset option, and this
* is one of the devices acting up, then our only
* option is to wait a bit, since the command is
* supposedly still running.
*
* FIXME(eric) - right now we will just end up falling
* through to the 'take device offline' case.
*/
SCSI_LOG_ERROR_RECOVERY(3,
printk("scsi_unjam_host: Unable to try hard host reset\n"));
/*
* Due to the spinlock, we will never get out of this
* loop without a proper wait. (DB)
*/
scsi_sleep(1 * HZ);
goto next_device2;
}
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard host reset\n"));
/*
* FIXME(eric) - we need to obtain a valid SCpnt to perform this call.
*/
rtn = scsi_try_host_reset(SCpnt);
if (rtn == SUCCESS) {
/*
* FIXME(eric) we assume that all commands are flushed from the
* controller. We should get a DID_RESET for all of the commands
* that were pending. We should ignore these so that we can
* guarantee that we are in a consistent state.
*
* I believe this to be the case right now, but this needs to be
* tested.
*/
for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->state != SCSI_STATE_FAILED
&& SCloop->state != SCSI_STATE_TIMEOUT) {
continue;
}
rtn = scsi_test_unit_ready(SCloop);
if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
rtn = scsi_eh_retry_command(SCloop);
if (rtn == SUCCESS) {
SCpnt->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
if (rtn != SUCCESS) {
printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after host reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
SDloop->online = FALSE;
SDloop->host->host_failed--;
scsi_eh_finish_command(&SCdone, SCloop);
}
}
}
}
}
}
/* /*
* If we solved all of the problems, then let's rev up the engines again. * Is this assert really ok anymore (andmike). Should we at least
*/ * be using spin_lock_unlocked.
if (host->host_failed == 0) {
ourrtn = TRUE;
goto leave;
}
/*
* If the HOST RESET failed, then for now we assume that the entire host
* adapter is too hosed to be of any use. For our purposes, however, it is
* easier to simply take the devices offline that correspond to commands
* that failed.
*/ */
SCSI_LOG_ERROR_RECOVERY(1, printk("scsi_unjam_host: Take device offline\n")); ASSERT_LOCK(shost->host_lock, 0);
for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
if (SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT) {
SDloop = SCloop->device;
if (SDloop->online == TRUE) {
printk(KERN_INFO "scsi: device set offline - command error recover failed: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
SDloop->online = FALSE;
}
/*
* This should pass the failure up to the top level driver, and
* it will have to try and do something intelligent with it.
*/
SCloop->host->host_failed--;
if (SCloop->state == SCSI_STATE_TIMEOUT) {
SCloop->result |= (DRIVER_TIMEOUT << 24);
}
SCSI_LOG_ERROR_RECOVERY(3, printk("Finishing command for device %d %x\n",
SDloop->id, SCloop->result));
scsi_eh_finish_command(&SCdone, SCloop); scsi_eh_get_failed(&sc_todo, shost);
}
}
}
if (host->host_failed != 0) { if (scsi_eh_get_sense(sc_todo, shost))
panic("scsi_unjam_host: Miscount of number of failed commands.\n"); if (scsi_eh_abort_cmd(sc_todo, shost))
} if (scsi_eh_bus_device_reset(sc_todo, shost))
SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Returning\n")); if(scsi_eh_bus_host_reset(sc_todo, shost))
scsi_eh_offline_sdevs(sc_todo, shost);
ourrtn = FALSE; BUG_ON(shost->host_failed);
leave:
/* /*
* We should have a list of commands that we 'finished' during the course of * We are currently holding these things in a linked list - we
* error recovery. This should be the same as the list of commands that timed out * didn't put them in the bottom half queue because we wanted to
* or failed. We are currently holding these things in a linked list - we didn't * keep things quiet while we were working on recovery, and
* put them in the bottom half queue because we wanted to keep things quiet while * passing them up to the top level could easily cause the top
* we were working on recovery, and passing them up to the top level could easily * level to try and queue something else again.
* cause the top level to try and queue something else again.
* *
* Start by marking that the host is no longer in error recovery. * start by marking that the host is no longer in error recovery.
*/ */
host->in_recovery = 0; shost->in_recovery = 0;
/* /*
* Take the list of commands, and stick them in the bottom half queue. * take the list of commands, and stick them in the bottom half queue.
* The current implementation of scsi_done will do this for us - if need * the current implementation of scsi_done will do this for us - if need
* be we can create a special version of this function to do the * be we can create a special version of this function to do the
* same job for us. * same job for us.
*/ */
for (SCpnt = SCdone; SCpnt != NULL; SCpnt = SCdone) { for (scmd = sc_todo; scmd; scmd = sc_todo) {
SCdone = SCpnt->bh_next; sc_todo = scmd->bh_next;
SCpnt->bh_next = NULL; scmd->bh_next = NULL;
/* /*
* Oh, this is a vile hack. scsi_done() expects a timer * Oh, this is a vile hack. scsi_done() expects a timer
* to be running on the command. If there isn't, it assumes * to be running on the command. If there isn't, it assumes
* that the command has actually timed out, and a timer * that the command has actually timed out, and a timer
* handler is running. That may well be how we got into * handler is running. That may well be how we got into
* this fix, but right now things are stable. We add * this fix, but right now things are stable. We add
* a timer back again so that we can report completion. * a timer back again so that we can report completion.
* scsi_done() will immediately remove said timer from * scsi_done() will immediately remove said timer from
* the command, and then process it. * the command, and then process it.
*/ */
scsi_add_timer(SCpnt, 100, scsi_eh_times_out); scsi_add_timer(scmd, 100, scsi_eh_times_out);
scsi_done(SCpnt); scsi_done(scmd);
} }
return (ourrtn);
} }
/**
/* * scsi_error_handler - Handle errors/timeouts of SCSI cmds.
* Function: scsi_error_handler * @data: Host for which we are running.
*
* Purpose: Handle errors/timeouts of scsi commands, try and clean up
* and unjam the bus, and restart things.
*
* Arguments: host - host for which we are running.
*
* Returns: Never returns.
*
* Notes: This is always run in the context of a kernel thread. The
* idea is that we start this thing up when the kernel starts
* up (one per host that we detect), and it immediately goes to
* sleep and waits for some event (i.e. failure). When this
* takes place, we have the job of trying to unjam the bus
* and restarting things.
* *
*/ * Notes:
* This is always run in the context of a kernel thread. The idea is
* that we start this thing up when the kernel starts up (one per host
* that we detect), and it immediately goes to sleep and waits for some
* event (i.e. failure). When this takes place, we have the job of
* trying to unjam the bus and restarting things.
**/
void scsi_error_handler(void *data) void scsi_error_handler(void *data)
{ {
struct Scsi_Host *host = (struct Scsi_Host *) data; struct Scsi_Host *shost = (struct Scsi_Host *) data;
int rtn; int rtn;
DECLARE_MUTEX_LOCKED(sem); DECLARE_MUTEX_LOCKED(sem);
/* /*
* We only listen to signals if the HA was loaded as a module. * We only listen to signals if the HA was loaded as a module.
* If the HA was compiled into the kernel, then we don't listen * If the HA was compiled into the kernel, then we don't listen
* to any signals. * to any signals.
*/ */
siginitsetinv(&current->blocked, SHUTDOWN_SIGS); siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
lock_kernel(); lock_kernel();
...@@ -1883,19 +1548,20 @@ void scsi_error_handler(void *data) ...@@ -1883,19 +1548,20 @@ void scsi_error_handler(void *data)
* Set the name of this process. * Set the name of this process.
*/ */
sprintf(current->comm, "scsi_eh_%d", host->host_no); sprintf(current->comm, "scsi_eh_%d", shost->host_no);
host->eh_wait = &sem; shost->eh_wait = &sem;
host->ehandler = current; shost->ehandler = current;
unlock_kernel(); unlock_kernel();
/* /*
* Wake up the thread that created us. * Wake up the thread that created us.
*/ */
SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent %d\n", host->eh_notify->count.counter)); SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent %d\n",
shost->eh_notify->count.counter));
up(host->eh_notify); up(shost->eh_notify);
while (1) { while (1) {
/* /*
...@@ -1920,20 +1586,20 @@ void scsi_error_handler(void *data) ...@@ -1920,20 +1586,20 @@ void scsi_error_handler(void *data)
SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler waking up\n")); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler waking up\n"));
host->eh_active = 1; shost->eh_active = 1;
/* /*
* We have a host that is failing for some reason. Figure out * We have a host that is failing for some reason. Figure out
* what we need to do to get it up and online again (if we can). * what we need to do to get it up and online again (if we can).
* If we fail, we end up taking the thing offline. * If we fail, we end up taking the thing offline.
*/ */
if (host->hostt->eh_strategy_handler != NULL) { if (shost->hostt->eh_strategy_handler != NULL) {
rtn = host->hostt->eh_strategy_handler(host); rtn = shost->hostt->eh_strategy_handler(shost);
} else { } else {
rtn = scsi_unjam_host(host); scsi_unjam_host(shost);
} }
host->eh_active = 0; shost->eh_active = 0;
/* /*
* Note - if the above fails completely, the action is to take * Note - if the above fails completely, the action is to take
...@@ -1942,7 +1608,7 @@ void scsi_error_handler(void *data) ...@@ -1942,7 +1608,7 @@ void scsi_error_handler(void *data)
* restart, we restart any I/O to any other devices on the bus * restart, we restart any I/O to any other devices on the bus
* which are still online. * which are still online.
*/ */
scsi_restart_operations(host); scsi_restart_operations(shost);
} }
...@@ -1951,7 +1617,7 @@ void scsi_error_handler(void *data) ...@@ -1951,7 +1617,7 @@ void scsi_error_handler(void *data)
/* /*
* Make sure that nobody tries to wake us up again. * Make sure that nobody tries to wake us up again.
*/ */
host->eh_wait = NULL; shost->eh_wait = NULL;
/* /*
* Knock this down too. From this point on, the host is flying * Knock this down too. From this point on, the host is flying
...@@ -1959,9 +1625,9 @@ void scsi_error_handler(void *data) ...@@ -1959,9 +1625,9 @@ void scsi_error_handler(void *data)
* that's fine. If the user sent a signal to this thing, we are * that's fine. If the user sent a signal to this thing, we are
* potentially in real danger. * potentially in real danger.
*/ */
host->in_recovery = 0; shost->in_recovery = 0;
host->eh_active = 0; shost->eh_active = 0;
host->ehandler = NULL; shost->ehandler = NULL;
/* /*
* If anyone is waiting for us to exit (i.e. someone trying to unload * If anyone is waiting for us to exit (i.e. someone trying to unload
...@@ -1971,41 +1637,39 @@ void scsi_error_handler(void *data) ...@@ -1971,41 +1637,39 @@ void scsi_error_handler(void *data)
* the error handling thread wakes up that it would just exit without * the error handling thread wakes up that it would just exit without
* needing to touch any memory associated with the driver itself. * needing to touch any memory associated with the driver itself.
*/ */
if (host->eh_notify != NULL) if (shost->eh_notify != NULL)
up(host->eh_notify); up(shost->eh_notify);
} }
/* /**
* Function: scsi_new_reset * scsi_new_reset - Send reset to a bus or device at any phase.
* * @scmd: Cmd to send reset with (usually a dummy)
* Purpose: Send requested reset to a bus or device at any phase. * @flag: Reset type.
* *
* Arguments: SCpnt - command ptr to send reset with (usually a dummy) * Description:
* flag - reset type (see scsi.h) * This is used by the SCSI Generic driver to provide Bus/Device reset
* capability.
* *
* Returns: SUCCESS/FAILURE. * Return value:
* * SUCCESS/FAILED.
* Notes: This is used by the SCSI Generic driver to provide **/
* Bus/Device reset capability. int scsi_new_reset(Scsi_Cmnd *scmd, int flag)
*/
int
scsi_new_reset(Scsi_Cmnd *SCpnt, int flag)
{ {
int rtn; int rtn;
switch(flag) { switch(flag) {
case SCSI_TRY_RESET_DEVICE: case SCSI_TRY_RESET_DEVICE:
rtn = scsi_try_bus_device_reset(SCpnt, 0); rtn = scsi_try_bus_device_reset(scmd);
if (rtn == SUCCESS) if (rtn == SUCCESS)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
case SCSI_TRY_RESET_BUS: case SCSI_TRY_RESET_BUS:
rtn = scsi_try_bus_reset(SCpnt); rtn = scsi_try_bus_reset(scmd);
if (rtn == SUCCESS) if (rtn == SUCCESS)
break; break;
/* FALLTHROUGH */ /* FALLTHROUGH */
case SCSI_TRY_RESET_HOST: case SCSI_TRY_RESET_HOST:
rtn = scsi_try_host_reset(SCpnt); rtn = scsi_try_host_reset(scmd);
break; break;
default: default:
rtn = FAILED; rtn = FAILED;
......
...@@ -140,7 +140,7 @@ void scsi_initialize_merge_fn(Scsi_Device * SDpnt) ...@@ -140,7 +140,7 @@ void scsi_initialize_merge_fn(Scsi_Device * SDpnt)
* Enable highmem I/O, if appropriate. * Enable highmem I/O, if appropriate.
*/ */
bounce_limit = BLK_BOUNCE_HIGH; bounce_limit = BLK_BOUNCE_HIGH;
if (SHpnt->highmem_io && (SDpnt->type == TYPE_DISK)) { if (SHpnt->highmem_io) {
if (!PCI_DMA_BUS_IS_PHYS) if (!PCI_DMA_BUS_IS_PHYS)
/* Platforms with virtual-DMA translation /* Platforms with virtual-DMA translation
* hardware have no practical limit. * hardware have no practical limit.
......
...@@ -82,6 +82,19 @@ static void sg_proc_cleanup(void); ...@@ -82,6 +82,19 @@ static void sg_proc_cleanup(void);
#define SG_MAX_DEVS_MASK ((1U << KDEV_MINOR_BITS) - 1) #define SG_MAX_DEVS_MASK ((1U << KDEV_MINOR_BITS) - 1)
/*
* Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d)
* Then when using 32 bit integers x * m may overflow during the calculation.
* Replacing muldiv(x) by muldiv(x)=((x % d) * m) / d + int(x / d) * m
* calculates the same, but prevents the overflow when both m and d
* are "small" numbers (like HZ and USER_HZ).
* Of course an overflow is inavoidable if the result of muldiv doesn't fit
* in 32 bits.
*/
#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL))
#define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)
int sg_big_buff = SG_DEF_RESERVED_SIZE; int sg_big_buff = SG_DEF_RESERVED_SIZE;
/* N.B. This variable is readable and writeable via /* N.B. This variable is readable and writeable via
/proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer
...@@ -150,7 +163,8 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ ...@@ -150,7 +163,8 @@ typedef struct sg_fd { /* holds the state of a file descriptor */
struct sg_device *parentdp; /* owning device */ struct sg_device *parentdp; /* owning device */
wait_queue_head_t read_wait; /* queue read until command done */ wait_queue_head_t read_wait; /* queue read until command done */
rwlock_t rq_list_lock; /* protect access to list in req_arr */ rwlock_t rq_list_lock; /* protect access to list in req_arr */
int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
Sg_scatter_hold reserve; /* buffer held for this file descriptor */ Sg_scatter_hold reserve; /* buffer held for this file descriptor */
unsigned save_scat_len; /* original length of trunc. scat. element */ unsigned save_scat_len; /* original length of trunc. scat. element */
Sg_request *headrp; /* head of request slist, NULL->empty */ Sg_request *headrp; /* head of request slist, NULL->empty */
...@@ -790,10 +804,15 @@ sg_ioctl(struct inode *inode, struct file *filp, ...@@ -790,10 +804,15 @@ sg_ioctl(struct inode *inode, struct file *filp,
return result; return result;
if (val < 0) if (val < 0)
return -EIO; return -EIO;
sfp->timeout = val; if (val >= MULDIV (INT_MAX, USER_HZ, HZ))
val = MULDIV (INT_MAX, USER_HZ, HZ);
sfp->timeout_user = val;
sfp->timeout = MULDIV (val, HZ, USER_HZ);
return 0; return 0;
case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */
return sfp->timeout; /* strange ..., for backward compatibility */ /* strange ..., for backward compatibility */
return sfp->timeout_user;
case SG_SET_FORCE_LOW_DMA: case SG_SET_FORCE_LOW_DMA:
result = get_user(val, (int *) arg); result = get_user(val, (int *) arg);
if (result) if (result)
...@@ -2432,6 +2451,7 @@ sg_add_sfp(Sg_device * sdp, int dev) ...@@ -2432,6 +2451,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
sfp->rq_list_lock = RW_LOCK_UNLOCKED; sfp->rq_list_lock = RW_LOCK_UNLOCKED;
sfp->timeout = SG_DEFAULT_TIMEOUT; sfp->timeout = SG_DEFAULT_TIMEOUT;
sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
sfp->force_packid = SG_DEF_FORCE_PACK_ID; sfp->force_packid = SG_DEF_FORCE_PACK_ID;
sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
sdp->device->host->unchecked_isa_dma : 1; sdp->device->host->unchecked_isa_dma : 1;
......
...@@ -304,7 +304,12 @@ struct sg_header ...@@ -304,7 +304,12 @@ struct sg_header
/* Defaults, commented if they differ from original sg driver */ /* Defaults, commented if they differ from original sg driver */
#define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */ #ifdef __KERNEL__
#define SG_DEFAULT_TIMEOUT_USER (60*USER_HZ) /* HZ == 'jiffies in 1 second' */
#else
#define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */
#endif
#define SG_DEF_COMMAND_Q 0 /* command queuing is always on when #define SG_DEF_COMMAND_Q 0 /* command queuing is always on when
the new interface is used */ the new interface is used */
#define SG_DEF_UNDERRUN_FLAG 0 #define SG_DEF_UNDERRUN_FLAG 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment