Commit 8b0f29fb authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-scsi.bkbits.net/scsi-for-linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 227b2020 6fe854d9
...@@ -22,13 +22,28 @@ has its own PCI device address. [The one-to-one correspondance between ...@@ -22,13 +22,28 @@ has its own PCI device address. [The one-to-one correspondance between
a SCSI host and a PCI device is common but not required (e.g. with a SCSI host and a PCI device is common but not required (e.g. with
ISA or MCA adapters).] ISA or MCA adapters).]
This version of the document roughly matches linux kernel version 2.5.63 .
Documentation
=============
There is a SCSI documentation directory within the kernel source tree.
That directory is typically /usr/src/linux/Documentation/scsi . Most
documents are in plain (i.e. ASCII) text. This file can be found in that
directory, named scsi_mid_low_api.txt . Many LLDs are documented there
(e.g. aic7xxx.txt). The SCSI mid-level is briefly described in scsi.txt
(with a url to a document describing the SCSI subsystem in the lk 2.4
series). Two upper level drivers have documents in that directory:
st.txt (SCSI tape driver) and scsi-generic.txt .
Some documentation (or urls) for LLDs may be in the C source code or
in the same directory. For example to find a url about the USB mass
storage driver see the /usr/src/linux/drivers/usb/storage directory.
The Linux kernel source Documentation/DocBook/scsidrivers.tmpl file The Linux kernel source Documentation/DocBook/scsidrivers.tmpl file
refers to this file. With the appropriate DocBook toolset, this permits refers to this file. With the appropriate DocBook toolset, this permits
users to generate html, ps and pdf renderings of information within this users to generate html, ps and pdf renderings of information within this
file (e.g. the interface functions). file (e.g. the interface functions).
This version of the document roughly matches lk 2.5.50 .
Driver structure Driver structure
================ ================
Traditionally a LLD for the SCSI subsystem has been at least two files in Traditionally a LLD for the SCSI subsystem has been at least two files in
...@@ -100,12 +115,11 @@ the stability of the kernel (specifically the block and SCSI subsystems) ...@@ -100,12 +115,11 @@ the stability of the kernel (specifically the block and SCSI subsystems)
since the effected disk can be "cleaned up" the next time it is seen. since the effected disk can be "cleaned up" the next time it is seen.
During LLD initialization the driver should register itself with the During LLD initialization the driver should register itself with the
appropriate IO bus that it expects to find HBA(s) (e.g. the PCI bus). This appropriate IO bus on which it expects to find HBA(s) (e.g. the PCI bus).
can probably be done via sysfs (formerly known as driverfs). Any driver This can probably be done via sysfs. Any driver parameters (especially
parameters (especially those that are writeable after the driver is those that are writeable after the driver is loaded) could also be
loaded) could also be registered with sysfs at this point. At the end of registered with sysfs at this point. At the end of driver initialization
driver initialization the SCSI mid level is typically not aware of its the SCSI mid level is typically not aware of its presence.
presence.
At some later time, the LLD becomes aware of a HBA and what follows At some later time, the LLD becomes aware of a HBA and what follows
is a typical sequence of calls between the LLD and the mid level. is a typical sequence of calls between the LLD and the mid level.
...@@ -149,7 +163,7 @@ It is practical for a LLD to keep track of struct Scsi_Host instances ...@@ -149,7 +163,7 @@ It is practical for a LLD to keep track of struct Scsi_Host instances
(a pointer is returned by scsi_register() ) and struct scsi_device (a pointer is returned by scsi_register() ) and struct scsi_device
instances (a pointer is passed as the parameter to slave_alloc() and instances (a pointer is passed as the parameter to slave_alloc() and
slave_configure() ). Both classes of instances are "owned" by the slave_configure() ). Both classes of instances are "owned" by the
mid-level. struct scsi_devices instances are freed after slave_destroy(). mid-level. struct scsi_device instances are freed after slave_destroy().
struct Scsi_Host instances are freed after scsi_unregister(). struct Scsi_Host instances are freed after scsi_unregister().
...@@ -249,6 +263,8 @@ names all start with "scsi_". ...@@ -249,6 +263,8 @@ names all start with "scsi_".
/** /**
* scsi_add_host - perform sysfs registration and SCSI bus scan. * scsi_add_host - perform sysfs registration and SCSI bus scan.
* @shost: pointer to scsi host instance * @shost: pointer to scsi host instance
* @dev: pointer to struct device host instance of class type scsi
* (or related)
* *
* Returns 0 on success, negative errno of failure (e.g. -ENOMEM) * Returns 0 on success, negative errno of failure (e.g. -ENOMEM)
* *
...@@ -256,7 +272,24 @@ names all start with "scsi_". ...@@ -256,7 +272,24 @@ names all start with "scsi_".
* successful call to scsi_register(). * successful call to scsi_register().
* Defined in drivers/scsi/hosts.c * Defined in drivers/scsi/hosts.c
**/ **/
int scsi_add_host(struct Scsi_Host *shost) int scsi_add_host(struct Scsi_Host *shost, struct device * dev)
/**
* scsi_add_timer - (re-)start timer on a SCSI command.
* @scmd: pointer to scsi command instance
* @timeout: duration of timeout in "jiffies"
* @complete: pointer to function to call if timeout expires
*
* Returns nothing
*
* Notes: All commands issued by upper levels already have a timeout
* associated with them. A LLD can use this function to change
* the existing timeout value.
* Defined in drivers/scsi/scsi_error.c
**/
void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete)
(Scsi_Cmnd *))
/** /**
...@@ -321,6 +354,21 @@ unsigned char *scsi_bios_ptable(struct block_device *dev) ...@@ -321,6 +354,21 @@ unsigned char *scsi_bios_ptable(struct block_device *dev)
void scsi_block_requests(struct Scsi_Host * SHpnt) void scsi_block_requests(struct Scsi_Host * SHpnt)
/**
* scsi_delete_timer - cancel timer on a SCSI command.
* @scmd: pointer to scsi command instance
*
* Returns 1 if able to cancel timer else 0 (i.e. too late or already
* cancelled).
*
* Notes: All commands issued by upper levels already have a timeout
* associated with them. A LLD can use this function to cancel the
* timer.
* Defined in drivers/scsi/scsi_error.c
**/
int scsi_delete_timer(Scsi_Cmnd *scmd)
/** /**
* scsi_partsize - parse partition table into cylinders, heads + sectors * scsi_partsize - parse partition table into cylinders, heads + sectors
* @buf: pointer to partition table * @buf: pointer to partition table
...@@ -429,6 +477,26 @@ void scsi_set_device(struct Scsi_Host * shost, struct device * dev) ...@@ -429,6 +477,26 @@ void scsi_set_device(struct Scsi_Host * shost, struct device * dev)
int scsi_to_pci_dma_dir(unsigned char scsi_data_direction) int scsi_to_pci_dma_dir(unsigned char scsi_data_direction)
/**
* scsi_track_queue_full - track successive QUEUE_FULL events on given
* device to determine if and when there is a need
* to adjust the queue depth on the device.
* @SDptr: pointer to SCSI device instance
* @depth: Current number of outstanding SCSI commands on this device,
* not counting the one returned as QUEUE_FULL.
*
* Returns 0 - no change needed
* >0 - adjust queue depth to this new depth
* -1 - drop back to untagged operation using host->cmd_per_lun
* as the untagged command depth
*
* Notes: LLDs may call this at any time and we will do "The Right
* Thing"; interrupt context safe.
* Defined in drivers/scsi/scsi.c .
**/
int scsi_track_queue_full(Scsi_Device *SDptr, int depth)
/** /**
* scsi_unblock_requests - allow further commands to be queued to given host * scsi_unblock_requests - allow further commands to be queued to given host
* *
...@@ -1008,4 +1076,4 @@ The following people have contributed to this document: ...@@ -1008,4 +1076,4 @@ The following people have contributed to this document:
Douglas Gilbert Douglas Gilbert
dgilbert@interlog.com dgilbert@interlog.com
29th November 2002 21st February 2003
...@@ -122,6 +122,7 @@ ...@@ -122,6 +122,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <asm/dma.h> #include <asm/dma.h>
...@@ -325,6 +326,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt, ...@@ -325,6 +326,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt,
host->max_lun = NCR_700_MAX_LUNS; host->max_lun = NCR_700_MAX_LUNS;
host->unique_id = hostdata->base; host->unique_id = hostdata->base;
host->base = hostdata->base; host->base = hostdata->base;
hostdata->eh_complete = NULL;
host->hostdata[0] = (unsigned long)hostdata; host->hostdata[0] = (unsigned long)hostdata;
/* kick the chip */ /* kick the chip */
NCR_700_writeb(0xff, host, CTEST9_REG); NCR_700_writeb(0xff, host, CTEST9_REG);
...@@ -1525,6 +1527,9 @@ NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1525,6 +1527,9 @@ NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs)
hostdata->state = NCR_700_HOST_FREE; hostdata->state = NCR_700_HOST_FREE;
hostdata->cmd = NULL; hostdata->cmd = NULL;
/* signal back if this was an eh induced reset */
if(hostdata->eh_complete != NULL)
complete(hostdata->eh_complete);
goto out_unlock; goto out_unlock;
} else if(sstat0 & SELECTION_TIMEOUT) { } else if(sstat0 & SELECTION_TIMEOUT) {
DEBUG(("scsi%d: (%d:%d) selection timeout\n", DEBUG(("scsi%d: (%d:%d) selection timeout\n",
...@@ -1949,10 +1954,27 @@ NCR_700_abort(Scsi_Cmnd * SCp) ...@@ -1949,10 +1954,27 @@ NCR_700_abort(Scsi_Cmnd * SCp)
STATIC int STATIC int
NCR_700_bus_reset(Scsi_Cmnd * SCp) NCR_700_bus_reset(Scsi_Cmnd * SCp)
{ {
DECLARE_COMPLETION(complete);
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
printk(KERN_INFO "scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t", printk(KERN_INFO "scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t",
SCp->device->host->host_no, SCp->device->id, SCp->device->lun, SCp); SCp->device->host->host_no, SCp->device->id, SCp->device->lun, SCp);
print_command(SCp->cmnd); print_command(SCp->cmnd);
/* In theory, eh_complete should always be null because the
* eh is single threaded, but just in case we're handling a
* reset via sg or something */
while(hostdata->eh_complete != NULL) {
spin_unlock_irq(SCp->device->host->host_lock);
schedule_timeout(HZ/10);
spin_lock_irq(SCp->device->host->host_lock);
}
hostdata->eh_complete = &complete;
NCR_700_internal_bus_reset(SCp->device->host); NCR_700_internal_bus_reset(SCp->device->host);
spin_unlock_irq(SCp->device->host->host_lock);
wait_for_completion(&complete);
spin_lock_irq(SCp->device->host->host_lock);
hostdata->eh_complete = NULL;
return SUCCESS; return SUCCESS;
} }
......
...@@ -136,38 +136,6 @@ NCR_700_clear_flag(Scsi_Device *SDp, __u32 flag) ...@@ -136,38 +136,6 @@ NCR_700_clear_flag(Scsi_Device *SDp, __u32 flag)
((unsigned long)SDp->hostdata) &= ~(flag & 0xffff0000); ((unsigned long)SDp->hostdata) &= ~(flag & 0xffff0000);
} }
/* These represent the Nexus hashing functions. A Nexus in SCSI terms
* just means the identification of an outstanding command, by ITL
* (Initiator Target Lun) or ITLQ (Initiator Target Lun Tag). I'm not
* very keen on XOR based hashes, so these are based on number theory
* instead. All you need to do is to fix your hash bucket size and
* then choose reasonable strides which are coprime with the chosen
* bucket size
*
* Note: this mathematical hash can be made very efficient, if the
* compiler is good at optimising: Choose the number of buckets to be
* 2^n and the modulo becomes a logical and with (2^n-1).
* Additionally, if you chose the coprimes of the form 2^n-2^n the
* multiplication can be done by a shift and an addition. */
#define MAX_ITL_HASH_BUCKETS 16
#define ITL_HASH_PRIME 7
#define MAX_ITLQ_HASH_BUCKETS 64
#define ITLQ_PUN_PRIME 7
#define ITLQ_LUN_PRIME 3
static inline int
hash_ITL(__u8 pun, __u8 lun)
{
return (pun*ITL_HASH_PRIME + lun) % MAX_ITL_HASH_BUCKETS;
}
static inline int
hash_ITLQ(__u8 pun, __u8 lun, __u8 tag)
{
return (pun*ITLQ_PUN_PRIME + lun*ITLQ_LUN_PRIME + tag) % MAX_ITLQ_HASH_BUCKETS;
}
struct NCR_700_command_slot { struct NCR_700_command_slot {
struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1]; struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1];
struct NCR_700_SG_List *pSG; struct NCR_700_SG_List *pSG;
...@@ -186,12 +154,8 @@ struct NCR_700_command_slot { ...@@ -186,12 +154,8 @@ struct NCR_700_command_slot {
/* if this command is a pci_single mapping, holds the dma address /* if this command is a pci_single mapping, holds the dma address
* for later unmapping in the done routine */ * for later unmapping in the done routine */
dma_addr_t dma_handle; dma_addr_t dma_handle;
/* Doubly linked ITL/ITLQ list kept in strict time order /* historical remnant, now used to link free commands */
* (latest at the back) */
struct NCR_700_command_slot *ITL_forw; struct NCR_700_command_slot *ITL_forw;
struct NCR_700_command_slot *ITL_back;
struct NCR_700_command_slot *ITLQ_forw;
struct NCR_700_command_slot *ITLQ_back;
}; };
struct NCR_700_Host_Parameters { struct NCR_700_Host_Parameters {
...@@ -238,20 +202,15 @@ struct NCR_700_Host_Parameters { ...@@ -238,20 +202,15 @@ struct NCR_700_Host_Parameters {
__u8 tag_negotiated; __u8 tag_negotiated;
__u8 rev; __u8 rev;
__u8 reselection_id; __u8 reselection_id;
/* flags for the host */
/* ITL list. ALL outstanding commands are hashed here in strict
* order, latest at the back */
struct NCR_700_command_slot *ITL_Hash_forw[MAX_ITL_HASH_BUCKETS];
struct NCR_700_command_slot *ITL_Hash_back[MAX_ITL_HASH_BUCKETS];
/* Only tagged outstanding commands are hashed here (also latest
* at the back) */
struct NCR_700_command_slot *ITLQ_Hash_forw[MAX_ITLQ_HASH_BUCKETS];
struct NCR_700_command_slot *ITLQ_Hash_back[MAX_ITLQ_HASH_BUCKETS];
/* Free list, singly linked by ITL_forw elements */ /* Free list, singly linked by ITL_forw elements */
struct NCR_700_command_slot *free_list; struct NCR_700_command_slot *free_list;
/* Completion for waited for ops, like reset, abort or
* device reset.
*
* NOTE: relies on single threading in the error handler to
* have only one outstanding at once */
struct completion *eh_complete;
}; };
/* /*
......
...@@ -129,6 +129,7 @@ config SCSI_MULTI_LUN ...@@ -129,6 +129,7 @@ config SCSI_MULTI_LUN
config SCSI_REPORT_LUNS config SCSI_REPORT_LUNS
bool "Build with SCSI REPORT LUNS support" bool "Build with SCSI REPORT LUNS support"
depends on SCSI depends on SCSI
default y
help help
If you want to build with SCSI REPORT LUNS support in the kernel, say Y here. If you want to build with SCSI REPORT LUNS support in the kernel, say Y here.
The REPORT LUNS command is useful for devices (such as disk arrays) with The REPORT LUNS command is useful for devices (such as disk arrays) with
......
...@@ -1228,7 +1228,6 @@ static int port_detect \ ...@@ -1228,7 +1228,6 @@ static int port_detect \
sh[j]->unchecked_isa_dma = FALSE; sh[j]->unchecked_isa_dma = FALSE;
else { else {
unsigned long flags; unsigned long flags;
scsi_register_blocked_host(sh[j]);
sh[j]->unchecked_isa_dma = TRUE; sh[j]->unchecked_isa_dma = TRUE;
flags=claim_dma_lock(); flags=claim_dma_lock();
...@@ -2353,8 +2352,6 @@ static int eata2x_release(struct Scsi_Host *shpnt) { ...@@ -2353,8 +2352,6 @@ static int eata2x_release(struct Scsi_Host *shpnt) {
if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n", if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
driver_name); driver_name);
if(sh[j]->unchecked_isa_dma) scsi_deregister_blocked_host(sh[j]);
for (i = 0; i < sh[j]->can_queue; i++) for (i = 0; i < sh[j]->can_queue; i++)
if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
......
...@@ -46,10 +46,7 @@ ...@@ -46,10 +46,7 @@
* last change: 2002/11/02 OS: Linux 2.5.45 * * last change: 2002/11/02 OS: Linux 2.5.45 *
************************************************************/ ************************************************************/
/* Look in eata_pio.h for configuration information */
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -59,15 +56,19 @@ ...@@ -59,15 +56,19 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/io.h>
#include "eata_pio.h"
#include "eata_dma_proc.h"
#include "scsi.h"
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/config.h> /* for CONFIG_PCI */ #include <linux/config.h>
#include <linux/blk.h> #include <linux/blk.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/io.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsicam.h>
#include "eata_generic.h"
#include "eata_pio.h"
static uint ISAbases[MAXISA] = { static uint ISAbases[MAXISA] = {
0x1F0, 0x170, 0x330, 0x230 0x1F0, 0x170, 0x330, 0x230
...@@ -82,16 +83,106 @@ static unsigned char EISAbases[] = { ...@@ -82,16 +83,106 @@ static unsigned char EISAbases[] = {
1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1
}; };
static uint registered_HBAs = 0; static uint registered_HBAs;
static struct Scsi_Host *last_HBA; static struct Scsi_Host *last_HBA;
static struct Scsi_Host *first_HBA; static struct Scsi_Host *first_HBA;
static unsigned char reg_IRQ[]; static unsigned char reg_IRQ[16];
static unsigned char reg_IRQL[]; static unsigned char reg_IRQL[16];
static unsigned long int_counter;
static unsigned long queue_counter;
static unsigned long int_counter = 0; /*
static unsigned long queue_counter = 0; * eata_proc_info
* inout : decides on the direction of the dataflow and the meaning of the
* variables
* buffer: If inout==FALSE data is being written to it else read from it
* *start: If inout==FALSE start of the valid data in the buffer
* offset: If inout==FALSE offset from the beginning of the imaginary file
* from which we start writing into the buffer
* length: If inout==FALSE max number of bytes to be written into the buffer
* else number of bytes in the buffer
*/
static int eata_pio_proc_info(char *buffer, char **start, off_t offset,
int length, int hostno, int rw)
{
struct Scsi_Host *shost;
struct scsi_device *sdev;
static u8 buff[512];
int size, len = 0;
off_t begin = 0, pos = 0;
if (rw)
return -ENOSYS;
shost = scsi_host_hn_get(hostno);
if (!shost)
return -EINVAL;
if (offset == 0)
memset(buff, 0, sizeof(buff));
#include "eata_pio_proc.c" size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
"%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
len += size; pos = begin + len;
size = sprintf(buffer + len, "queued commands: %10ld\n"
"processed interrupts:%10ld\n", queue_counter, int_counter);
len += size; pos = begin + len;
size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
shost->host_no, SD(shost)->name);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Firmware revision: v%s\n",
SD(shost)->revision);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "IO: PIO\n");
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) shost->base);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Host Bus: %s\n",
(SD(shost)->bustype == 'P')?"PCI ":
(SD(shost)->bustype == 'E')?"EISA":"ISA ");
len += size;
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
if (pos > offset + length)
goto stop_output;
size = sprintf(buffer+len,"Attached devices: %s\n",
(!list_empty(&shost->my_devices))?"":"none");
len += size;
pos = begin + len;
list_for_each_entry(sdev, &shost->my_devices, siblings) {
proc_print_scsidevice(sdev, buffer, &size, len);
len += size;
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
if (pos > offset + length)
goto stop_output;
}
stop_output:
DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Start slop */
if(len>length)
len = length; /* Ending slop */
DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
return (len);
}
static int eata_pio_release(struct Scsi_Host *sh) static int eata_pio_release(struct Scsi_Host *sh)
{ {
...@@ -895,27 +986,19 @@ static int eata_pio_detect(Scsi_Host_Template * tpnt) ...@@ -895,27 +986,19 @@ static int eata_pio_detect(Scsi_Host_Template * tpnt)
return (registered_HBAs); return (registered_HBAs);
} }
/* Eventually this will go into an include file, but this will be later */ static Scsi_Host_Template driver_template = {
static Scsi_Host_Template driver_template = EATA_PIO; .proc_info = eata_pio_proc_info,
.name = "EATA (Extended Attachment) PIO driver",
.detect = eata_pio_detect,
.release = eata_pio_release,
.queuecommand = eata_pio_queue,
.eh_abort_handler = eata_pio_abort,
.eh_host_reset_handler = eata_pio_host_reset,
.use_clustering = ENABLE_CLUSTERING,
};
#include "scsi_module.c" MODULE_AUTHOR("Michael Neuffer, Alfred Arnold");
MODULE_DESCRIPTION("EATA SCSI PIO driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* #include "scsi_module.c"
* Overrides for Emacs so that we almost follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
...@@ -9,13 +9,6 @@ ...@@ -9,13 +9,6 @@
#ifndef _EATA_PIO_H #ifndef _EATA_PIO_H
#define _EATA_PIO_H #define _EATA_PIO_H
#include <linux/blk.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsicam.h>
#include "eata_generic.h"
#define VER_MAJOR 0 #define VER_MAJOR 0
#define VER_MINOR 0 #define VER_MINOR 0
#define VER_SUB "1b" #define VER_SUB "1b"
...@@ -57,22 +50,4 @@ ...@@ -57,22 +50,4 @@
#define DBG(x, y) #define DBG(x, y)
#endif #endif
static int eata_pio_detect(Scsi_Host_Template *);
static int eata_pio_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int eata_pio_abort(Scsi_Cmnd *);
static int eata_pio_host_reset(Scsi_Cmnd *);
static int eata_pio_proc_info(char *, char **, off_t, int, int, int);
static int eata_pio_release(struct Scsi_Host *);
#define EATA_PIO { \
.proc_info = eata_pio_proc_info, /* procinfo */ \
.name = "EATA (Extended Attachment) PIO driver",\
.detect = eata_pio_detect, \
.release = eata_pio_release, \
.queuecommand = eata_pio_queue, \
.eh_abort_handler = eata_pio_abort, \
.eh_host_reset_handler = eata_pio_host_reset, \
.use_clustering = ENABLE_CLUSTERING \
}
#endif /* _EATA_PIO_H */ #endif /* _EATA_PIO_H */
/*
* eata_set_info
* buffer : pointer to the data that has been written to the hostfile
* length : number of bytes written to the hostfile
* HBA_ptr: pointer to the Scsi_Host struct
*/
int eata_pio_set_info(char *buffer, int length, struct Scsi_Host *HBA_ptr)
{
DBG(DBG_PROC_WRITE, printk("%s\n", buffer));
return(-ENOSYS); /* Currently this is a no-op */
}
/*
* eata_proc_info
* inout : decides on the direction of the dataflow and the meaning of the
* variables
* buffer: If inout==FALSE data is being written to it else read from it
* *start: If inout==FALSE start of the valid data in the buffer
* offset: If inout==FALSE offset from the beginning of the imaginary file
* from which we start writing into the buffer
* length: If inout==FALSE max number of bytes to be written into the buffer
* else number of bytes in the buffer
*/
int eata_pio_proc_info(char *buffer, char **start, off_t offset, int length,
int hostno, int inout)
{
Scsi_Device *scd;
struct Scsi_Host *HBA_ptr;
static u8 buff[512];
int i;
int size, len = 0;
off_t begin = 0;
off_t pos = 0;
HBA_ptr = first_HBA;
for (i = 1; i <= registered_HBAs; i++) {
if (HBA_ptr->host_no == hostno)
break;
HBA_ptr = SD(HBA_ptr)->next;
}
if(inout == TRUE) /* Has data been written to the file ? */
return(eata_pio_set_info(buffer, length, HBA_ptr));
if (offset == 0)
memset(buff, 0, sizeof(buff));
size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
"%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
len += size; pos = begin + len;
size = sprintf(buffer + len, "queued commands: %10ld\n"
"processed interrupts:%10ld\n", queue_counter, int_counter);
len += size; pos = begin + len;
size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
HBA_ptr->host_no, SD(HBA_ptr)->name);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Firmware revision: v%s\n",
SD(HBA_ptr)->revision);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "IO: PIO\n");
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
len += size;
pos = begin + len;
size = sprintf(buffer + len, "Host Bus: %s\n",
(SD(HBA_ptr)->bustype == 'P')?"PCI ":
(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ");
len += size;
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
if (pos > offset + length)
goto stop_output;
size = sprintf(buffer+len,"Attached devices: %s\n",
(!list_empty(&HBA_ptr->my_devices))?"":"none");
len += size;
pos = begin + len;
list_for_each_entry(scd, &HBA_ptr->my_devices, siblings) {
proc_print_scsidevice(scd, buffer, &size, len);
len += size;
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
if (pos > offset + length)
goto stop_output;
}
stop_output:
DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Start slop */
if(len>length)
len = length; /* Ending slop */
DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
return (len);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* tab-width: 8
* End:
*/
...@@ -397,6 +397,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes) ...@@ -397,6 +397,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes)
spin_lock_init(&shost->default_lock); spin_lock_init(&shost->default_lock);
scsi_assign_lock(shost, &shost->default_lock); scsi_assign_lock(shost, &shost->default_lock);
INIT_LIST_HEAD(&shost->my_devices); INIT_LIST_HEAD(&shost->my_devices);
INIT_LIST_HEAD(&shost->eh_cmd_q);
init_waitqueue_head(&shost->host_wait); init_waitqueue_head(&shost->host_wait);
shost->dma_channel = 0xff; shost->dma_channel = 0xff;
...@@ -634,22 +635,9 @@ void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev) ...@@ -634,22 +635,9 @@ void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev)
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
shost->host_busy--; shost->host_busy--;
sdev->device_busy--; sdev->device_busy--;
if (shost->in_recovery && (shost->host_busy == shost->host_failed)) { if (shost->in_recovery && shost->host_failed &&
up(shost->eh_wait); (shost->host_busy == shost->host_failed))
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler" {
" thread\n"));
}
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); up(shost->eh_wait);
SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler" SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
" thread\n")); " thread\n"));
......
...@@ -384,6 +384,7 @@ struct Scsi_Host ...@@ -384,6 +384,7 @@ struct Scsi_Host
spinlock_t default_lock; spinlock_t default_lock;
spinlock_t *host_lock; spinlock_t *host_lock;
struct list_head eh_cmd_q;
struct task_struct * ehandler; /* Error recovery thread. */ struct task_struct * ehandler; /* Error recovery thread. */
struct semaphore * eh_wait; /* The error recovery thread waits on struct semaphore * eh_wait; /* The error recovery thread waits on
this. */ this. */
...@@ -514,8 +515,6 @@ extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host *); ...@@ -514,8 +515,6 @@ extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host *);
extern void scsi_unblock_requests(struct Scsi_Host *); extern void scsi_unblock_requests(struct Scsi_Host *);
extern void scsi_block_requests(struct Scsi_Host *); extern void scsi_block_requests(struct Scsi_Host *);
extern void scsi_report_bus_reset(struct Scsi_Host *, int); extern void scsi_report_bus_reset(struct Scsi_Host *, int);
extern void scsi_register_blocked_host(struct Scsi_Host *);
extern void scsi_deregister_blocked_host(struct Scsi_Host *);
static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock) static inline void scsi_assign_lock(struct Scsi_Host *shost, spinlock_t *lock)
{ {
...@@ -587,7 +586,6 @@ extern void scsi_host_init(void); ...@@ -587,7 +586,6 @@ extern void scsi_host_init(void);
*/ */
extern void scsi_host_busy_inc(struct Scsi_Host *, Scsi_Device *); 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_busy_dec_and_test(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_failed_inc_and_test(struct Scsi_Host *);
/** /**
* scsi_find_device - find a device given the host * scsi_find_device - find a device given the host
......
...@@ -287,7 +287,7 @@ int pluto_release(struct Scsi_Host *host) ...@@ -287,7 +287,7 @@ int pluto_release(struct Scsi_Host *host)
struct pluto *pluto = (struct pluto *)host->hostdata; struct pluto *pluto = (struct pluto *)host->hostdata;
fc_channel *fc = pluto->fc; fc_channel *fc = pluto->fc;
if (fc->module) __MOD_DEC_USE_COUNT(fc->module); module_put(fc->module);
fc->fcp_register(fc, TYPE_SCSI_FCP, 1); fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
PLND((" releasing pluto.\n")); PLND((" releasing pluto.\n"));
......
...@@ -36,9 +36,6 @@ ...@@ -36,9 +36,6 @@
* out_of_space hacks, D. Gilbert (dpg) 990608 * out_of_space hacks, D. Gilbert (dpg) 990608
*/ */
#define REVISION "Revision: 1.00"
#define VERSION "Id: scsi.c 1.00 2000/09/26"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -790,13 +787,9 @@ static void scsi_softirq(struct softirq_action *h) ...@@ -790,13 +787,9 @@ static void scsi_softirq(struct softirq_action *h)
if ((status_byte(SCpnt->result) & CHECK_CONDITION) != 0) { if ((status_byte(SCpnt->result) & CHECK_CONDITION) != 0) {
SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt)); SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt));
} }
if (SCpnt->device->host->eh_wait != NULL) {
scsi_eh_eflags_set(SCpnt, SCSI_EH_CMD_FAILED | SCSI_EH_CMD_ERR);
SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
SCpnt->state = SCSI_STATE_FAILED;
scsi_host_failed_inc_and_test(SCpnt->device->host); if (!scsi_eh_scmd_add(SCpnt, 0))
} else { {
/* /*
* We only get here if the error * We only get here if the error
* recovery thread has died. * recovery thread has died.
...@@ -1147,29 +1140,6 @@ int scsi_dev_info_list_add_str (char *dev_list) ...@@ -1147,29 +1140,6 @@ int scsi_dev_info_list_add_str (char *dev_list)
return res; return res;
} }
/**
* scsi_dev_list_init: set up the dynamic device list.
* @dev_list: string of device flags to add
*
* Description:
* Add command line @dev_list entries, then add
* scsi_static_device_list entries to the scsi device info list.
**/
static void scsi_dev_info_list_init (char *dev_list)
{
int i;
if (scsi_dev_info_list_add_str(dev_list) == -ENOMEM)
return;
for (i = 0; scsi_static_device_list[i].vendor != NULL; i++)
if (scsi_dev_info_list_add(1 /* compatibile */,
scsi_static_device_list[i].vendor,
scsi_static_device_list[i].model,
NULL,
scsi_static_device_list[i].flags) == -ENOMEM)
return;
}
/** /**
* scsi_dev_info_list_delete: called from scsi.c:exit_scsi to remove * scsi_dev_info_list_delete: called from scsi.c:exit_scsi to remove
* the scsi_dev_info_list. * the scsi_dev_info_list.
...@@ -1186,6 +1156,37 @@ static void scsi_dev_info_list_delete (void) ...@@ -1186,6 +1156,37 @@ static void scsi_dev_info_list_delete (void)
} }
} }
/**
* scsi_dev_list_init: set up the dynamic device list.
* @dev_list: string of device flags to add
*
* Description:
* Add command line @dev_list entries, then add
* scsi_static_device_list entries to the scsi device info list.
**/
static int scsi_dev_info_list_init (char *dev_list)
{
int error, i;
error = scsi_dev_info_list_add_str(dev_list);
if (error)
return error;
for (i = 0; scsi_static_device_list[i].vendor != NULL; i++) {
error = scsi_dev_info_list_add(1 /* compatibile */,
scsi_static_device_list[i].vendor,
scsi_static_device_list[i].model,
NULL,
scsi_static_device_list[i].flags);
if (error)
break;
}
if (error)
scsi_dev_info_list_delete();
return error;
}
/** /**
* get_device_flags - get device specific flags from the dynamic device * get_device_flags - get device specific flags from the dynamic device
* list. Called during scan time. * list. Called during scan time.
...@@ -1298,6 +1299,44 @@ void scsi_device_put(struct scsi_device *sdev) ...@@ -1298,6 +1299,44 @@ void scsi_device_put(struct scsi_device *sdev)
module_put(sdev->host->hostt->module); module_put(sdev->host->hostt->module);
} }
/**
* scsi_set_device_offline - set scsi_device offline
* @sdev: pointer to struct scsi_device to offline.
*
* Locks: host_lock held on entry.
**/
void scsi_set_device_offline(struct scsi_device *sdev)
{
struct scsi_cmnd *scmd;
int cmds_active = 0;
unsigned long flags;
sdev->online = FALSE;
spin_lock_irqsave(&sdev->list_lock, flags);
list_for_each_entry(scmd, &sdev->cmd_list, list) {
if (scmd->request && scmd->request->rq_status != RQ_INACTIVE) {
/*
* If we are unable to remove the timer, it means
* that the command has already timed out or
* finished.
*/
if (!scsi_delete_timer(scmd)) {
continue;
}
++cmds_active;
scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);
}
}
spin_unlock_irqrestore(&sdev->list_lock, flags);
if (!cmds_active) {
/* FIXME: Send online state change hotplug event */
}
}
/* /*
* Function: scsi_slave_attach() * Function: scsi_slave_attach()
* *
...@@ -1437,17 +1476,38 @@ __setup("scsi_default_dev_flags=", setup_scsi_default_dev_flags); ...@@ -1437,17 +1476,38 @@ __setup("scsi_default_dev_flags=", setup_scsi_default_dev_flags);
#endif #endif
/* FIXME(hch): add proper error handling */
static int __init init_scsi(void) static int __init init_scsi(void)
{ {
scsi_init_queue(); int error;
scsi_init_procfs();
devfs_mk_dir(NULL, "scsi", NULL); error = scsi_init_queue();
if (error)
return error;
error = scsi_init_procfs();
if (error)
goto cleanup_queue;
error = scsi_dev_info_list_init(scsi_dev_flags);
if (error)
goto cleanup_procfs;
error = scsi_sysfs_register();
if (error)
goto cleanup_devlist;
scsi_host_init(); scsi_host_init();
scsi_dev_info_list_init(scsi_dev_flags); devfs_mk_dir(NULL, "scsi", NULL);
scsi_sysfs_register();
open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL); open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL);
printk(KERN_NOTICE "SCSI subsystem initialized\n");
return 0; return 0;
cleanup_devlist:
scsi_dev_info_list_delete();
cleanup_procfs:
scsi_exit_procfs();
cleanup_queue:
scsi_exit_queue();
printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n",
-error);
return error;
} }
static void __exit exit_scsi(void) static void __exit exit_scsi(void)
......
...@@ -455,6 +455,7 @@ extern int scsi_slave_attach(struct scsi_device *); ...@@ -455,6 +455,7 @@ extern int scsi_slave_attach(struct scsi_device *);
extern void scsi_slave_detach(struct scsi_device *); extern void scsi_slave_detach(struct scsi_device *);
extern int scsi_device_get(struct scsi_device *); extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *); extern void scsi_device_put(struct scsi_device *);
extern void scsi_set_device_offline(struct scsi_device *);
extern void scsi_done(Scsi_Cmnd * SCpnt); extern void scsi_done(Scsi_Cmnd * SCpnt);
extern void scsi_finish_command(Scsi_Cmnd *); extern void scsi_finish_command(Scsi_Cmnd *);
extern int scsi_retry_command(Scsi_Cmnd *); extern int scsi_retry_command(Scsi_Cmnd *);
...@@ -726,6 +727,7 @@ struct scsi_cmnd { ...@@ -726,6 +727,7 @@ struct scsi_cmnd {
struct list_head list; /* scsi_cmnd participates in queue lists */ struct list_head list; /* scsi_cmnd participates in queue lists */
struct list_head eh_entry; /* entry for the host eh_cmd_q */
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 */ 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 */
...@@ -850,6 +852,7 @@ struct scsi_cmnd { ...@@ -850,6 +852,7 @@ struct scsi_cmnd {
*/ */
#define SCSI_MLQUEUE_HOST_BUSY 0x1055 #define SCSI_MLQUEUE_HOST_BUSY 0x1055
#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056 #define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
#define SCSI_MLQUEUE_EH_RETRY 0x1057
/* /*
* old style reset request from external source * old style reset request from external source
...@@ -960,13 +963,13 @@ static inline Scsi_Cmnd *scsi_find_tag(Scsi_Device *SDpnt, int tag) { ...@@ -960,13 +963,13 @@ static inline Scsi_Cmnd *scsi_find_tag(Scsi_Device *SDpnt, int tag) {
/* /*
* Scsi Error Handler Flags * Scsi Error Handler Flags
*/ */
#define SCSI_EH_CMD_ERR 0x0001 /* Orig cmd error'd */ #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */
#define SCSI_EH_CMD_FAILED 0x0002 /* Orig cmd error type failed */ #define SCSI_EH_REC_TIMEOUT 0x0002 /* EH retry timed out */
#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) #define SCSI_SENSE_VALID(scmd) ((scmd->sense_buffer[0] & 0x70) == 0x70)
extern int scsi_eh_scmd_add(struct scsi_cmnd *, int);
int scsi_set_medium_removal(Scsi_Device *dev, char state); int scsi_set_medium_removal(Scsi_Device *dev, char state);
extern int scsi_device_register(struct scsi_device *); extern int scsi_device_register(struct scsi_device *);
......
...@@ -15,33 +15,19 @@ ...@@ -15,33 +15,19 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/blk.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/blkdev.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/completion.h> #include <scsi/scsi_ioctl.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
#include <scsi/scsi_ioctl.h> /* grr */
#ifdef DEBUG #ifdef DEBUG
#define SENSE_TIMEOUT SCSI_TIMEOUT #define SENSE_TIMEOUT SCSI_TIMEOUT
#else #else
...@@ -55,6 +41,49 @@ ...@@ -55,6 +41,49 @@
#define BUS_RESET_SETTLE_TIME 10*HZ #define BUS_RESET_SETTLE_TIME 10*HZ
#define HOST_RESET_SETTLE_TIME 10*HZ #define HOST_RESET_SETTLE_TIME 10*HZ
/**
* scsi_eh_scmd_add - add scsi cmd to error handling.
* @scmd: scmd to run eh on.
* @eh_flag: optional SCSI_EH flag.
*
* Return value:
* 0 on failure.
**/
int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
{
struct Scsi_Host *shost = scmd->device->host;
unsigned long flags;
if (shost->eh_wait == NULL)
return 0;
spin_lock_irqsave(shost->host_lock, flags);
scsi_eh_eflags_set(scmd, eh_flag);
/*
* FIXME: Can we stop setting owner and state.
*/
scmd->owner = SCSI_OWNER_ERROR_HANDLER;
scmd->state = SCSI_STATE_FAILED;
/*
* Set the serial_number_at_timeout to the current
* serial_number
*/
scmd->serial_number_at_timeout = scmd->serial_number;
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
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\n"));
}
spin_unlock_irqrestore(shost->host_lock, flags);
return 1;
}
/** /**
* scsi_add_timer - Start timeout timer for a single scsi command. * scsi_add_timer - Start timeout timer for a single scsi command.
* @scmd: scsi command that is about to start running. * @scmd: scsi command that is about to start running.
...@@ -68,8 +97,8 @@ ...@@ -68,8 +97,8 @@
* simple, really, especially compared to the old way of handling this * simple, really, especially compared to the old way of handling this
* crap. * crap.
**/ **/
void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete) void scsi_add_timer(struct scsi_cmnd *scmd, int timeout,
(Scsi_Cmnd *)) void (*complete)(struct scsi_cmnd *))
{ {
/* /*
...@@ -77,10 +106,10 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete) ...@@ -77,10 +106,10 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete)
* 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 (scmd->eh_timeout.function != NULL) { if (scmd->eh_timeout.function)
del_timer(&scmd->eh_timeout); del_timer(&scmd->eh_timeout);
}
scmd->eh_timeout.data = (unsigned long) scmd; scmd->eh_timeout.data = (unsigned long)scmd;
scmd->eh_timeout.expires = jiffies + timeout; scmd->eh_timeout.expires = jiffies + timeout;
scmd->eh_timeout.function = (void (*)(unsigned long)) complete; scmd->eh_timeout.function = (void (*)(unsigned long)) complete;
...@@ -89,7 +118,6 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete) ...@@ -89,7 +118,6 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete)
scmd, timeout, complete)); scmd, timeout, complete));
add_timer(&scmd->eh_timeout); add_timer(&scmd->eh_timeout);
} }
/** /**
...@@ -103,7 +131,7 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete) ...@@ -103,7 +131,7 @@ void scsi_add_timer(Scsi_Cmnd *scmd, int timeout, void (*complete)
* 1 if we were able to detach the timer. 0 if we blew it, and the * 1 if we were able to detach the timer. 0 if we blew it, and the
* timer function has already started to run. * timer function has already started to run.
**/ **/
int scsi_delete_timer(Scsi_Cmnd *scmd) int scsi_delete_timer(struct scsi_cmnd *scmd)
{ {
int rtn; int rtn;
...@@ -113,7 +141,7 @@ int scsi_delete_timer(Scsi_Cmnd *scmd) ...@@ -113,7 +141,7 @@ int scsi_delete_timer(Scsi_Cmnd *scmd)
" rtn: %d\n", __FUNCTION__, " rtn: %d\n", __FUNCTION__,
scmd, rtn)); scmd, rtn));
scmd->eh_timeout.data = (unsigned long) NULL; scmd->eh_timeout.data = (unsigned long)NULL;
scmd->eh_timeout.function = NULL; scmd->eh_timeout.function = NULL;
return rtn; return rtn;
...@@ -129,24 +157,16 @@ int scsi_delete_timer(Scsi_Cmnd *scmd) ...@@ -129,24 +157,16 @@ int scsi_delete_timer(Scsi_Cmnd *scmd)
* normal completion function determines that the timer has already * normal completion function determines that the timer has already
* fired, then it mustn't do anything. * fired, then it mustn't do anything.
**/ **/
void scsi_times_out(Scsi_Cmnd *scmd) void scsi_times_out(struct scsi_cmnd *scmd)
{ {
struct Scsi_Host *shost = scmd->device->host; if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
/* 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 (unlikely(shost->eh_wait == NULL)) {
panic("Error handler thread not present at %p %p %s %d", panic("Error handler thread not present at %p %p %s %d",
scmd, shost, __FILE__, __LINE__); scmd, scmd->device->host, __FILE__, __LINE__);
} }
scsi_host_failed_inc_and_test(shost);
SCSI_LOG_TIMEOUT(3, printk("Command timed out busy=%d failed=%d\n", SCSI_LOG_TIMEOUT(3, printk("Command timed out busy=%d failed=%d\n",
shost->host_busy, shost->host_failed)); scmd->device->host->host_busy,
scmd->device->host->host_failed));
} }
/** /**
...@@ -160,7 +180,7 @@ void scsi_times_out(Scsi_Cmnd *scmd) ...@@ -160,7 +180,7 @@ void scsi_times_out(Scsi_Cmnd *scmd)
* Return value: * Return value:
* 0 when dev was taken offline by error recovery. 1 OK to proceed. * 0 when dev was taken offline by error recovery. 1 OK to proceed.
**/ **/
int scsi_block_when_processing_errors(Scsi_Device *sdev) int scsi_block_when_processing_errors(struct scsi_device *sdev)
{ {
wait_event(sdev->host->host_wait, (sdev->host->in_recovery == 0)); wait_event(sdev->host->host_wait, (sdev->host->in_recovery == 0));
...@@ -173,39 +193,40 @@ int scsi_block_when_processing_errors(Scsi_Device *sdev) ...@@ -173,39 +193,40 @@ int scsi_block_when_processing_errors(Scsi_Device *sdev)
#if CONFIG_SCSI_LOGGING #if CONFIG_SCSI_LOGGING
/** /**
* scsi_eh_prt_fail_stats - Log info on failures. * scsi_eh_prt_fail_stats - Log info on failures.
* @sc_list: List for failed cmds.
* @shost: scsi host being recovered. * @shost: scsi host being recovered.
* @work_q: Queue of scsi cmds to process.
**/ **/
static void scsi_eh_prt_fail_stats(Scsi_Cmnd *sc_list, struct Scsi_Host *shost) static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
struct list_head *work_q)
{ {
Scsi_Cmnd *scmd; struct scsi_cmnd *scmd;
Scsi_Device *sdev; struct scsi_device *sdev;
int total_failures = 0; int total_failures = 0;
int cmd_failed = 0; int cmd_failed = 0;
int cmd_timed_out = 0; int cmd_cancel = 0;
int devices_failed = 0; int devices_failed = 0;
list_for_each_entry(sdev, &shost->my_devices, siblings) { list_for_each_entry(sdev, &shost->my_devices, siblings) {
for (scmd = sc_list; scmd; scmd = scmd->bh_next) { list_for_each_entry(scmd, work_q, eh_entry) {
if (scmd->device == sdev) { if (scmd->device == sdev) {
++total_failures; ++total_failures;
if (scsi_eh_eflags_chk(scmd, if (scsi_eh_eflags_chk(scmd,
SCSI_EH_CMD_TIMEOUT)) SCSI_EH_CANCEL_CMD))
++cmd_timed_out; ++cmd_cancel;
else else
++cmd_failed; ++cmd_failed;
} }
} }
if (cmd_timed_out || cmd_failed) { if (cmd_cancel || cmd_failed) {
SCSI_LOG_ERROR_RECOVERY(3, SCSI_LOG_ERROR_RECOVERY(3,
printk("%s: %d:%d:%d:%d cmds failed: %d," printk("%s: %d:%d:%d:%d cmds failed: %d,"
" timedout: %d\n", " cancel: %d\n",
__FUNCTION__, shost->host_no, __FUNCTION__, shost->host_no,
sdev->channel, sdev->id, sdev->lun, sdev->channel, sdev->id, sdev->lun,
cmd_failed, cmd_timed_out)); cmd_failed, cmd_cancel));
cmd_timed_out = 0; cmd_cancel = 0;
cmd_failed = 0; cmd_failed = 0;
++devices_failed; ++devices_failed;
} }
...@@ -217,68 +238,6 @@ static void scsi_eh_prt_fail_stats(Scsi_Cmnd *sc_list, struct Scsi_Host *shost) ...@@ -217,68 +238,6 @@ static void scsi_eh_prt_fail_stats(Scsi_Cmnd *sc_list, struct Scsi_Host *shost)
} }
#endif #endif
/**
* scsi_eh_get_failed - Gather failed cmds.
* @sc_list: A pointer to a list for failed cmds.
* @shost: Scsi host being recovered.
*
* XXX Add opaque interator for device / shost. Investigate direct
* addition to per eh list on error allowing skipping of this step.
**/
static void scsi_eh_get_failed(Scsi_Cmnd **sc_list, struct Scsi_Host *shost)
{
int found;
Scsi_Device *sdev;
Scsi_Cmnd *scmd;
found = 0;
list_for_each_entry(sdev, &shost->my_devices, siblings) {
unsigned long flags;
spin_lock_irqsave(&sdev->list_lock, flags);
list_for_each_entry(scmd, &sdev->cmd_list, list) {
if (scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) {
scmd->bh_next = *sc_list;
*sc_list = scmd;
found++;
} else {
/*
* FIXME Verify how this can happen and if
* this is still needed??
*/
if (scmd->state != SCSI_STATE_INITIALIZING
&& scmd->state != SCSI_STATE_UNUSED) {
/*
* 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 hdlr"
" prematurely woken"
" cmds still active"
" (%p %x %d)\n",
scmd, scmd->state,
scmd->device->id));
}
}
}
spin_unlock_irqrestore(&sdev->list_lock, flags);
}
SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(*sc_list, shost));
if (shost->host_failed != found)
printk(KERN_ERR "%s: host_failed: %d != found: %d\n",
__FUNCTION__, shost->host_failed, found);
}
/** /**
* scsi_check_sense - Examine scsi cmd sense * scsi_check_sense - Examine scsi cmd sense
* @scmd: Cmd to have sense checked. * @scmd: Cmd to have sense checked.
...@@ -286,11 +245,10 @@ static void scsi_eh_get_failed(Scsi_Cmnd **sc_list, struct Scsi_Host *shost) ...@@ -286,11 +245,10 @@ static void scsi_eh_get_failed(Scsi_Cmnd **sc_list, struct Scsi_Host *shost)
* Return value: * Return value:
* SUCCESS or FAILED or NEEDS_RETRY * SUCCESS or FAILED or NEEDS_RETRY
**/ **/
static int scsi_check_sense(Scsi_Cmnd *scmd) static int scsi_check_sense(struct scsi_cmnd *scmd)
{ {
if (!SCSI_SENSE_VALID(scmd)) { if (!SCSI_SENSE_VALID(scmd))
return FAILED; return FAILED;
}
if (scmd->sense_buffer[2] & 0xe0) if (scmd->sense_buffer[2] & 0xe0)
return SUCCESS; return SUCCESS;
...@@ -352,9 +310,8 @@ static int scsi_check_sense(Scsi_Cmnd *scmd) ...@@ -352,9 +310,8 @@ static int scsi_check_sense(Scsi_Cmnd *scmd)
* don't allow for the possibility of retries here, and we are a lot * don't allow for the possibility of retries here, and we are a lot
* more restrictive about what we consider acceptable. * more restrictive about what we consider acceptable.
**/ **/
static int scsi_eh_completed_normally(Scsi_Cmnd *scmd) static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
{ {
/* /*
* 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.
...@@ -379,15 +336,15 @@ static int scsi_eh_completed_normally(Scsi_Cmnd *scmd) ...@@ -379,15 +336,15 @@ static int scsi_eh_completed_normally(Scsi_Cmnd *scmd)
*/ */
return scsi_check_sense(scmd); return scsi_check_sense(scmd);
} }
if (host_byte(scmd->result) != DID_OK) { if (host_byte(scmd->result) != DID_OK)
return FAILED; return FAILED;
}
/* /*
* next, check the message byte. * next, check the message byte.
*/ */
if (msg_byte(scmd->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.
...@@ -423,46 +380,38 @@ static int scsi_eh_completed_normally(Scsi_Cmnd *scmd) ...@@ -423,46 +380,38 @@ static int scsi_eh_completed_normally(Scsi_Cmnd *scmd)
* for some action to complete on the device. our only job is to * for some action to complete on the device. our only job is to
* record that it timed out, and to wake up the thread. * record that it timed out, and to wake up the thread.
**/ **/
static void scsi_eh_times_out(Scsi_Cmnd *scmd) static void scsi_eh_times_out(struct scsi_cmnd *scmd)
{ {
scsi_eh_eflags_set(scmd, SCSI_EH_REC_TIMEOUT); scsi_eh_eflags_set(scmd, SCSI_EH_REC_TIMEOUT);
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__, SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__,
scmd)); scmd));
if (scmd->device->host->eh_action != NULL) if (scmd->device->host->eh_action)
up(scmd->device->host->eh_action); up(scmd->device->host->eh_action);
else
printk("%s: eh_action NULL\n", __FUNCTION__);
} }
/** /**
* scsi_eh_done - Completion function for error handling. * scsi_eh_done - Completion function for error handling.
* @scmd: Cmd that is done. * @scmd: Cmd that is done.
**/ **/
static void scsi_eh_done(Scsi_Cmnd *scmd) static void scsi_eh_done(struct scsi_cmnd *scmd)
{ {
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(&scmd->eh_timeout); if (del_timer(&scmd->eh_timeout)) {
if (!rtn) {
return;
}
scmd->request->rq_status = RQ_SCSI_DONE; scmd->request->rq_status = RQ_SCSI_DONE;
scmd->owner = SCSI_OWNER_ERROR_HANDLER; scmd->owner = SCSI_OWNER_ERROR_HANDLER;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n", SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n",
__FUNCTION__, scmd, scmd->result)); __FUNCTION__, scmd, scmd->result));
if (scmd->device->host->eh_action != NULL) if (scmd->device->host->eh_action)
up(scmd->device->host->eh_action); up(scmd->device->host->eh_action);
}
} }
/** /**
...@@ -477,10 +426,10 @@ static void scsi_eh_done(Scsi_Cmnd *scmd) ...@@ -477,10 +426,10 @@ static void scsi_eh_done(Scsi_Cmnd *scmd)
* Return value: * Return value:
* SUCCESS or FAILED or NEEDS_RETRY * SUCCESS or FAILED or NEEDS_RETRY
**/ **/
static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout) static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
{ {
unsigned long flags;
struct Scsi_Host *host = scmd->device->host; struct Scsi_Host *host = scmd->device->host;
unsigned long flags;
int rtn = SUCCESS; int rtn = SUCCESS;
ASSERT_LOCK(host->host_lock, 0); ASSERT_LOCK(host->host_lock, 0);
...@@ -535,7 +484,8 @@ static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout) ...@@ -535,7 +484,8 @@ static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout)
spin_lock_irqsave(scmd->device->host->host_lock, flags); spin_lock_irqsave(scmd->device->host->host_lock, flags);
if (scmd->device->host->hostt->eh_abort_handler) if (scmd->device->host->hostt->eh_abort_handler)
scmd->device->host->hostt->eh_abort_handler(scmd); scmd->device->host->hostt->eh_abort_handler(scmd);
spin_unlock_irqrestore(scmd->device->host->host_lock, flags); spin_unlock_irqrestore(scmd->device->host->host_lock,
flags);
scmd->request->rq_status = RQ_SCSI_DONE; scmd->request->rq_status = RQ_SCSI_DONE;
scmd->owner = SCSI_OWNER_ERROR_HANDLER; scmd->owner = SCSI_OWNER_ERROR_HANDLER;
...@@ -592,32 +542,33 @@ static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout) ...@@ -592,32 +542,33 @@ static int scsi_send_eh_cmnd(Scsi_Cmnd *scmd, int timeout)
* that we obtain it on our own. This function will *not* return until * that we obtain it on our own. This function will *not* return until
* the command either times out, or it completes. * the command either times out, or it completes.
**/ **/
static int scsi_request_sense(Scsi_Cmnd *scmd) static int scsi_request_sense(struct scsi_cmnd *scmd)
{ {
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 = &scsi_result0[0];
int saved_result; int saved_result;
int rtn; int rtn;
memcpy((void *) scmd->cmnd, (void *) generic_sense, memcpy(scmd->cmnd, generic_sense, sizeof(generic_sense));
sizeof(generic_sense));
scsi_result = (!scmd->device->host->hostt->unchecked_isa_dma)
? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA);
if (scsi_result == NULL) { if (scmd->device->host->hostt->unchecked_isa_dma) {
printk("%s: cannot allocate scsi_result.\n", __FUNCTION__); scsi_result = kmalloc(512, GFP_ATOMIC | __GFP_DMA);
if (unlikely(!scsi_result)) {
printk(KERN_ERR "%s: cannot allocate scsi_result.\n",
__FUNCTION__);
return FAILED; return FAILED;
} }
}
/* /*
* zero the sense buffer. some host adapters automatically always * zero the sense buffer. some host adapters automatically always
* request sense, so it is not a good idea that * request sense, so it is not a good idea that
* scmd->request_buffer and scmd->sense_buffer point to the same * scmd->request_buffer and scmd->sense_buffer point to the same
* address (db). 0 is not a valid sense code. * address (db). 0 is not a valid sense code.
*/ */
memset((void *) scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
memset((void *) scsi_result, 0, 256); memset(scsi_result, 0, 256);
saved_result = scmd->result; saved_result = scmd->result;
scmd->request_buffer = scsi_result; scmd->request_buffer = scsi_result;
...@@ -630,12 +581,12 @@ static int scsi_request_sense(Scsi_Cmnd *scmd) ...@@ -630,12 +581,12 @@ static int scsi_request_sense(Scsi_Cmnd *scmd)
rtn = scsi_send_eh_cmnd(scmd, 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(scmd)) if (!SCSI_SENSE_VALID(scmd)) {
memcpy((void *) scmd->sense_buffer, memcpy(scmd->sense_buffer, scmd->request_buffer,
scmd->request_buffer,
sizeof(scmd->sense_buffer)); sizeof(scmd->sense_buffer));
}
if (scsi_result != &scsi_result0[0] && scsi_result != NULL) if (scsi_result != &scsi_result0[0])
kfree(scsi_result); kfree(scsi_result);
/* /*
...@@ -644,10 +595,6 @@ static int scsi_request_sense(Scsi_Cmnd *scmd) ...@@ -644,10 +595,6 @@ static int scsi_request_sense(Scsi_Cmnd *scmd)
*/ */
scsi_setup_cmd_retry(scmd); scsi_setup_cmd_retry(scmd);
scmd->result = saved_result; scmd->result = saved_result;
/*
* hey, we are done. let's look to see what happened.
*/
return rtn; return rtn;
} }
...@@ -659,7 +606,7 @@ static int scsi_request_sense(Scsi_Cmnd *scmd) ...@@ -659,7 +606,7 @@ static int scsi_request_sense(Scsi_Cmnd *scmd)
* This function will *not* return until the command either times out, * This function will *not* return until the command either times out,
* or it completes. * or it completes.
**/ **/
static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd) static int scsi_eh_retry_cmd(struct scsi_cmnd *scmd)
{ {
int rtn = SUCCESS; int rtn = SUCCESS;
...@@ -676,7 +623,7 @@ static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd) ...@@ -676,7 +623,7 @@ static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd)
/** /**
* scsi_eh_finish_cmd - Handle a cmd that eh is finished with. * scsi_eh_finish_cmd - Handle a cmd that eh is finished with.
* @scmd: Original SCSI cmd that eh has finished. * @scmd: Original SCSI cmd that eh has finished.
* @shost: SCSI host that cmd originally failed on. * @done_q: Queue for processed commands.
* *
* Notes: * Notes:
* We don't want to use the normal command completion while we are are * We don't want to use the normal command completion while we are are
...@@ -685,10 +632,12 @@ static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd) ...@@ -685,10 +632,12 @@ static int scsi_eh_retry_cmd(Scsi_Cmnd *scmd)
* keep a list of pending commands for final completion, and once we * keep a list of pending commands for final completion, and once we
* are ready to leave error handling we handle completion for real. * are ready to leave error handling we handle completion for real.
**/ **/
static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost) static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd,
struct list_head *done_q)
{ {
shost->host_failed--; scmd->device->host->host_failed--;
scmd->state = SCSI_STATE_BHQUEUE; scmd->state = SCSI_STATE_BHQUEUE;
scsi_eh_eflags_clr_all(scmd); scsi_eh_eflags_clr_all(scmd);
/* /*
...@@ -696,18 +645,18 @@ static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost) ...@@ -696,18 +645,18 @@ static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost)
* things. * things.
*/ */
scsi_setup_cmd_retry(scmd); scsi_setup_cmd_retry(scmd);
list_move_tail(&scmd->eh_entry, done_q);
} }
/** /**
* scsi_eh_get_sense - Get device sense data. * scsi_eh_get_sense - Get device sense data.
* @sc_todo: list of cmds that have failed. * @work_q: Queue of commands to process.
* @shost: scsi host being recovered. * @done_q: Queue of proccessed commands..
* *
* Description: * Description:
* See if we need to request sense information. if so, then get it * See if we need to request sense information. if so, then get it
* now, so we have a better idea of what to do. * now, so we have a better idea of what to do.
* *
*
* Notes: * Notes:
* This has the unfortunate side effect that if a shost adapter does * This has the unfortunate side effect that if a shost adapter does
* not automatically request sense information, that we end up shutting * not automatically request sense information, that we end up shutting
...@@ -718,24 +667,26 @@ static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost) ...@@ -718,24 +667,26 @@ static void scsi_eh_finish_cmd(Scsi_Cmnd *scmd, struct Scsi_Host *shost)
* this. * this.
* *
* In 2.5 this capability will be going away. * In 2.5 this capability will be going away.
*
* Really? --hch
**/ **/
static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) static int scsi_eh_get_sense(struct list_head *work_q,
struct list_head *done_q)
{ {
struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd;
int rtn; int rtn;
Scsi_Cmnd *scmd;
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) { list_for_each_safe(lh, lh_sf, work_q) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_FAILED) || scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
if (scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD) ||
SCSI_SENSE_VALID(scmd)) SCSI_SENSE_VALID(scmd))
continue; continue;
SCSI_LOG_ERROR_RECOVERY(2, printk("%s: requesting sense" SCSI_LOG_ERROR_RECOVERY(2, printk("%s: requesting sense"
" for tgt: %d\n", " for id: %d\n",
__FUNCTION__, scmd->device->id)); current->comm,
scmd->device->id));
rtn = scsi_request_sense(scmd); rtn = scsi_request_sense(scmd);
if (rtn != SUCCESS) if (rtn != SUCCESS)
continue; continue;
...@@ -752,7 +703,7 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -752,7 +703,7 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
* upper level. * upper level.
*/ */
if (rtn == SUCCESS) if (rtn == SUCCESS)
scsi_eh_finish_cmd(scmd, shost); scsi_eh_finish_cmd(scmd, done_q);
if (rtn != NEEDS_RETRY) if (rtn != NEEDS_RETRY)
continue; continue;
...@@ -771,10 +722,10 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -771,10 +722,10 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
/* /*
* we eventually hand this one back to the top level. * we eventually hand this one back to the top level.
*/ */
scsi_eh_finish_cmd(scmd, shost); scsi_eh_finish_cmd(scmd, done_q);
} }
return shost->host_failed; return list_empty(work_q);
} }
/** /**
...@@ -788,14 +739,14 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -788,14 +739,14 @@ static int scsi_eh_get_sense(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
* they can provide this facility themselves. helper functions in * they can provide this facility themselves. helper functions in
* scsi_error.c can be supplied to make this easier to do. * scsi_error.c can be supplied to make this easier to do.
**/ **/
static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd) static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
{ {
int rtn = FAILED;
unsigned long flags; unsigned long flags;
int rtn = FAILED;
if (scmd->device->host->hostt->eh_abort_handler == NULL) { if (!scmd->device->host->hostt->eh_abort_handler)
return rtn; return rtn;
}
/* /*
* scsi_done was called just after the command timed out and before * scsi_done was called just after the command timed out and before
* we had a chance to process it. (db) * we had a chance to process it. (db)
...@@ -808,6 +759,7 @@ static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd) ...@@ -808,6 +759,7 @@ static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd)
spin_lock_irqsave(scmd->device->host->host_lock, flags); spin_lock_irqsave(scmd->device->host->host_lock, flags);
rtn = scmd->device->host->hostt->eh_abort_handler(scmd); rtn = scmd->device->host->hostt->eh_abort_handler(scmd);
spin_unlock_irqrestore(scmd->device->host->host_lock, flags); spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
return rtn; return rtn;
} }
...@@ -818,22 +770,19 @@ static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd) ...@@ -818,22 +770,19 @@ static int scsi_try_to_abort_cmd(Scsi_Cmnd *scmd)
* Return value: * Return value:
* 0 - Device is ready. 1 - Device NOT ready. * 0 - Device is ready. 1 - Device NOT ready.
**/ **/
static int scsi_eh_tur(Scsi_Cmnd *scmd) static int scsi_eh_tur(struct scsi_cmnd *scmd)
{ {
static unsigned char tur_command[6] = static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
{TEST_UNIT_READY, 0, 0, 0, 0, 0}; int retry_cnt = 1, rtn;
int rtn;
int retry_cnt = 1;
retry_tur: retry_tur:
memcpy((void *) scmd->cmnd, (void *) tur_command, memcpy(scmd->cmnd, tur_command, sizeof(tur_command));
sizeof(tur_command));
/* /*
* zero the sense buffer. the scsi spec mandates that any * zero the sense buffer. the scsi spec mandates that any
* untransferred sense data should be interpreted as being zero. * untransferred sense data should be interpreted as being zero.
*/ */
memset((void *) scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
scmd->request_buffer = NULL; scmd->request_buffer = NULL;
scmd->request_bufflen = 0; scmd->request_bufflen = 0;
...@@ -864,9 +813,9 @@ static int scsi_eh_tur(Scsi_Cmnd *scmd) ...@@ -864,9 +813,9 @@ static int scsi_eh_tur(Scsi_Cmnd *scmd)
} }
/** /**
* scsi_eh_abort_cmd - abort a timed-out cmd. * scsi_eh_abort_cmds - abort canceled commands.
* @sc_todo: A list of cmds that have failed.
* @shost: scsi host being recovered. * @shost: scsi host being recovered.
* @eh_done_q: list_head for processed commands.
* *
* Decription: * Decription:
* Try and see whether or not it makes sense to try and abort the * Try and see whether or not it makes sense to try and abort the
...@@ -875,29 +824,36 @@ static int scsi_eh_tur(Scsi_Cmnd *scmd) ...@@ -875,29 +824,36 @@ static int scsi_eh_tur(Scsi_Cmnd *scmd)
* no sense to try and abort the command, since as far as the shost * no sense to try and abort the command, since as far as the shost
* adapter is concerned, it isn't running. * adapter is concerned, it isn't running.
**/ **/
static int scsi_eh_abort_cmd(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) static int scsi_eh_abort_cmds(struct list_head *work_q,
struct list_head *done_q)
{ {
struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd;
int rtn; int rtn;
Scsi_Cmnd *scmd;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: checking to see if we need" list_for_each_safe(lh, lh_sf, work_q) {
" to abort cmd\n", __FUNCTION__)); scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD))
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_TIMEOUT))
continue; continue;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:"
"0x%p\n", current->comm,
scmd));
rtn = scsi_try_to_abort_cmd(scmd); rtn = scsi_try_to_abort_cmd(scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS) {
if (!scsi_eh_tur(scmd)) { scsi_eh_eflags_clr(scmd, SCSI_EH_CANCEL_CMD);
rtn = scsi_eh_retry_cmd(scmd); if (!scmd->device->online || !scsi_eh_tur(scmd)) {
if (rtn == SUCCESS) scsi_eh_finish_cmd(scmd, done_q);
scsi_eh_finish_cmd(scmd, shost);
}
} }
} else
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting"
" cmd failed:"
"0x%p\n",
current->comm,
scmd));
} }
return shost->host_failed;
return list_empty(work_q);
} }
/** /**
...@@ -910,14 +866,14 @@ static int scsi_eh_abort_cmd(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -910,14 +866,14 @@ static int scsi_eh_abort_cmd(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
* timer on it, and set the host back to a consistent state prior to * timer on it, and set the host back to a consistent state prior to
* returning. * returning.
**/ **/
static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd) static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
{ {
unsigned long flags; unsigned long flags;
int rtn = FAILED; int rtn = FAILED;
if (scmd->device->host->hostt->eh_device_reset_handler == NULL) { if (!scmd->device->host->hostt->eh_device_reset_handler)
return rtn; return rtn;
}
scmd->owner = SCSI_OWNER_LOWLEVEL; scmd->owner = SCSI_OWNER_LOWLEVEL;
spin_lock_irqsave(scmd->device->host->host_lock, flags); spin_lock_irqsave(scmd->device->host->host_lock, flags);
...@@ -933,9 +889,9 @@ static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd) ...@@ -933,9 +889,9 @@ static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd)
} }
/** /**
* scsi_eh_bus_device_reset - send bdr is needed * scsi_eh_bus_device_reset - send bdr if needed
* @sc_todo: a list of cmds that have failed.
* @shost: scsi host being recovered. * @shost: scsi host being recovered.
* @eh_done_q: list_head for processed commands.
* *
* Notes: * Notes:
* Try a bus device reset. still, look to see whether we have multiple * Try a bus device reset. still, look to see whether we have multiple
...@@ -943,57 +899,70 @@ static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd) ...@@ -943,57 +899,70 @@ static int scsi_try_bus_device_reset(Scsi_Cmnd *scmd)
* makes no sense to try bus_device_reset - we really would need to try * makes no sense to try bus_device_reset - we really would need to try
* a bus_reset instead. * a bus_reset instead.
**/ **/
static int scsi_eh_bus_device_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
struct list_head *work_q,
struct list_head *done_q)
{ {
struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd, *bdr_scmd;
struct scsi_device *sdev;
int rtn; int rtn;
Scsi_Cmnd *scmd;
Scsi_Device *sdev;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Trying BDR\n", __FUNCTION__));
list_for_each_entry(sdev, &shost->my_devices, siblings) { list_for_each_entry(sdev, &shost->my_devices, siblings) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) bdr_scmd = NULL;
if ((scmd->device == sdev) && list_for_each_entry(scmd, work_q, eh_entry)
scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) if (scmd->device == sdev) {
bdr_scmd = scmd;
break; break;
}
if (!scmd) if (!bdr_scmd)
continue; continue;
/* SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending BDR sdev:"
* ok, we have a device that is having problems. try and send " 0x%p\n", current->comm,
* a bus device reset to it. sdev));
*/ rtn = scsi_try_bus_device_reset(bdr_scmd);
rtn = scsi_try_bus_device_reset(scmd); if (rtn == SUCCESS) {
if ((rtn == SUCCESS) && (!scsi_eh_tur(scmd))) if (!sdev->online || !scsi_eh_tur(bdr_scmd)) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) list_for_each_safe(lh, lh_sf,
if ((scmd->device == sdev) && work_q) {
scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) { scmd = list_entry(lh, struct
rtn = scsi_eh_retry_cmd(scmd); scsi_cmnd,
if (rtn == SUCCESS) eh_entry);
scsi_eh_finish_cmd(scmd, shost); if (scmd->device == sdev)
scsi_eh_finish_cmd(scmd,
done_q);
}
}
} else {
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BDR"
" failed sdev:"
"0x%p\n",
current->comm,
sdev));
} }
} }
return shost->host_failed; return list_empty(work_q);
} }
/** /**
* scsi_try_bus_reset - ask host to perform a bus reset * scsi_try_bus_reset - ask host to perform a bus reset
* @scmd: SCSI cmd to send bus reset. * @scmd: SCSI cmd to send bus reset.
**/ **/
static int scsi_try_bus_reset(Scsi_Cmnd *scmd) static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
{ {
struct scsi_device *sdev;
unsigned long flags; unsigned long flags;
int rtn; int rtn;
Scsi_Device *sdev;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n", SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
__FUNCTION__)); __FUNCTION__));
scmd->owner = SCSI_OWNER_LOWLEVEL; scmd->owner = SCSI_OWNER_LOWLEVEL;
scmd->serial_number_at_timeout = scmd->serial_number; scmd->serial_number_at_timeout = scmd->serial_number;
if (scmd->device->host->hostt->eh_bus_reset_handler == NULL) if (!scmd->device->host->hostt->eh_bus_reset_handler)
return FAILED; return FAILED;
spin_lock_irqsave(scmd->device->host->host_lock, flags); spin_lock_irqsave(scmd->device->host->host_lock, flags);
...@@ -1005,7 +974,8 @@ static int scsi_try_bus_reset(Scsi_Cmnd *scmd) ...@@ -1005,7 +974,8 @@ static int scsi_try_bus_reset(Scsi_Cmnd *scmd)
/* /*
* Mark all affected devices to expect a unit attention. * Mark all affected devices to expect a unit attention.
*/ */
list_for_each_entry(sdev, &scmd->device->host->my_devices, siblings) list_for_each_entry(sdev, &scmd->device->host->my_devices,
siblings)
if (scmd->device->channel == sdev->channel) { if (scmd->device->channel == sdev->channel) {
sdev->was_reset = 1; sdev->was_reset = 1;
sdev->expecting_cc_ua = 1; sdev->expecting_cc_ua = 1;
...@@ -1018,18 +988,18 @@ static int scsi_try_bus_reset(Scsi_Cmnd *scmd) ...@@ -1018,18 +988,18 @@ static int scsi_try_bus_reset(Scsi_Cmnd *scmd)
* scsi_try_host_reset - ask host adapter to reset itself * scsi_try_host_reset - ask host adapter to reset itself
* @scmd: SCSI cmd to send hsot reset. * @scmd: SCSI cmd to send hsot reset.
**/ **/
static int scsi_try_host_reset(Scsi_Cmnd *scmd) static int scsi_try_host_reset(struct scsi_cmnd *scmd)
{ {
struct scsi_device *sdev;
unsigned long flags; unsigned long flags;
int rtn; int rtn;
Scsi_Device *sdev;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n", SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
__FUNCTION__)); __FUNCTION__));
scmd->owner = SCSI_OWNER_LOWLEVEL; scmd->owner = SCSI_OWNER_LOWLEVEL;
scmd->serial_number_at_timeout = scmd->serial_number; scmd->serial_number_at_timeout = scmd->serial_number;
if (scmd->device->host->hostt->eh_host_reset_handler == NULL) if (!scmd->device->host->hostt->eh_host_reset_handler)
return FAILED; return FAILED;
spin_lock_irqsave(scmd->device->host->host_lock, flags); spin_lock_irqsave(scmd->device->host->host_lock, flags);
...@@ -1041,7 +1011,8 @@ static int scsi_try_host_reset(Scsi_Cmnd *scmd) ...@@ -1041,7 +1011,8 @@ static int scsi_try_host_reset(Scsi_Cmnd *scmd)
/* /*
* Mark all affected devices to expect a unit attention. * Mark all affected devices to expect a unit attention.
*/ */
list_for_each_entry(sdev, &scmd->device->host->my_devices, siblings) list_for_each_entry(sdev, &scmd->device->host->my_devices,
siblings)
if (scmd->device->channel == sdev->channel) { if (scmd->device->channel == sdev->channel) {
sdev->was_reset = 1; sdev->was_reset = 1;
sdev->expecting_cc_ua = 1; sdev->expecting_cc_ua = 1;
...@@ -1051,24 +1022,19 @@ static int scsi_try_host_reset(Scsi_Cmnd *scmd) ...@@ -1051,24 +1022,19 @@ static int scsi_try_host_reset(Scsi_Cmnd *scmd)
} }
/** /**
* scsi_eh_bus_host_reset - send a bus reset and on failure try host reset * scsi_eh_bus_reset - send a bus reset
* @sc_todo: a list of cmds that have failed.
* @shost: scsi host being recovered. * @shost: scsi host being recovered.
* @eh_done_q: list_head for processed commands.
**/ **/
static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) static int scsi_eh_bus_reset(struct Scsi_Host *shost,
struct list_head *work_q,
struct list_head *done_q)
{ {
int rtn; struct list_head *lh, *lh_sf;
Scsi_Cmnd *scmd; struct scsi_cmnd *scmd;
Scsi_Cmnd *chan_scmd; struct scsi_cmnd *chan_scmd;
unsigned int channel; unsigned int channel;
int rtn;
/*
* 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("%s: Try Bus/Host RST\n",
__FUNCTION__));
/* /*
* we really want to loop over the various channels, and do this on * we really want to loop over the various channels, and do this on
...@@ -1078,9 +1044,8 @@ static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -1078,9 +1044,8 @@ static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
*/ */
for (channel = 0; channel <= shost->max_channel; channel++) { for (channel = 0; channel <= shost->max_channel; channel++) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) { chan_scmd = NULL;
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR)) list_for_each_entry(scmd, work_q, eh_entry) {
continue;
if (channel == scmd->device->channel) { if (channel == scmd->device->channel) {
chan_scmd = scmd; chan_scmd = scmd;
break; break;
...@@ -1091,63 +1056,95 @@ static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -1091,63 +1056,95 @@ static int scsi_eh_bus_host_reset(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
} }
} }
if (!scmd) if (!chan_scmd)
continue; continue;
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending BRST chan:"
/* " %d\n", current->comm,
* we now know that we are able to perform a reset for the channel));
* channel that scmd points to. rtn = scsi_try_bus_reset(chan_scmd);
*/
rtn = scsi_try_bus_reset(scmd);
if (rtn != SUCCESS)
rtn = scsi_try_host_reset(scmd);
if (rtn == SUCCESS) { if (rtn == SUCCESS) {
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) { list_for_each_safe(lh, lh_sf, work_q) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR) scmd = list_entry(lh, struct scsi_cmnd,
|| channel != scmd->device->channel) eh_entry);
continue; if (channel == scmd->device->channel)
if (!scsi_eh_tur(scmd)) { if (!scmd->device->online ||
rtn = scsi_eh_retry_cmd(scmd); !scsi_eh_tur(scmd))
scsi_eh_finish_cmd(scmd,
if (rtn == SUCCESS) done_q);
scsi_eh_finish_cmd(scmd, shost);
} }
} else {
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BRST"
" failed chan: %d\n",
current->comm,
channel));
} }
} }
return list_empty(work_q);
}
/**
* scsi_eh_host_reset - send a host reset
* @work_q: list_head for processed commands.
* @done_q: list_head for processed commands.
**/
static int scsi_eh_host_reset(struct list_head *work_q,
struct list_head *done_q)
{
int rtn;
struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd;
if (!list_empty(work_q)) {
scmd = list_entry(work_q->next,
struct scsi_cmnd, eh_entry);
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending HRST\n"
, current->comm));
rtn = scsi_try_host_reset(scmd);
if (rtn == SUCCESS) {
list_for_each_safe(lh, lh_sf, work_q) {
scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
if (!scmd->device->online || !scsi_eh_tur(scmd))
scsi_eh_finish_cmd(scmd, done_q);
}
} else {
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: HRST"
" failed\n",
current->comm));
} }
return shost->host_failed; }
return list_empty(work_q);
} }
/** /**
* scsi_eh_offline_sdevs - offline scsi devices that fail to recover * scsi_eh_offline_sdevs - offline scsi devices that fail to recover
* @sc_todo: a list of cmds that have failed. * @work_q: list_head for processed commands.
* @shost: scsi host being recovered. * @done_q: list_head for processed commands.
* *
**/ **/
static void scsi_eh_offline_sdevs(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) static void scsi_eh_offline_sdevs(struct list_head *work_q,
struct list_head *done_q)
{ {
Scsi_Cmnd *scmd; struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd;
for (scmd = sc_todo; scmd; scmd = scmd->bh_next) {
if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_ERR))
continue;
list_for_each_safe(lh, lh_sf, work_q) {
scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
printk(KERN_INFO "scsi: Device offlined - not" printk(KERN_INFO "scsi: Device offlined - not"
" ready or command retry failed" " ready after error recovery: host"
" after error recovery: host"
" %d channel %d id %d lun %d\n", " %d channel %d id %d lun %d\n",
shost->host_no, scmd->device->host->host_no,
scmd->device->channel, scmd->device->channel,
scmd->device->id, scmd->device->id,
scmd->device->lun); scmd->device->lun);
scmd->device->online = FALSE;
if (scsi_eh_eflags_chk(scmd, SCSI_EH_CMD_TIMEOUT)) if (scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD)) {
scmd->result |= (DRIVER_TIMEOUT << 24); /*
* FIXME: Handle lost cmds.
scmd->device->online = 0; */
scsi_eh_finish_cmd(scmd, shost); }
scsi_eh_finish_cmd(scmd, done_q);
} }
return; return;
} }
...@@ -1157,12 +1154,12 @@ static void scsi_eh_offline_sdevs(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost) ...@@ -1157,12 +1154,12 @@ static void scsi_eh_offline_sdevs(Scsi_Cmnd *sc_todo, struct Scsi_Host *shost)
* @sem: semphore to signal * @sem: semphore to signal
* *
**/ **/
static static void scsi_sleep_done(unsigned long data)
void scsi_sleep_done(struct semaphore *sem)
{ {
if (sem != NULL) { struct semaphore *sem = (struct semaphore *)data;
if (sem)
up(sem); up(sem);
}
} }
/** /**
...@@ -1176,9 +1173,9 @@ void scsi_sleep(int timeout) ...@@ -1176,9 +1173,9 @@ void scsi_sleep(int timeout)
struct timer_list timer; struct timer_list timer;
init_timer(&timer); init_timer(&timer);
timer.data = (unsigned long) &sem; timer.data = (unsigned long)&sem;
timer.expires = jiffies + timeout; timer.expires = jiffies + timeout;
timer.function = (void (*)(unsigned long)) scsi_sleep_done; timer.function = (void (*)(unsigned long))scsi_sleep_done;
SCSI_LOG_ERROR_RECOVERY(5, printk("sleeping for timer tics %d\n", SCSI_LOG_ERROR_RECOVERY(5, printk("sleeping for timer tics %d\n",
timeout)); timeout));
...@@ -1203,7 +1200,7 @@ void scsi_sleep(int timeout) ...@@ -1203,7 +1200,7 @@ void scsi_sleep(int timeout)
* doesn't require the error handler read (i.e. we don't need to * doesn't require the error handler read (i.e. we don't need to
* abort/reset), then this function should return SUCCESS. * abort/reset), then this function should return SUCCESS.
**/ **/
int scsi_decide_disposition(Scsi_Cmnd *scmd) int scsi_decide_disposition(struct scsi_cmnd *scmd)
{ {
int rtn; int rtn;
...@@ -1403,7 +1400,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) ...@@ -1403,7 +1400,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev)
{ {
struct scsi_request *sreq = scsi_allocate_request(sdev); struct scsi_request *sreq = scsi_allocate_request(sdev);
if (sreq == NULL) { if (unlikely(!sreq)) {
printk(KERN_ERR "%s: request allocate failed," printk(KERN_ERR "%s: request allocate failed,"
"prevent media removal cmd not sent", __FUNCTION__); "prevent media removal cmd not sent", __FUNCTION__);
return; return;
...@@ -1437,7 +1434,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) ...@@ -1437,7 +1434,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev)
**/ **/
static void scsi_restart_operations(struct Scsi_Host *shost) static void scsi_restart_operations(struct Scsi_Host *shost)
{ {
Scsi_Device *sdev; struct scsi_device *sdev;
unsigned long flags; unsigned long flags;
ASSERT_LOCK(shost->host_lock, 0); ASSERT_LOCK(shost->host_lock, 0);
...@@ -1459,6 +1456,8 @@ static void scsi_restart_operations(struct Scsi_Host *shost) ...@@ -1459,6 +1456,8 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
__FUNCTION__)); __FUNCTION__));
shost->in_recovery = 0;
wake_up(&shost->host_wait); wake_up(&shost->host_wait);
/* /*
...@@ -1481,6 +1480,55 @@ static void scsi_restart_operations(struct Scsi_Host *shost) ...@@ -1481,6 +1480,55 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
} }
/**
* scsi_eh_ready_devs - check device ready state and recover if not.
* @shost: host to be recovered.
* @eh_done_q: list_head for processed commands.
*
**/
static void scsi_eh_ready_devs(struct Scsi_Host *shost,
struct list_head *work_q,
struct list_head *done_q)
{
if (scsi_eh_bus_device_reset(shost, work_q, done_q))
if (scsi_eh_bus_reset(shost, work_q, done_q))
if (scsi_eh_host_reset(work_q, done_q))
scsi_eh_offline_sdevs(work_q, done_q);
}
/**
* scsi_eh_flush_done_q - finish processed commands or retry them.
* @done_q: list_head of processed commands.
*
**/
static void scsi_eh_flush_done_q(struct list_head *done_q)
{
struct list_head *lh, *lh_sf;
struct scsi_cmnd *scmd;
list_for_each_safe(lh, lh_sf, done_q) {
scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
list_del_init(lh);
if (!scmd->device->online) {
scmd->result |= (DRIVER_TIMEOUT << 24);
} else {
if (++scmd->retries < scmd->allowed) {
SCSI_LOG_ERROR_RECOVERY(3,
printk("%s: flush retry"
" cmd: %p\n",
current->comm,
scmd));
scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
continue;
}
}
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: flush finish"
" cmd: %p\n",
current->comm, scmd));
scsi_finish_command(scmd);
}
}
/** /**
* scsi_unjam_host - Attempt to fix a host which has a cmd that failed. * scsi_unjam_host - Attempt to fix a host which has a cmd that failed.
* @shost: Host to unjam. * @shost: Host to unjam.
...@@ -1506,60 +1554,21 @@ static void scsi_restart_operations(struct Scsi_Host *shost) ...@@ -1506,60 +1554,21 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
**/ **/
static void scsi_unjam_host(struct Scsi_Host *shost) static void scsi_unjam_host(struct Scsi_Host *shost)
{ {
Scsi_Cmnd *sc_todo = NULL; unsigned long flags;
Scsi_Cmnd *scmd; LIST_HEAD(eh_work_q);
LIST_HEAD(eh_done_q);
/*
* Is this assert really ok anymore (andmike). Should we at least
* be using spin_lock_unlocked.
*/
ASSERT_LOCK(shost->host_lock, 0);
scsi_eh_get_failed(&sc_todo, shost);
if (scsi_eh_get_sense(sc_todo, shost)) spin_lock_irqsave(shost->host_lock, flags);
if (scsi_eh_abort_cmd(sc_todo, shost)) list_splice_init(&shost->eh_cmd_q, &eh_work_q);
if (scsi_eh_bus_device_reset(sc_todo, shost)) spin_unlock_irqrestore(shost->host_lock, flags);
if (scsi_eh_bus_host_reset(sc_todo, shost))
scsi_eh_offline_sdevs(sc_todo, shost);
BUG_ON(shost->host_failed); SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));
if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
/* scsi_eh_flush_done_q(&eh_done_q);
* We are currently holding these things in a linked list - we
* didn't put them in the bottom half queue because we wanted to
* keep things quiet while we were working on recovery, and
* passing them up to the top level could easily cause the top
* level to try and queue something else again.
*
* start by marking that the host is no longer in error recovery.
*/
shost->in_recovery = 0;
/*
* 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
* be we can create a special version of this function to do the
* same job for us.
*/
for (scmd = sc_todo; scmd; scmd = sc_todo) {
sc_todo = scmd->bh_next;
scmd->bh_next = NULL;
/*
* Oh, this is a vile hack. scsi_done() expects a timer
* to be running on the command. If there isn't, it assumes
* that the command has actually timed out, and a timer
* handler is running. That may well be how we got into
* this fix, but right now things are stable. We add
* a timer back again so that we can report completion.
* scsi_done() will immediately remove said timer from
* the command, and then process it.
*/
scsi_add_timer(scmd, 100, scsi_eh_times_out);
scsi_done(scmd);
}
} }
/** /**
...@@ -1597,7 +1606,8 @@ void scsi_error_handler(void *data) ...@@ -1597,7 +1606,8 @@ void scsi_error_handler(void *data)
/* /*
* Wake up the thread that created us. * Wake up the thread that created us.
*/ */
SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of scsi_eh_%d\n",shost->host_no)); SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of"
" scsi_eh_%d\n",shost->host_no));
complete(shost->eh_notify); complete(shost->eh_notify);
...@@ -1607,7 +1617,9 @@ void scsi_error_handler(void *data) ...@@ -1607,7 +1617,9 @@ void scsi_error_handler(void *data)
* away and die. This typically happens if the user is * away and die. This typically happens if the user is
* trying to unload a module. * trying to unload a module.
*/ */
SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d sleeping\n",shost->host_no)); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler"
" scsi_eh_%d"
" sleeping\n",shost->host_no));
/* /*
* Note - we always use down_interruptible with the semaphore * Note - we always use down_interruptible with the semaphore
...@@ -1622,7 +1634,9 @@ void scsi_error_handler(void *data) ...@@ -1622,7 +1634,9 @@ void scsi_error_handler(void *data)
if (shost->eh_kill) if (shost->eh_kill)
break; break;
SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d waking up\n",shost->host_no)); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler"
" scsi_eh_%d waking"
" up\n",shost->host_no));
shost->eh_active = 1; shost->eh_active = 1;
...@@ -1631,11 +1645,10 @@ void scsi_error_handler(void *data) ...@@ -1631,11 +1645,10 @@ void scsi_error_handler(void *data)
* 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 (shost->hostt->eh_strategy_handler != NULL) { if (shost->hostt->eh_strategy_handler)
rtn = shost->hostt->eh_strategy_handler(shost); rtn = shost->hostt->eh_strategy_handler(shost);
} else { else
scsi_unjam_host(shost); scsi_unjam_host(shost);
}
shost->eh_active = 0; shost->eh_active = 0;
...@@ -1650,7 +1663,8 @@ void scsi_error_handler(void *data) ...@@ -1650,7 +1663,8 @@ void scsi_error_handler(void *data)
} }
SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d exiting\n",shost->host_no)); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d"
" exiting\n",shost->host_no));
/* /*
* Make sure that nobody tries to wake us up again. * Make sure that nobody tries to wake us up again.
...@@ -1675,45 +1689,8 @@ void scsi_error_handler(void *data) ...@@ -1675,45 +1689,8 @@ void scsi_error_handler(void *data)
complete_and_exit(shost->eh_notify, 0); complete_and_exit(shost->eh_notify, 0);
} }
/**
* scsi_new_reset - Send reset to a bus or device at any phase.
* @scmd: Cmd to send reset with (usually a dummy)
* @flag: Reset type.
*
* Description:
* This is used by the SCSI Generic driver to provide Bus/Device reset
* capability.
*
* Return value:
* SUCCESS/FAILED.
**/
static int scsi_new_reset(Scsi_Cmnd *scmd, int flag)
{
int rtn;
switch(flag) {
case SCSI_TRY_RESET_DEVICE:
rtn = scsi_try_bus_device_reset(scmd);
if (rtn == SUCCESS)
break;
/* FALLTHROUGH */
case SCSI_TRY_RESET_BUS:
rtn = scsi_try_bus_reset(scmd);
if (rtn == SUCCESS)
break;
/* FALLTHROUGH */
case SCSI_TRY_RESET_HOST:
rtn = scsi_try_host_reset(scmd);
break;
default:
rtn = FAILED;
}
return rtn;
}
static void static void
scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt) scsi_reset_provider_done_command(struct scsi_cmnd *scmd)
{ {
} }
...@@ -1731,47 +1708,65 @@ scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt) ...@@ -1731,47 +1708,65 @@ scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt)
* Bus/Device reset capability. * Bus/Device reset capability.
*/ */
int int
scsi_reset_provider(Scsi_Device *dev, int flag) scsi_reset_provider(struct scsi_device *dev, int flag)
{ {
struct scsi_cmnd *SCpnt = scsi_get_command(dev, GFP_KERNEL); struct scsi_cmnd *scmd = scsi_get_command(dev, GFP_KERNEL);
struct request req; struct request req;
int rtn; int rtn;
SCpnt->request = &req; scmd->request = &req;
memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout)); memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
SCpnt->request->rq_status = RQ_SCSI_BUSY; scmd->request->rq_status = RQ_SCSI_BUSY;
SCpnt->state = SCSI_STATE_INITIALIZING; scmd->state = SCSI_STATE_INITIALIZING;
SCpnt->owner = SCSI_OWNER_MIDLEVEL; scmd->owner = SCSI_OWNER_MIDLEVEL;
memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd)); scmd->scsi_done = scsi_reset_provider_done_command;
scmd->done = NULL;
scmd->reset_chain = NULL;
SCpnt->scsi_done = scsi_reset_provider_done_command; scmd->buffer = NULL;
SCpnt->done = NULL; scmd->bufflen = 0;
SCpnt->reset_chain = NULL; scmd->request_buffer = NULL;
scmd->request_bufflen = 0;
SCpnt->buffer = NULL; scmd->internal_timeout = NORMAL_TIMEOUT;
SCpnt->bufflen = 0; scmd->abort_reason = DID_ABORT;
SCpnt->request_buffer = NULL;
SCpnt->request_bufflen = 0;
SCpnt->internal_timeout = NORMAL_TIMEOUT; scmd->cmd_len = 0;
SCpnt->abort_reason = DID_ABORT;
SCpnt->cmd_len = 0; scmd->sc_data_direction = SCSI_DATA_UNKNOWN;
scmd->sc_request = NULL;
scmd->sc_magic = SCSI_CMND_MAGIC;
SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; init_timer(&scmd->eh_timeout);
SCpnt->sc_request = NULL;
SCpnt->sc_magic = SCSI_CMND_MAGIC;
/* /*
* Sometimes the command can get back into the timer chain, * Sometimes the command can get back into the timer chain,
* so use the pid as an identifier. * so use the pid as an identifier.
*/ */
SCpnt->pid = 0; scmd->pid = 0;
rtn = scsi_new_reset(SCpnt, flag); switch (flag) {
case SCSI_TRY_RESET_DEVICE:
rtn = scsi_try_bus_device_reset(scmd);
if (rtn == SUCCESS)
break;
/* FALLTHROUGH */
case SCSI_TRY_RESET_BUS:
rtn = scsi_try_bus_reset(scmd);
if (rtn == SUCCESS)
break;
/* FALLTHROUGH */
case SCSI_TRY_RESET_HOST:
rtn = scsi_try_host_reset(scmd);
break;
default:
rtn = FAILED;
}
scsi_delete_timer(SCpnt); scsi_delete_timer(scmd);
scsi_put_command(SCpnt); scsi_put_command(scmd);
return rtn; return rtn;
} }
...@@ -117,7 +117,7 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) ...@@ -117,7 +117,7 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
*/ */
if (reason == SCSI_MLQUEUE_HOST_BUSY) if (reason == SCSI_MLQUEUE_HOST_BUSY)
host->host_blocked = host->max_host_blocked; host->host_blocked = host->max_host_blocked;
else else if (reason == SCSI_MLQUEUE_DEVICE_BUSY)
device->device_blocked = device->max_device_blocked; device->device_blocked = device->max_device_blocked;
/* /*
...@@ -1340,23 +1340,6 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel) ...@@ -1340,23 +1340,6 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
} }
} }
/*
* FIXME(eric) - these are empty stubs for the moment. I need to re-implement
* host blocking from scratch. The theory is that hosts that wish to block
* will register/deregister using these functions instead of the old way
* of setting the wish_block flag.
*
* The details of the implementation remain to be settled, however the
* stubs are here now so that the actual drivers will properly compile.
*/
void scsi_register_blocked_host(struct Scsi_Host * shost)
{
}
void scsi_deregister_blocked_host(struct Scsi_Host * shost)
{
}
int __init scsi_init_queue(void) int __init scsi_init_queue(void)
{ {
int i; int i;
......
...@@ -84,6 +84,7 @@ struct dev_info scsi_static_device_list[] __initdata = { ...@@ -84,6 +84,7 @@ struct dev_info scsi_static_device_list[] __initdata = {
{"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},/* locks up */ {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},/* locks up */
{"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* responds to all lun */ {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* responds to all lun */
{"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* locks up */ {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* locks up */
{"SUN", "SENA", NULL, BLIST_NOLUN}, /* responds to all luns */
/* /*
* The following causes a failed REQUEST SENSE on lun 1 for * The following causes a failed REQUEST SENSE on lun 1 for
* aha152x controller, which causes SCSI code to reset bus. * aha152x controller, which causes SCSI code to reset bus.
...@@ -128,6 +129,7 @@ struct dev_info scsi_static_device_list[] __initdata = { ...@@ -128,6 +129,7 @@ struct dev_info scsi_static_device_list[] __initdata = {
{"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */
{"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */
{"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */
{"NEC", "D3856", "0009", BLIST_NOLUN},
/* /*
* Other types of devices that have special flags. * Other types of devices that have special flags.
......
...@@ -74,12 +74,11 @@ EXPORT_SYMBOL(scsi_sleep); ...@@ -74,12 +74,11 @@ EXPORT_SYMBOL(scsi_sleep);
EXPORT_SYMBOL(scsi_io_completion); EXPORT_SYMBOL(scsi_io_completion);
EXPORT_SYMBOL(scsi_register_blocked_host);
EXPORT_SYMBOL(scsi_deregister_blocked_host);
EXPORT_SYMBOL(scsi_slave_attach); EXPORT_SYMBOL(scsi_slave_attach);
EXPORT_SYMBOL(scsi_slave_detach); EXPORT_SYMBOL(scsi_slave_detach);
EXPORT_SYMBOL(scsi_device_get); EXPORT_SYMBOL(scsi_device_get);
EXPORT_SYMBOL(scsi_device_put); EXPORT_SYMBOL(scsi_device_put);
EXPORT_SYMBOL(scsi_set_device_offline);
/* /*
* This symbol is for the highlevel drivers (e.g. sg) only. * This symbol is for the highlevel drivers (e.g. sg) only.
......
...@@ -14,6 +14,44 @@ ...@@ -14,6 +14,44 @@
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
/*
* shost_show_function: macro to create an attr function that can be used to
* show a non-bit field.
*/
#define shost_show_function(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
{ \
struct Scsi_Host *shost = to_scsi_host(dev); \
return snprintf (buf, 20, format_string, shost->field); \
}
/*
* shost_rd_attr: macro to create a function and attribute variable for a
* read only field.
*/
#define shost_rd_attr(field, format_string) \
shost_show_function(field, format_string) \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
/*
* Create the actual show/store functions and data structures.
*/
shost_rd_attr(unique_id, "%u\n");
shost_rd_attr(host_busy, "%hu\n");
shost_rd_attr(cmd_per_lun, "%hd\n");
shost_rd_attr(sg_tablesize, "%hu\n");
shost_rd_attr(unchecked_isa_dma, "%d\n");
static struct device_attribute *const shost_attrs[] = {
&dev_attr_unique_id,
&dev_attr_host_busy,
&dev_attr_cmd_per_lun,
&dev_attr_sg_tablesize,
&dev_attr_unchecked_isa_dma,
};
/** /**
* scsi_host_class_name_show - copy out the SCSI host name * scsi_host_class_name_show - copy out the SCSI host name
* @dev: device to check * @dev: device to check
...@@ -39,12 +77,21 @@ DEVICE_ATTR(class_name, S_IRUGO, scsi_host_class_name_show, NULL); ...@@ -39,12 +77,21 @@ DEVICE_ATTR(class_name, S_IRUGO, scsi_host_class_name_show, NULL);
static int scsi_host_class_add_dev(struct device * dev) static int scsi_host_class_add_dev(struct device * dev)
{ {
int i;
device_create_file(dev, &dev_attr_class_name); device_create_file(dev, &dev_attr_class_name);
for (i = 0; i < ARRAY_SIZE(shost_attrs); i++)
device_create_file(dev, shost_attrs[i]);
return 0; return 0;
} }
static void scsi_host_class_rm_dev(struct device * dev) static void scsi_host_class_rm_dev(struct device * dev)
{ {
int i;
for (i = 0; i < ARRAY_SIZE(shost_attrs); i++)
device_remove_file(dev, shost_attrs[i]);
device_remove_file(dev, &dev_attr_class_name); device_remove_file(dev, &dev_attr_class_name);
} }
...@@ -129,10 +176,10 @@ void scsi_upper_driver_unregister(struct Scsi_Device_Template *sdev_tp) ...@@ -129,10 +176,10 @@ void scsi_upper_driver_unregister(struct Scsi_Device_Template *sdev_tp)
/* /*
* show_function: macro to create an attr function that can be used to * sdev_show_function: macro to create an attr function that can be used to
* show a non-bit field. * show a non-bit field.
*/ */
#define show_function(field, format_string) \ #define sdev_show_function(field, format_string) \
static ssize_t \ static ssize_t \
show_##field (struct device *dev, char *buf) \ show_##field (struct device *dev, char *buf) \
{ \ { \
...@@ -146,7 +193,7 @@ show_##field (struct device *dev, char *buf) \ ...@@ -146,7 +193,7 @@ show_##field (struct device *dev, char *buf) \
* read only field. * read only field.
*/ */
#define sdev_rd_attr(field, format_string) \ #define sdev_rd_attr(field, format_string) \
show_function(field, format_string) \ sdev_show_function(field, format_string) \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
...@@ -155,27 +202,27 @@ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) ...@@ -155,27 +202,27 @@ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
* read/write field. * read/write field.
*/ */
#define sdev_rw_attr(field, format_string) \ #define sdev_rw_attr(field, format_string) \
show_function(field, format_string) \ sdev_show_function(field, format_string) \
\ \
static ssize_t \ static ssize_t \
store_##field (struct device *dev, const char *buf, size_t count) \ sdev_store_##field (struct device *dev, const char *buf, size_t count) \
{ \ { \
struct scsi_device *sdev; \ struct scsi_device *sdev; \
sdev = to_scsi_device(dev); \ sdev = to_scsi_device(dev); \
snscanf (buf, 20, format_string, &sdev->field); \ snscanf (buf, 20, format_string, &sdev->field); \
return count; \ return count; \
} \ } \
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, store_##field) static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, sdev_store_##field)
/* /*
* sdev_rd_attr: create a function and attribute variable for a * sdev_rd_attr: create a function and attribute variable for a
* read/write bit field. * read/write bit field.
*/ */
#define sdev_rw_attr_bit(field) \ #define sdev_rw_attr_bit(field) \
show_function(field, "%d\n") \ sdev_show_function(field, "%d\n") \
\ \
static ssize_t \ static ssize_t \
store_##field (struct device *dev, const char *buf, size_t count) \ sdev_store_##field (struct device *dev, const char *buf, size_t count) \
{ \ { \
int ret; \ int ret; \
struct scsi_device *sdev; \ struct scsi_device *sdev; \
...@@ -187,7 +234,7 @@ store_##field (struct device *dev, const char *buf, size_t count) \ ...@@ -187,7 +234,7 @@ store_##field (struct device *dev, const char *buf, size_t count) \
} \ } \
return ret; \ return ret; \
} \ } \
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, store_##field) static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, sdev_store_##field)
/* /*
* scsi_sdev_check_buf_bit: return 0 if buf is "0", return 1 if buf is "1", * scsi_sdev_check_buf_bit: return 0 if buf is "0", return 1 if buf is "1",
......
...@@ -2689,18 +2689,6 @@ static int sg_proc_devstrs_read(char *buffer, char **start, off_t offset, ...@@ -2689,18 +2689,6 @@ static int sg_proc_devstrs_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data); int size, int *eof, void *data);
static int sg_proc_devstrs_info(char *buffer, int *len, off_t * begin, static int sg_proc_devstrs_info(char *buffer, int *len, off_t * begin,
off_t offset, int size); off_t offset, int size);
static int sg_proc_host_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data);
static int sg_proc_host_info(char *buffer, int *len, off_t * begin,
off_t offset, int size);
static int sg_proc_hosthdr_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data);
static int sg_proc_hosthdr_info(char *buffer, int *len, off_t * begin,
off_t offset, int size);
static int sg_proc_hoststrs_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data);
static int sg_proc_hoststrs_info(char *buffer, int *len, off_t * begin,
off_t offset, int size);
static int sg_proc_version_read(char *buffer, char **start, off_t offset, static int sg_proc_version_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data); int size, int *eof, void *data);
static int sg_proc_version_info(char *buffer, int *len, off_t * begin, static int sg_proc_version_info(char *buffer, int *len, off_t * begin,
...@@ -2708,7 +2696,6 @@ static int sg_proc_version_info(char *buffer, int *len, off_t * begin, ...@@ -2708,7 +2696,6 @@ static int sg_proc_version_info(char *buffer, int *len, off_t * begin,
static read_proc_t *sg_proc_leaf_reads[] = { static read_proc_t *sg_proc_leaf_reads[] = {
sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read,
sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read,
sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read,
sg_proc_version_read sg_proc_version_read
}; };
static write_proc_t *sg_proc_leaf_writes[] = { static write_proc_t *sg_proc_leaf_writes[] = {
...@@ -3033,81 +3020,6 @@ sg_proc_devstrs_info(char *buffer, int *len, off_t * begin, ...@@ -3033,81 +3020,6 @@ sg_proc_devstrs_info(char *buffer, int *len, off_t * begin,
return 1; return 1;
} }
static int
sg_proc_host_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data)
{
SG_PROC_READ_FN(sg_proc_host_info);
}
static int
sg_proc_host_info(char *buffer, int *len, off_t * begin, off_t offset, int size)
{
struct Scsi_Host *shp;
int k;
for (k = 0, shp = scsi_host_get_next(NULL); shp;
shp = scsi_host_get_next(shp), ++k) {
for (; k < shp->host_no; ++k)
PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\n");
PRINT_PROC("%u\t%hu\t%hd\t%hu\t%d\t%d\n",
shp->unique_id, shp->host_busy, shp->cmd_per_lun,
shp->sg_tablesize, (int) shp->unchecked_isa_dma,
(int) shp->hostt->emulated);
}
return 1;
}
static int
sg_proc_hosthdr_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data)
{
SG_PROC_READ_FN(sg_proc_hosthdr_info);
}
static int
sg_proc_hosthdr_info(char *buffer, int *len, off_t * begin,
off_t offset, int size)
{
PRINT_PROC("uid\tbusy\tcpl\tscatg\tisa\temul\n");
return 1;
}
static int
sg_proc_hoststrs_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data)
{
SG_PROC_READ_FN(sg_proc_hoststrs_info);
}
#define SG_MAX_HOST_STR_LEN 256
static int
sg_proc_hoststrs_info(char *buffer, int *len, off_t * begin,
off_t offset, int size)
{
struct Scsi_Host *shp;
int k;
char buff[SG_MAX_HOST_STR_LEN];
char *cp;
for (k = 0, shp = scsi_host_get_next(NULL); shp;
shp = scsi_host_get_next(shp), ++k) {
for (; k < shp->host_no; ++k)
PRINT_PROC("<no active host>\n");
strncpy(buff, shp->hostt->info ? shp->hostt->info(shp) :
(shp->hostt->name ? shp->hostt->name : "<no name>"),
SG_MAX_HOST_STR_LEN);
buff[SG_MAX_HOST_STR_LEN - 1] = '\0';
for (cp = buff; *cp; ++cp) {
if ('\n' == *cp)
*cp = ' '; /* suppress imbedded newlines */
}
PRINT_PROC("%s\n", buff);
}
return 1;
}
static int static int
sg_proc_version_read(char *buffer, char **start, off_t offset, sg_proc_version_read(char *buffer, char **start, off_t offset,
int size, int *eof, void *data) int size, int *eof, void *data)
......
...@@ -328,7 +328,7 @@ struct eisa_driver sim710_eisa_driver = { ...@@ -328,7 +328,7 @@ struct eisa_driver sim710_eisa_driver = {
static int __init sim710_init(void) static int __init sim710_init(void)
{ {
int err = -ENODEV, err2; int err = -ENODEV;
#ifdef MODULE #ifdef MODULE
if (sim710) if (sim710)
...@@ -336,23 +336,17 @@ static int __init sim710_init(void) ...@@ -336,23 +336,17 @@ static int __init sim710_init(void)
#endif #endif
#ifdef CONFIG_MCA #ifdef CONFIG_MCA
if (MCA_bus)
err = mca_register_driver(&sim710_mca_driver); err = mca_register_driver(&sim710_mca_driver);
#endif #endif
#ifdef CONFIG_EISA #ifdef CONFIG_EISA
err2 = eisa_driver_register(&sim710_eisa_driver); err = eisa_driver_register(&sim710_eisa_driver);
/*
* The eise_driver_register return values are strange. I have
* no idea why we don't just use river_register directly anyway..
*/
if (err2 == 1)
err2 = 0;
#endif #endif
/* FIXME: what we'd really like to return here is -ENODEV if
* no devices have actually been found. Instead, the err
* above actually only reports problems with kobject_register,
* so for the moment return success */
if (err < 0 || err2 < 0)
return (err < 0) ? err : err2;
return 0; return 0;
} }
......
...@@ -905,7 +905,6 @@ static int port_detect \ ...@@ -905,7 +905,6 @@ static int port_detect \
} }
else { else {
unsigned long flags; unsigned long flags;
scsi_register_blocked_host(sh[j]);
sh[j]->unchecked_isa_dma = TRUE; sh[j]->unchecked_isa_dma = TRUE;
flags=claim_dma_lock(); flags=claim_dma_lock();
...@@ -1911,8 +1910,6 @@ static int u14_34f_release(struct Scsi_Host *shpnt) { ...@@ -1911,8 +1910,6 @@ static int u14_34f_release(struct Scsi_Host *shpnt) {
if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n", if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
driver_name); driver_name);
if(sh[j]->unchecked_isa_dma) scsi_deregister_blocked_host(sh[j]);
for (i = 0; i < sh[j]->can_queue; i++) for (i = 0; i < sh[j]->can_queue; i++)
if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
......
...@@ -158,6 +158,11 @@ ...@@ -158,6 +158,11 @@
* Clean up delay to udelay, and yielding sleeps * Clean up delay to udelay, and yielding sleeps
* Make host reset actually reset the card * Make host reset actually reset the card
* Make everything static * Make everything static
*
* 2003/02/12 - Christoph Hellwig <hch@infradead.org>
*
* Cleaned up host template defintion
* Removed now obsolete wd7000.h
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -170,8 +175,8 @@ ...@@ -170,8 +175,8 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/blk.h> #include <linux/blk.h>
#include <linux/version.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/stat.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/dma.h> #include <asm/dma.h>
...@@ -179,9 +184,9 @@ ...@@ -179,9 +184,9 @@
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
#include <scsi/scsicam.h> #include <scsi/scsicam.h>
#define ANY2SCSI_INLINE /* undef this to use old macros */ #define ANY2SCSI_INLINE /* undef this to use old macros */
#undef WD7000_DEBUG /* general debug */ #undef WD7000_DEBUG /* general debug */
#ifdef WD7000_DEBUG #ifdef WD7000_DEBUG
...@@ -190,9 +195,6 @@ ...@@ -190,9 +195,6 @@
#define dprintk(format,args...) #define dprintk(format,args...)
#endif #endif
#include "wd7000.h"
#include <linux/stat.h>
/* /*
* Mailbox structure sizes. * Mailbox structure sizes.
* I prefer to keep the number of ICMBs much larger than the number of * I prefer to keep the number of ICMBs much larger than the number of
...@@ -211,6 +213,21 @@ ...@@ -211,6 +213,21 @@
*/ */
#define MAX_SCBS 32 #define MAX_SCBS 32
/*
* In this version, sg_tablesize now defaults to WD7000_SG, and will
* be set to SG_NONE for older boards. This is the reverse of the
* previous default, and was changed so that the driver-level
* Scsi_Host_Template would reflect the driver's support for scatter/
* gather.
*
* Also, it has been reported that boards at Revision 6 support scatter/
* gather, so the new definition of an "older" board has been changed
* accordingly.
*/
#define WD7000_Q 16
#define WD7000_SG 16
/* /*
* WD7000-specific mailbox structure * WD7000-specific mailbox structure
* *
...@@ -1737,7 +1754,23 @@ MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac"); ...@@ -1737,7 +1754,23 @@ MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac");
MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers"); MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* Eventually this will go into an include file, but this will be later */ static Scsi_Host_Template driver_template = {
static Scsi_Host_Template driver_template = WD7000; .proc_name = "wd7000",
.proc_info = wd7000_proc_info,
.name = "Western Digital WD-7000",
.detect = wd7000_detect,
.command = wd7000_command,
.queuecommand = wd7000_queuecommand,
.eh_bus_reset_handler = wd7000_bus_reset,
.eh_device_reset_handler = wd7000_device_reset,
.eh_host_reset_handler = wd7000_host_reset,
.bios_param = wd7000_biosparam,
.can_queue = WD7000_Q,
.this_id = 7,
.sg_tablesize = WD7000_SG,
.cmd_per_lun = 1,
.unchecked_isa_dma = 1,
.use_clustering = ENABLE_CLUSTERING,
};
#include "scsi_module.c" #include "scsi_module.c"
#ifndef _WD7000_H
/* $Id: $
*
* Header file for the WD-7000 driver for Linux
*
* John Boyd <boyd@cis.ohio-state.edu> Jan 1994:
* This file has been reduced to only the definitions needed for the
* WD7000 host structure.
*
* Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr> Jun 1997.
*/
#include <linux/types.h>
static int wd7000_set_info(char *buffer, int length, struct Scsi_Host *host);
static int wd7000_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
static int wd7000_detect(Scsi_Host_Template *);
static int wd7000_command(Scsi_Cmnd *);
static int wd7000_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int wd7000_abort(Scsi_Cmnd *);
static int wd7000_bus_reset(Scsi_Cmnd *);
static int wd7000_host_reset(Scsi_Cmnd *);
static int wd7000_device_reset(Scsi_Cmnd *);
static int wd7000_biosparam(struct scsi_device *, struct block_device *,
sector_t, int *);
#ifndef NULL
#define NULL 0L
#endif
/*
* In this version, sg_tablesize now defaults to WD7000_SG, and will
* be set to SG_NONE for older boards. This is the reverse of the
* previous default, and was changed so that the driver-level
* Scsi_Host_Template would reflect the driver's support for scatter/
* gather.
*
* Also, it has been reported that boards at Revision 6 support scatter/
* gather, so the new definition of an "older" board has been changed
* accordingly.
*/
#define WD7000_Q 16
#define WD7000_SG 16
#define WD7000 { \
.proc_name = "wd7000", \
.proc_info = wd7000_proc_info, \
.name = "Western Digital WD-7000", \
.detect = wd7000_detect, \
.command = wd7000_command, \
.queuecommand = wd7000_queuecommand, \
.eh_bus_reset_handler = wd7000_bus_reset, \
.eh_device_reset_handler = wd7000_device_reset, \
.eh_host_reset_handler = wd7000_host_reset, \
.bios_param = wd7000_biosparam, \
.can_queue = WD7000_Q, \
.this_id = 7, \
.sg_tablesize = WD7000_SG, \
.cmd_per_lun = 1, \
.unchecked_isa_dma = 1, \
.use_clustering = ENABLE_CLUSTERING, \
}
#endif
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