Commit f57d08d1 authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

[PATCH] SCSI Flexible timout intfrastructure

The object of this infrastructure is to give HBAs early warning that
error handling is about to happen and also provide them with the
opportunity to do something about it.

It introduces the extra template callback:

eh_timed_out()

which scsi_times_out() will call if it is populated to notify the LLD
that an outstanding command took a timeout.

There are three possible returns:

EH_HANDLED:	I've fixed the problem, please complete the command for me
(as soon as the timer fires, scsi_done will do nothing, so the timer
itself will call a special version of scsi_done that doesn't check the
timer).

EH_NOT_HANDLED:	Invoke error recovery as normal

EH_RESET_TIMER:	The command will complete, reset the timer to its
original value and start it ticking again.
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
Based on work by Christoph Hellwig and Luben Tuikov
parent f0378b9d
...@@ -827,6 +827,7 @@ The interface functions are listed below in alphabetical order. ...@@ -827,6 +827,7 @@ The interface functions are listed below in alphabetical order.
Summary: Summary:
bios_param - fetch head, sector, cylinder info for a disk bios_param - fetch head, sector, cylinder info for a disk
detect - detects HBAs this driver wants to control detect - detects HBAs this driver wants to control
eh_timed_out - notify the host that a command timer expired
eh_abort_handler - abort given command eh_abort_handler - abort given command
eh_bus_reset_handler - issue SCSI bus reset eh_bus_reset_handler - issue SCSI bus reset
eh_device_reset_handler - issue SCSI device reset eh_device_reset_handler - issue SCSI device reset
...@@ -894,6 +895,32 @@ Details: ...@@ -894,6 +895,32 @@ Details:
int detect(struct scsi_host_template * shtp) int detect(struct scsi_host_template * shtp)
/**
* eh_timed_out - The timer for the command has just fired
* @scp: identifies command timing out
*
* Returns:
*
* EH_HANDLED: I fixed the error, please complete the command
* EH_RESET_TIMER: I need more time, reset the timer and
* begin counting again
* EH_NOT_HANDLED Begin normal error recovery
*
* Locks: None held
*
* Calling context: interrupt
*
* Notes: This is to give the LLD an opportunity to do local recovery.
* This recovery is limited to determining if the outstanding command
* will ever complete. You may not abort and restart the command from
* this callback.
*
* Optionally defined in: LLD
**/
int eh_timed_out(struct scsi_cmnd * scp)
/** /**
* eh_abort_handler - abort command associated with scp * eh_abort_handler - abort command associated with scp
* @scp: identifies command to be aborted * @scp: identifies command to be aborted
......
...@@ -689,8 +689,6 @@ static DEFINE_PER_CPU(struct list_head, scsi_done_q); ...@@ -689,8 +689,6 @@ static DEFINE_PER_CPU(struct list_head, scsi_done_q);
*/ */
void scsi_done(struct scsi_cmnd *cmd) void scsi_done(struct scsi_cmnd *cmd)
{ {
unsigned long flags;
/* /*
* We don't have to worry about this one timing out any more. * We don't have to worry about this one timing out any more.
* If we are unable to remove the timer, then the command * If we are unable to remove the timer, then the command
...@@ -701,6 +699,14 @@ void scsi_done(struct scsi_cmnd *cmd) ...@@ -701,6 +699,14 @@ void scsi_done(struct scsi_cmnd *cmd)
*/ */
if (!scsi_delete_timer(cmd)) if (!scsi_delete_timer(cmd))
return; return;
__scsi_done(cmd);
}
/* Private entry to scsi_done() to complete a command when the timer
* isn't running --- used by scsi_times_out */
void __scsi_done(struct scsi_cmnd *cmd)
{
unsigned long flags;
/* /*
* Set the serial numbers back to zero * Set the serial numbers back to zero
......
...@@ -162,6 +162,24 @@ int scsi_delete_timer(struct scsi_cmnd *scmd) ...@@ -162,6 +162,24 @@ int scsi_delete_timer(struct scsi_cmnd *scmd)
void scsi_times_out(struct scsi_cmnd *scmd) void scsi_times_out(struct scsi_cmnd *scmd)
{ {
scsi_log_completion(scmd, TIMEOUT_ERROR); scsi_log_completion(scmd, TIMEOUT_ERROR);
if (scmd->device->host->hostt->eh_timed_out)
switch (scmd->device->host->hostt->eh_timed_out(scmd)) {
case EH_HANDLED:
__scsi_done(scmd);
return;
case EH_RESET_TIMER:
/* This allows a single retry even of a command
* with allowed == 0 */
if (scmd->retries++ > scmd->allowed)
break;
scsi_add_timer(scmd, scmd->timeout_per_command,
scsi_times_out);
return;
case EH_NOT_HANDLED:
break;
}
if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
panic("Error handler thread not present at %p %p %s %d", panic("Error handler thread not present at %p %p %s %d",
scmd, scmd->device->host, __FILE__, __LINE__); scmd, scmd->device->host, __FILE__, __LINE__);
......
...@@ -82,6 +82,7 @@ extern int scsi_insert_special_req(struct scsi_request *sreq, int); ...@@ -82,6 +82,7 @@ extern int scsi_insert_special_req(struct scsi_request *sreq, int);
extern void scsi_init_cmd_from_req(struct scsi_cmnd *cmd, extern void scsi_init_cmd_from_req(struct scsi_cmnd *cmd,
struct scsi_request *sreq); struct scsi_request *sreq);
extern void __scsi_release_request(struct scsi_request *sreq); extern void __scsi_release_request(struct scsi_request *sreq);
extern void __scsi_done(struct scsi_cmnd *cmd);
#ifdef CONFIG_SCSI_LOGGING #ifdef CONFIG_SCSI_LOGGING
void scsi_log_send(struct scsi_cmnd *cmd); void scsi_log_send(struct scsi_cmnd *cmd);
void scsi_log_completion(struct scsi_cmnd *cmd, int disposition); void scsi_log_completion(struct scsi_cmnd *cmd, int disposition);
......
...@@ -30,6 +30,12 @@ struct scsi_transport_template; ...@@ -30,6 +30,12 @@ struct scsi_transport_template;
#define DISABLE_CLUSTERING 0 #define DISABLE_CLUSTERING 0
#define ENABLE_CLUSTERING 1 #define ENABLE_CLUSTERING 1
enum scsi_eh_timer_return {
EH_NOT_HANDLED,
EH_HANDLED,
EH_RESET_TIMER,
};
struct scsi_host_template { struct scsi_host_template {
struct module *module; struct module *module;
...@@ -125,6 +131,20 @@ struct scsi_host_template { ...@@ -125,6 +131,20 @@ struct scsi_host_template {
int (* eh_bus_reset_handler)(struct scsi_cmnd *); int (* eh_bus_reset_handler)(struct scsi_cmnd *);
int (* eh_host_reset_handler)(struct scsi_cmnd *); int (* eh_host_reset_handler)(struct scsi_cmnd *);
/*
* This is an optional routine to notify the host that the scsi
* timer just fired. The returns tell the timer routine what to
* do about this:
*
* EH_HANDLED: I fixed the error, please complete the command
* EH_RESET_TIMER: I need more time, reset the timer and
* begin counting again
* EH_NOT_HANDLED Begin normal error recovery
*
* Status: OPTIONAL
*/
enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
/* /*
* Old EH handlers, no longer used. Make them warn the user of old * Old EH handlers, no longer used. Make them warn the user of old
* drivers by using a wrong type * drivers by using a wrong type
......
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