summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /drivers/scsi/scsi.c
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r--drivers/scsi/scsi.c2898
1 files changed, 1352 insertions, 1546 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 9f20c373c..4876ad297 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -12,9 +12,8 @@
* Rik Faith <faith@cs.unc.edu>
* Tommy Thorn <tthorn>
* Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
- * Andrea Arcangeli <arcangeli@mbox.queen.it>
*
- * Modified by Eric Youngdale eric@aib.com to
+ * Modified by Eric Youngdale eric@andante.jic.com or ericy@gnu.ai.mit.edu to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
*
@@ -30,12 +29,6 @@
* Leonard N. Zubkoff <lnz@dandelion.com>
*/
-/*
- * Don't import our own symbols, as this would severely mess up our
- * symbol tables.
- */
-#define _SCSI_SYMS_VER_
-
#include <linux/config.h>
#include <linux/module.h>
@@ -51,9 +44,14 @@
#include <linux/delay.h>
#include <linux/init.h>
+#define __KERNEL_SYSCALLS__
+
+#include <linux/unistd.h>
+
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/dma.h>
+#include <asm/spinlock.h>
#include "scsi.h"
#include "hosts.h"
@@ -69,10 +67,9 @@
static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $";
*/
-
-/* Command groups 3 and 4 are reserved and should never be used. */
-const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
-
+/*
+ * Definitions and constants.
+ */
#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
/*
@@ -90,27 +87,73 @@ const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
# error You lose.
#endif
-static void scsi_done (Scsi_Cmnd *SCpnt);
-int update_timeout (Scsi_Cmnd *, int);
-static void print_inquiry(unsigned char *data);
-static void scsi_times_out (Scsi_Cmnd * SCpnt);
-static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
- int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
- struct Scsi_Host *shpnt, char * scsi_result);
-void scsi_build_commandblocks(Scsi_Device * SDpnt);
+#define MAX_SCSI_DEVICE_CODE 10
+
+#ifdef DEBUG
+ #define SCSI_TIMEOUT (5*HZ)
+#else
+ #define SCSI_TIMEOUT (2*HZ)
+#endif
+
+#define MIN_RESET_DELAY (2*HZ)
+
+/* Do not call reset on error if we just did a reset within 15 sec. */
+#define MIN_RESET_PERIOD (15*HZ)
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another. Some will respond to all luns, others will
+ * lock up.
+ */
+
+#define BLIST_NOLUN 0x01
+#define BLIST_FORCELUN 0x02
+#define BLIST_BORKEN 0x04
+#define BLIST_KEY 0x08
+#define BLIST_SINGLELUN 0x10
+#define BLIST_NOTQ 0x20
+#define BLIST_SPARSELUN 0x40
+/*
+ * Data declarations.
+ */
+unsigned long scsi_pid = 0;
+Scsi_Cmnd * last_cmnd = NULL;
+/* Command groups 3 and 4 are reserved and should never be used. */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12,
+ 12, 12, 10, 10 };
+static unsigned long serial_number = 0;
+static Scsi_Cmnd * scsi_bh_queue_head = NULL;
+static Scsi_Cmnd * scsi_bh_queue_tail = NULL;
+static spinlock_t scsi_bh_queue_spin = SPIN_LOCK_UNLOCKED;
static FreeSectorBitmap * dma_malloc_freelist = NULL;
-static int scsi_need_isa_bounce_buffers;
-static unsigned int dma_sectors = 0;
-unsigned int dma_free_sectors = 0;
-unsigned int need_isa_buffer = 0;
-static unsigned char ** dma_malloc_pages = NULL;
-
-static int time_start;
-static int time_elapsed;
+static int need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int scsi_dma_free_sectors = 0;
+unsigned int scsi_need_isa_buffer = 0;
+static unsigned char ** dma_malloc_pages = NULL;
+
+/*
+ * Note - the initial logging level can be set here to log events at boot time.
+ * After the system is up, you may enable logging via the /proc interface.
+ */
+unsigned int scsi_logging_level = 0;
+
static volatile struct Scsi_Host * host_active = NULL;
-#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
- || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+#if CONFIG_PROC_FS
+/*
+ * This is the pointer to the /proc/scsi code.
+ * It is only initialized to !=0 if the scsi code is present
+ */
+struct proc_dir_entry proc_scsi_scsi = {
+ PROC_SCSI_SCSI, 4, "scsi",
+ S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0,
+ NULL,
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+#endif
+
const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
{
@@ -126,93 +169,40 @@ const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
"Communications "
};
-
-/*
- * global variables :
- * scsi_devices an array of these specifying the address for each
- * (host, id, LUN)
+/*
+ * Function prototypes.
*/
-
-Scsi_Device * scsi_devices = NULL;
-
-/* Process ID of SCSI commands */
-unsigned long scsi_pid = 0;
-
-static unsigned long serial_number = 0;
-
-static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
static void resize_dma_pool(void);
+static void print_inquiry(unsigned char *data);
+extern void scsi_times_out (Scsi_Cmnd * SCpnt);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+ int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char * scsi_result);
+void scsi_build_commandblocks(Scsi_Device * SDpnt);
-/* This variable is merely a hook so that we can debug the kernel with gdb. */
-Scsi_Cmnd * last_cmnd = NULL;
-
-/* This is the pointer to the /proc/scsi code.
- * It is only initialized to !=0 if the scsi code is present
+/*
+ * These are the interface to the old error handling code. It should go away
+ * someday soon.
*/
+extern void scsi_old_done (Scsi_Cmnd *SCpnt);
+extern void scsi_old_times_out (Scsi_Cmnd * SCpnt);
+
#if CONFIG_PROC_FS
extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start,
off_t offset, int length, int inout);
extern int dispatch_scsi_info(int ino, char *buffer, char **start,
off_t offset, int length, int inout);
-
-struct proc_dir_entry proc_scsi_scsi = {
- PROC_SCSI_SCSI, 4, "scsi",
- S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0,
- NULL,
- NULL, NULL,
- NULL, NULL, NULL
-};
#endif
-/*
- * This is the number of clock ticks we should wait before we time out
- * and abort the command. This is for where the scsi.c module generates
- * the command, not where it originates from a higher level, in which
- * case the timeout is specified there.
- *
- * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
- * respectively.
- */
+#define SCSI_BLOCK(DEVICE, HOST) \
+ ((HOST->block && host_active && HOST != host_active) \
+ || ((HOST)->can_queue && HOST->host_busy >= HOST->can_queue) \
+ || ((HOST)->host_blocked) \
+ || ((DEVICE) != NULL && (DEVICE)->device_blocked) )
-#ifdef DEBUG_TIMEOUT
-static void scsi_dump_status(void);
-#endif
+static void scsi_dump_status(int level);
-#ifdef DEBUG
- #define SCSI_TIMEOUT (5*HZ)
-#else
- #define SCSI_TIMEOUT (2*HZ)
-#endif
-
-#ifdef DEBUG
- #define SENSE_TIMEOUT SCSI_TIMEOUT
- #define ABORT_TIMEOUT SCSI_TIMEOUT
- #define RESET_TIMEOUT SCSI_TIMEOUT
-#else
- #define SENSE_TIMEOUT (5*HZ/10)
- #define RESET_TIMEOUT (5*HZ/10)
- #define ABORT_TIMEOUT (5*HZ/10)
-#endif
-
-#define MIN_RESET_DELAY (2*HZ)
-
-/* Do not call reset on error if we just did a reset within 15 sec. */
-#define MIN_RESET_PERIOD (15*HZ)
-
-/* The following devices are known not to tolerate a lun != 0 scan for
- * one reason or another. Some will respond to all luns, others will
- * lock up.
- */
-
-#define BLIST_NOLUN 0x01
-#define BLIST_FORCELUN 0x02
-#define BLIST_BORKEN 0x04
-#define BLIST_KEY 0x08
-#define BLIST_SINGLELUN 0x10
-#define BLIST_NOTQ 0x20
-#define BLIST_SPARSELUN 0x40
-
struct dev_info{
const char * vendor;
const char * model;
@@ -227,6 +217,7 @@ struct dev_info{
*/
static struct dev_info device_list[] =
{
+{"Aashima","IMAGERY 2400SP","1.03",BLIST_NOLUN},/* Locks up if polled for lun != 0 */
{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
@@ -284,6 +275,7 @@ static struct dev_info device_list[] =
{"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
{"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN},
{"CANON","IPUBJD","*", BLIST_SPARSELUN},
+{"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
{"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
@@ -311,7 +303,31 @@ static int get_device_flags(unsigned char * response_data){
return 0;
}
-void scsi_make_blocked_list(void) {
+/*
+ * Function: scsi_make_blocked_list
+ *
+ * Purpose: Build linked list of hosts that require blocking.
+ *
+ * Arguments: None.
+ *
+ * Returns: Nothing
+ *
+ * Notes: Blocking is sort of a hack that is used to prevent more than one
+ * host adapter from being active at one time. This is used in cases
+ * where the ISA bus becomes unreliable if you have more than one
+ * host adapter really pumping data through.
+ *
+ * We spent a lot of time examining the problem, and I *believe* that
+ * the problem is bus related as opposed to being a driver bug.
+ *
+ * The blocked list is used as part of the synchronization object
+ * that we use to ensure that only one host is active at one time.
+ * I (ERY) would like to make this go away someday, but this would
+ * require that we have a recursive mutex object.
+ */
+void
+scsi_make_blocked_list(void)
+{
int block_count = 0, index;
unsigned long flags;
struct Scsi_Host * sh[128], * shpnt;
@@ -373,22 +389,31 @@ void scsi_make_blocked_list(void) {
static void scan_scsis_done (Scsi_Cmnd * SCpnt)
{
-#ifdef DEBUG
- printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
-#endif
+ SCSI_LOG_MLCOMPLETE(1,printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result));
SCpnt->request.rq_status = RQ_SCSI_DONE;
if (SCpnt->request.sem != NULL)
up(SCpnt->request.sem);
}
+void scsi_logging_setup(char *str, int *ints)
+{
+ if (ints[0] != 1) {
+ printk("scsi_logging_setup : usage scsi_logging_level=n "
+ "(n should be 0 or non-zero)\n");
+ } else {
+ scsi_logging_level = (ints[1])? ~0 : 0;
+ }
+}
+
#ifdef CONFIG_SCSI_MULTI_LUN
static int max_scsi_luns = 8;
#else
static int max_scsi_luns = 1;
#endif
-void scsi_luns_setup(char *str, int *ints) {
+void scsi_luns_setup(char *str, int *ints)
+{
if (ints[0] != 1)
printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
else
@@ -402,20 +427,29 @@ void scsi_luns_setup(char *str, int *ints) {
* lun address of all sequential devices to the tape driver, all random
* devices to the disk driver.
*/
-static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
- unchar hchannel, unchar hid, unchar hlun)
+static void scan_scsis (struct Scsi_Host *shpnt,
+ unchar hardcoded,
+ unchar hchannel,
+ unchar hid,
+ unchar hlun)
{
- int dev, lun, channel;
- unsigned char scsi_result0[256];
- unsigned char *scsi_result;
- Scsi_Device *SDpnt;
- int max_dev_lun, sparse_lun;
- Scsi_Cmnd *SCpnt;
+ int channel;
+ int dev;
+ int lun;
+ int max_dev_lun;
+ Scsi_Cmnd * SCpnt;
+ unsigned char * scsi_result;
+ unsigned char scsi_result0[256];
+ Scsi_Device * SDpnt;
+ Scsi_Device * SDtail;
+ int sparse_lun;
SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
- SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ memset (SDpnt, 0, sizeof (Scsi_Device));
+
/* Make sure we have something that is valid for DMA purposes */
#ifndef CONFIG_SGI
@@ -425,18 +459,44 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
scsi_result = (scsi_init_malloc (512, GFP_DMA));
#endif
- if (scsi_result == NULL) {
- printk ("Unable to obtain scsi_result buffer\n");
- goto leave;
+ if (scsi_result == NULL)
+ {
+ printk ("Unable to obtain scsi_result buffer\n");
+ goto leave;
}
- /* We must chain ourself in the host_queue, so commands can time out */
- if(shpnt->host_queue)
- shpnt->host_queue->prev = SCpnt;
- SCpnt->next = shpnt->host_queue;
- SCpnt->prev = NULL;
- shpnt->host_queue = SCpnt;
+ /*
+ * We must chain ourself in the host_queue, so commands can time out
+ */
+ SCpnt->next = NULL;
+ SDpnt->device_queue = SCpnt;
+ SDpnt->host = shpnt;
+ SDpnt->online = TRUE;
+ /*
+ * Next, hook the device to the host in question.
+ */
+ SDpnt->prev = NULL;
+ SDpnt->next = NULL;
+ if( shpnt->host_queue != NULL )
+ {
+ SDtail = shpnt->host_queue;
+ while( SDtail->next != NULL )
+ SDtail = SDtail->next;
+
+ SDtail->next = SDpnt;
+ SDpnt->prev = SDtail;
+ }
+ else
+ {
+ shpnt->host_queue = SDpnt;
+ }
+
+ /*
+ * We need to increment the counter for this one device so we can track when
+ * things are quiet.
+ */
+ atomic_inc(&shpnt->host_active);
if (hardcoded == 1) {
Scsi_Device *oldSDpnt=SDpnt;
@@ -494,20 +554,30 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
} /* for channel ends */
} /* if/else hardcoded */
+ /*
+ * We need to decrement the counter for this one device
+ * so we know when everything is quiet.
+ */
+ atomic_dec(&shpnt->host_active);
+
leave:
{/* Unchain SCpnt from host_queue */
- Scsi_Cmnd *prev, *next, *hqptr;
- for(hqptr = shpnt->host_queue; hqptr != SCpnt; hqptr = hqptr->next) ;
- if(hqptr) {
- prev = hqptr->prev;
- next = hqptr->next;
- if(prev)
- prev->next = next;
- else
- shpnt->host_queue = next;
- if(next) next->prev = prev;
- }
+ Scsi_Device *prev, *next;
+ Scsi_Device * dqptr;
+
+ for(dqptr = shpnt->host_queue; dqptr != SDpnt; dqptr = dqptr->next)
+ continue;
+ if(dqptr)
+ {
+ prev = dqptr->prev;
+ next = dqptr->next;
+ if(prev)
+ prev->next = next;
+ else
+ shpnt->host_queue = next;
+ if(next) next->prev = prev;
+ }
}
/* Last device block does not exist. Free memory. */
@@ -519,8 +589,25 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
/* If we allocated a buffer so we could do DMA, free it now */
if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
- scsi_init_free (scsi_result, 512);
+ {
+ scsi_init_free (scsi_result, 512);
+ }
+ {
+ Scsi_Device * sdev;
+ Scsi_Cmnd * scmd;
+
+ SCSI_LOG_SCAN_BUS(4,printk("Host status for host %p:\n", shpnt));
+ for(sdev = shpnt->host_queue; sdev; sdev = sdev->next)
+ {
+ SCSI_LOG_SCAN_BUS(4,printk("Device %d %p: ", sdev->id, sdev));
+ for(scmd=sdev->device_queue; scmd; scmd = scmd->next)
+ {
+ SCSI_LOG_SCAN_BUS(4,printk("%p ", scmd));
+ }
+ SCSI_LOG_SCAN_BUS(4,printk("\n"));
+ }
+ }
}
/*
@@ -537,16 +624,11 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
Scsi_Device * SDtail, *SDpnt=*SDpnt2;
int bflags, type=-1;
- SDtail = scsi_devices;
- if (scsi_devices)
- while (SDtail->next)
- SDtail = SDtail->next;
-
- memset (SDpnt, 0, sizeof (Scsi_Device));
SDpnt->host = shpnt;
SDpnt->id = dev;
SDpnt->lun = lun;
SDpnt->channel = channel;
+ SDpnt->online = TRUE;
/* Some low level driver could use device->type (DB) */
SDpnt->type = -1;
@@ -576,14 +658,14 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
(void *) scsi_result,
256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
down (&sem);
+ SCpnt->request.sem = NULL;
}
-#if defined(DEBUG) || defined(DEBUG_INIT)
- printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
- dev, lun, SCpnt->result);
- print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result);
- printk("\n");
-#endif
+ SCSI_LOG_SCAN_BUS(3, printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
+ dev, lun, SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3,print_driverbyte(SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3,print_hostbyte(SCpnt->result));
+ SCSI_LOG_SCAN_BUS(3,printk("\n"));
if (SCpnt->result) {
if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
@@ -598,9 +680,7 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
return 0;
}
-#if defined (DEBUG) || defined(DEBUG_INIT)
- printk ("scsi: performing INQUIRY\n");
-#endif
+ SCSI_LOG_SCAN_BUS(3,printk ("scsi: performing INQUIRY\n"));
/*
* Build an INQUIRY command block.
*/
@@ -619,12 +699,11 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
(void *) scsi_result,
256, scan_scsis_done, SCSI_TIMEOUT, 3);
down (&sem);
+ SCpnt->request.sem = NULL;
}
-#if defined(DEBUG) || defined(DEBUG_INIT)
- printk ("scsi: INQUIRY %s with code 0x%x\n",
- SCpnt->result ? "failed" : "successful", SCpnt->result);
-#endif
+ SCSI_LOG_SCAN_BUS(3,printk ("scsi: INQUIRY %s with code 0x%x\n",
+ SCpnt->result ? "failed" : "successful", SCpnt->result));
if (SCpnt->result)
return 0; /* assume no peripheral if any sort of error */
@@ -653,6 +732,7 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
memcpy (SDpnt->rev, scsi_result + 32, 4);
SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+ SDpnt->online = TRUE;
SDpnt->lockable = SDpnt->removable;
SDpnt->changed = 0;
SDpnt->access_count = 0;
@@ -679,6 +759,8 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
printk ("scsi: unknown type %d\n", type);
}
+ SDpnt->device_blocked = FALSE;
+ SDpnt->device_busy = 0;
SDpnt->single_lun = 0;
SDpnt->soft_reset =
(scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
@@ -732,6 +814,13 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
SDpnt->borken = 0;
/*
+ * If we want to only allow I/O to one of the luns attached to this device
+ * at a time, then we set this flag.
+ */
+ if (bflags & BLIST_SINGLELUN)
+ SDpnt->single_lun = 1;
+
+ /*
* These devices need this "key" to unlock the devices so we can use it
*/
if ((bflags & BLIST_KEY) != 0) {
@@ -752,33 +841,61 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
(void *) scsi_result, 0x2a,
scan_scsis_done, SCSI_TIMEOUT, 3);
down (&sem);
+ SCpnt->request.sem = NULL;
}
}
- /* Add this device to the linked list at the end */
- if (SDtail)
- SDtail->next = SDpnt;
- else
- scsi_devices = SDpnt;
- SDtail = SDpnt;
+ /*
+ * Detach the command from the device. It was just a temporary to be used while
+ * scanning the bus - the real ones will be allocated later.
+ */
+ SDpnt->device_queue = NULL;
+
+ /*
+ * This device was already hooked up to the host in question,
+ * so at this point we just let go of it and it should be fine. We do need to
+ * allocate a new one and attach it to the host so that we can further scan the bus.
+ */
SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
*SDpnt2=SDpnt;
if (!SDpnt)
- printk ("scsi: scan_scsis_single: Cannot malloc\n");
+ {
+ printk ("scsi: scan_scsis_single: Cannot malloc\n");
+ return 0;
+ }
+ memset (SDpnt, 0, sizeof (Scsi_Device));
/*
- * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ * And hook up our command block to the new device we will be testing
+ * for.
*/
- if (bflags & BLIST_NOLUN)
- return 0; /* break; */
+ SDpnt->device_queue = SCpnt;
+ SDpnt->online = TRUE;
/*
- * If we want to only allow I/O to one of the luns attached to this device
- * at a time, then we set this flag.
+ * Since we just found one device, there had damn well better be one in the list
+ * already.
*/
- if (bflags & BLIST_SINGLELUN)
- SDpnt->single_lun = 1;
+ if( shpnt->host_queue == NULL )
+ panic("scan_scsis_single: Host queue == NULL\n");
+
+ SDtail = shpnt->host_queue;
+ while (SDtail->next)
+ {
+ SDtail = SDtail->next;
+ }
+
+ /* Add this device to the linked list at the end */
+ SDtail->next = SDpnt;
+ SDpnt->prev = SDtail;
+ SDpnt->next = NULL;
+
+ /*
+ * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ */
+ if (bflags & BLIST_NOLUN)
+ return 0; /* break; */
/*
* If this device is known to support sparse multiple units, override the
@@ -820,68 +937,6 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
#define IN_RESET2 4
#define IN_RESET3 8
-/*
- * This is our time out function, called when the timer expires for a
- * given host adapter. It will attempt to abort the currently executing
- * command, that failing perform a kernel panic.
- */
-
-static void scsi_times_out (Scsi_Cmnd * SCpnt)
-{
-
- switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3))
- {
- case NORMAL_TIMEOUT:
- {
-#ifdef DEBUG_TIMEOUT
- scsi_dump_status();
-#endif
- }
-
- if (!scsi_abort (SCpnt, DID_TIME_OUT))
- return;
- case IN_ABORT:
- printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
- SCpnt->host->host_no, SCpnt->pid);
- if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS))
- return;
- case IN_RESET:
- case (IN_ABORT | IN_RESET):
- /* This might be controversial, but if there is a bus hang,
- * you might conceivably want the machine up and running
- * esp if you have an ide disk.
- */
- printk("SCSI host %d channel %d reset (pid %ld) timed out - "
- "trying harder\n",
- SCpnt->host->host_no, SCpnt->channel, SCpnt->pid);
- SCpnt->internal_timeout &= ~IN_RESET;
- SCpnt->internal_timeout |= IN_RESET2;
- scsi_reset (SCpnt,
- SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
- return;
- case (IN_ABORT | IN_RESET | IN_RESET2):
- /* Obviously the bus reset didn't work.
- * Let's try even harder and call for an HBA reset.
- * Maybe the HBA itself crashed and this will shake it loose.
- */
- printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n",
- SCpnt->host->host_no, SCpnt->pid);
- SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2);
- SCpnt->internal_timeout |= IN_RESET3;
- scsi_reset (SCpnt,
- SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET);
- return;
-
- default:
- printk("SCSI host %d reset (pid %ld) timed out again -\n",
- SCpnt->host->host_no, SCpnt->pid);
- printk("probably an unrecoverable SCSI bus or device hang.\n");
- return;
-
- }
-
-}
-
/* This function takes a quick look at a request, and decides if it
* can be queued now, or if there would be a stall while waiting for
@@ -890,7 +945,7 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
* of the calling code to ensure that this is the case.
*/
-Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
+Scsi_Cmnd * scsi_request_queueable (struct request * req, Scsi_Device * device)
{
Scsi_Cmnd * SCpnt = NULL;
int tablesize;
@@ -898,10 +953,10 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
struct buffer_head * bh, *bhp;
if (!device)
- panic ("No device passed to request_queueable().\n");
+ panic ("No device passed to scsi_request_queueable().\n");
if (req && req->rq_status == RQ_INACTIVE)
- panic("Inactive in request_queueable");
+ panic("Inactive in scsi_request_queueable");
/*
* Look for a free command block. If we have been instructed not to queue
@@ -913,10 +968,10 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
SCpnt = device->device_queue;
while(SCpnt){
if(SCpnt->request.rq_status == RQ_INACTIVE) break;
- SCpnt = SCpnt->device_next;
+ SCpnt = SCpnt->next;
}
} else {
- SCpnt = device->host->host_queue;
+ SCpnt = device->device_queue;
while(SCpnt){
if(SCpnt->channel == device->channel
&& SCpnt->target == device->id) {
@@ -943,7 +998,7 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
if (!SCpnt) return NULL;
- if (SCSI_BLOCK(device->host)) return NULL;
+ if (SCSI_BLOCK(device, device->host)) return NULL;
if (req) {
memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -983,6 +1038,9 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
* either */
}
+ atomic_inc(&SCpnt->host->host_active);
+ SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n", SCpnt->target,
+ atomic_read(&SCpnt->host->host_active)));
SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
SCpnt->old_use_sg = 0;
SCpnt->transfersize = 0;
@@ -995,6 +1053,8 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
SCpnt->channel = device->channel;
SCpnt->lun = device->lun;
SCpnt->target = device->id;
+ SCpnt->state = SCSI_STATE_INITIALIZING;
+ SCpnt->owner = SCSI_OWNER_HIGHLEVEL;
return SCpnt;
}
@@ -1005,11 +1065,11 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
* not to return a descriptor if the host is unable to accept any more
* commands for the time being. We need to keep in mind that there is no
* guarantee that the host remain not busy. Keep in mind the
- * request_queueable function also knows the internal allocation scheme
+ * scsi_request_queueable function also knows the internal allocation scheme
* of the packets for each device
*/
-Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
+Scsi_Cmnd * scsi_allocate_device (struct request ** reqp, Scsi_Device * device,
int wait)
{
kdev_t dev;
@@ -1023,7 +1083,7 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
Scsi_Cmnd * found = NULL;
if (!device)
- panic ("No device passed to allocate_device().\n");
+ panic ("No device passed to scsi_allocate_device().\n");
if (reqp) req = *reqp;
@@ -1036,7 +1096,7 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
host = device->host;
- if (in_interrupt() && SCSI_BLOCK(host)) return NULL;
+ if (in_interrupt() && SCSI_BLOCK(device, host)) return NULL;
while (1==1){
if (!device->single_lun) {
@@ -1044,10 +1104,10 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
while(SCpnt){
SCwait = SCpnt;
if(SCpnt->request.rq_status == RQ_INACTIVE) break;
- SCpnt = SCpnt->device_next;
+ SCpnt = SCpnt->next;
}
} else {
- SCpnt = device->host->host_queue;
+ SCpnt = device->device_queue;
while(SCpnt){
if(SCpnt->channel == device->channel
&& SCpnt->target == device->id) {
@@ -1084,7 +1144,6 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
}
if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */
{
-#if 1 /* NEW CODE */
if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){
sleep_on(&device->device_wait);
restore_flags(flags);
@@ -1095,21 +1154,9 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
printk("Attempt to allocate device channel %d,"
" target %d, lun %d\n", device->channel,
device->id, device->lun);
- panic("No device found in allocate_device\n");
+ panic("No device found in scsi_allocate_device\n");
}
}
-#else /* ORIGINAL CODE */
- restore_flags(flags);
- if(!wait) return NULL;
- if (!SCwait) {
- printk("Attempt to allocate device channel %d, target"
- " %d, lun %d\n", device->channel, device->id,
- device->lun);
- panic("No device found in allocate_device\n");
- }
- SCSI_SLEEP(&device->device_wait,
- (SCwait->request.rq_status != RQ_INACTIVE));
-#endif
} else {
if (req) {
memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -1150,7 +1197,11 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
SCpnt->request.sem = NULL; /* And no one is waiting for this
* to complete */
}
+ atomic_inc(&SCpnt->host->host_active);
restore_flags(flags);
+ SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n",
+ SCpnt->target,
+ atomic_read(&SCpnt->host->host_active)));
break;
}
}
@@ -1164,25 +1215,74 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
/* Since not everyone seems to set the device info correctly
* before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ * FIXME(eric) This doesn't make any sense.
*/
SCpnt->channel = device->channel;
SCpnt->lun = device->lun;
SCpnt->target = device->id;
+ SCpnt->state = SCSI_STATE_INITIALIZING;
+ SCpnt->owner = SCSI_OWNER_HIGHLEVEL;
return SCpnt;
}
/*
+ * Function: scsi_release_command
+ *
+ * Purpose: Release a command block.
+ *
+ * Arguments: SCpnt - command block we are releasing.
+ *
+ * Notes: The command block can no longer be used by the caller once
+ * this funciton is called. This is in effect the inverse
+ * of scsi_allocate_device/scsi_request_queueable.
+ */
+void
+scsi_release_command(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ SCpnt->state = SCSI_STATE_UNUSED;
+ SCpnt->owner = SCSI_OWNER_NOBODY;
+ atomic_dec(&SCpnt->host->host_active);
+
+ SCSI_LOG_MLQUEUE(5, printk("Deactivating command for device %d (active=%d, failed=%d)\n",
+ SCpnt->target,
+ atomic_read(&SCpnt->host->host_active),
+ SCpnt->host->host_failed));
+ if( SCpnt->host->host_failed != 0 )
+ {
+ SCSI_LOG_ERROR_RECOVERY(5, printk("Error handler thread %d %d\n",
+ SCpnt->host->in_recovery,
+ SCpnt->host->eh_active));
+ }
+
+ /*
+ * If the host is having troubles, then look to see if this was the last
+ * command that might have failed. If so, wake up the error handler.
+ */
+ if( SCpnt->host->in_recovery
+ && !SCpnt->host->eh_active
+ && atomic_read(&SCpnt->host->host_active) == SCpnt->host->host_failed )
+ {
+ SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
+ atomic_read(&SCpnt->host->eh_wait->count)));
+ up(SCpnt->host->eh_wait);
+ }
+}
+
+/*
* This is inline because we have stack problemes if we recurse to deeply.
*/
-inline void internal_cmnd (Scsi_Cmnd * SCpnt)
+inline int internal_cmnd (Scsi_Cmnd * SCpnt)
{
- unsigned long flags, timeout;
- struct Scsi_Host * host;
#ifdef DEBUG_DELAY
unsigned long clock;
#endif
+ unsigned long flags;
+ struct Scsi_Host * host;
+ int rtn = 0;
+ unsigned long timeout;
#if DEBUG
unsigned long *ret = 0;
@@ -1223,25 +1323,31 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
}
restore_flags(flags);
- update_timeout(SCpnt, SCpnt->timeout_per_command);
+ if( host->hostt->use_new_eh_code )
+ {
+ scsi_add_timer(SCpnt, SCpnt->timeout_per_command, scsi_times_out);
+ }
+ else
+ {
+ scsi_add_timer(SCpnt, SCpnt->timeout_per_command,
+ scsi_old_times_out);
+ }
/*
* We will use a queued command if possible, otherwise we will emulate the
* queuing and calling of completion function ourselves.
*/
-#ifdef DEBUG
- printk("internal_cmnd (host = %d, channel = %d, target = %d, "
+ SCSI_LOG_MLQUEUE(3,printk("internal_cmnd (host = %d, channel = %d, target = %d, "
"command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
- SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
-#endif
+ SCpnt->buffer, SCpnt->bufflen, SCpnt->done));
+ SCpnt->state = SCSI_STATE_QUEUED;
+ SCpnt->owner = SCSI_OWNER_LOWLEVEL;
if (host->can_queue)
{
-#ifdef DEBUG
- printk("queuecommand : routine at %p\n",
- host->hostt->queuecommand);
-#endif
+ SCSI_LOG_MLQUEUE(3,printk("queuecommand : routine at %p\n",
+ host->hostt->queuecommand));
/* This locking tries to prevent all sorts of races between
* queuecommand and the interrupt code. In effect,
* we are only allowed to be in queuecommand once at
@@ -1254,7 +1360,23 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
if(!in_interrupt() && SCpnt->host->irq)
disable_irq(SCpnt->host->irq);
- host->hostt->queuecommand (SCpnt, scsi_done);
+ /*
+ * Use the old error handling code if we haven't converted the driver
+ * to use the new one yet. Note - only the new queuecommand variant
+ * passes a meaningful return value.
+ */
+ if( host->hostt->use_new_eh_code )
+ {
+ rtn = host->hostt->queuecommand (SCpnt, scsi_done);
+ if( rtn != 0 )
+ {
+ scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_HOST_BUSY);
+ }
+ }
+ else
+ {
+ host->hostt->queuecommand (SCpnt, scsi_old_done);
+ }
if(!in_interrupt() && SCpnt->host->irq)
enable_irq(SCpnt->host->irq);
@@ -1263,9 +1385,7 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
{
int temp;
-#ifdef DEBUG
- printk("command() : routine at %p\n", host->hostt->command);
-#endif
+ SCSI_LOG_MLQUEUE(3,printk("command() : routine at %p\n", host->hostt->command));
temp = host->hostt->command (SCpnt);
SCpnt->result = temp;
#ifdef DEBUG_DELAY
@@ -1274,39 +1394,19 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
printk("done(host = %d, result = %04x) : routine at %p\n",
host->host_no, temp, host->hostt->command);
#endif
- scsi_done(SCpnt);
+ if( host->hostt->use_new_eh_code )
+ {
+ scsi_done(SCpnt);
+ }
+ else
+ {
+ scsi_old_done(SCpnt);
+ }
}
-#ifdef DEBUG
- printk("leaving internal_cmnd()\n");
-#endif
+ SCSI_LOG_MLQUEUE(3,printk("leaving internal_cmnd()\n"));
+ return rtn;
}
-static void scsi_request_sense (Scsi_Cmnd * SCpnt)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
- update_timeout(SCpnt, SENSE_TIMEOUT);
- restore_flags(flags);
-
-
- memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
- sizeof(generic_sense));
-
- SCpnt->cmnd[1] = SCpnt->lun << 5;
- SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
-
- SCpnt->request_buffer = &SCpnt->sense_buffer;
- SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
- SCpnt->use_sg = 0;
- SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
- internal_cmnd (SCpnt);
-}
-
-
-
/*
* scsi_do_cmd sends all the commands out to the low-level driver. It
* handles the specifics required for each low level driver - ie queued
@@ -1320,8 +1420,11 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
{
unsigned long flags;
struct Scsi_Host * host = SCpnt->host;
+ Scsi_Device * device = SCpnt->device;
-#ifdef DEBUG
+ SCpnt->owner = SCSI_OWNER_MIDLEVEL;
+
+SCSI_LOG_MLQUEUE(4,
{
int i;
int target = SCpnt->target;
@@ -1333,8 +1436,7 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
for (i = 0; i < 10; ++i)
printk ("%02x ", ((unsigned char *) cmnd)[i]);
printk("\n");
- }
-#endif
+ });
if (!host)
{
@@ -1354,15 +1456,16 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
cli();
SCpnt->pid = scsi_pid++;
- while (SCSI_BLOCK(host)) {
+ while (SCSI_BLOCK((Scsi_Device *) NULL, host)) {
restore_flags(flags);
- SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+ SCSI_SLEEP(&host->host_wait, SCSI_BLOCK((Scsi_Device *) NULL, host));
cli();
}
if (host->block) host_active = host;
host->host_busy++;
+ device->device_busy++;
restore_flags(flags);
/*
@@ -1373,12 +1476,6 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
*/
memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);
-#if 0
- SCpnt->host = host;
- SCpnt->channel = channel;
- SCpnt->target = target;
- SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
-#endif
SCpnt->reset_chain = NULL;
SCpnt->serial_number = 0;
SCpnt->bufflen = bufflen;
@@ -1407,967 +1504,307 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
SCpnt->abort_reason = 0;
internal_cmnd (SCpnt);
-#ifdef DEBUG
- printk ("Leaving scsi_do_cmd()\n");
-#endif
-}
-
-static int check_sense (Scsi_Cmnd * SCpnt)
-{
- /* If there is no sense information, request it. If we have already
- * requested it, there is no point in asking again - the firmware must
- * be confused.
- */
- if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
- if(!(SCpnt->flags & ASKED_FOR_SENSE))
- return SUGGEST_SENSE;
- else
- return SUGGEST_RETRY;
- }
-
- SCpnt->flags &= ~ASKED_FOR_SENSE;
-
-#ifdef DEBUG_INIT
- printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel);
- print_sense("", SCpnt);
- printk("\n");
-#endif
- if (SCpnt->sense_buffer[2] & 0xe0)
- return SUGGEST_ABORT;
-
- switch (SCpnt->sense_buffer[2] & 0xf)
- {
- case NO_SENSE:
- return 0;
- case RECOVERED_ERROR:
- return SUGGEST_IS_OK;
-
- case ABORTED_COMMAND:
- return SUGGEST_RETRY;
- case NOT_READY:
- case UNIT_ATTENTION:
- /*
- * If we are expecting a CC/UA because of a bus reset that we
- * performed, treat this just as a retry. Otherwise this is
- * information that we should pass up to the upper-level driver
- * so that we can deal with it there.
- */
- if( SCpnt->device->expecting_cc_ua )
- {
- SCpnt->device->expecting_cc_ua = 0;
- return SUGGEST_RETRY;
- }
- return SUGGEST_ABORT;
-
- /* these three are not supported */
- case COPY_ABORTED:
- case VOLUME_OVERFLOW:
- case MISCOMPARE:
-
- case MEDIUM_ERROR:
- return SUGGEST_REMAP;
- case BLANK_CHECK:
- case DATA_PROTECT:
- case HARDWARE_ERROR:
- case ILLEGAL_REQUEST:
- default:
- return SUGGEST_ABORT;
- }
+ SCSI_LOG_MLQUEUE(3,printk ("Leaving scsi_do_cmd()\n"));
}
/* This function is the mid-level interrupt routine, which decides how
* to handle error conditions. Each invocation of this function must
* do one and *only* one of the following:
*
- * (1) Call last_cmnd[host].done. This is done for fatal errors and
- * normal completion, and indicates that the handling for this
- * request is complete.
- * (2) Call internal_cmnd to requeue the command. This will result in
- * scsi_done being called again when the retry is complete.
- * (3) Call scsi_request_sense. This asks the host adapter/drive for
- * more information about the error condition. When the information
- * is available, scsi_done will be called again.
- * (4) Call reset(). This is sort of a last resort, and the idea is that
- * this may kick things loose and get the drive working again. reset()
- * automatically calls scsi_request_sense, and thus scsi_done will be
- * called again once the reset is complete.
+ * 1) Insert command in BH queue.
+ * 2) Activate error handler for host.
*
- * If none of the above actions are taken, the drive in question
- * will hang. If more than one of the above actions are taken by
- * scsi_done, then unpredictable behavior will result.
+ * FIXME(eric) - I am concerned about stack overflow (still). An interrupt could
+ * come while we are processing the bottom queue, which would cause another command
+ * to be stuffed onto the bottom queue, and it would in turn be processed as that
+ * interrupt handler is returning. Given a sufficiently steady rate of returning
+ * commands, this could cause the stack to overflow. I am not sure what is the most
+ * appropriate solution here - we should probably keep a depth count, and not process
+ * any commands while we still have a bottom handler active higher in the stack.
+ *
+ * There is currently code in the bottom half handler to monitor recursion in the bottom
+ * handler and report if it ever happens. If this becomes a problem, it won't be hard to
+ * engineer something to deal with it so that only the outer layer ever does any real
+ * processing.
*/
-static void scsi_done (Scsi_Cmnd * SCpnt)
+void
+scsi_done (Scsi_Cmnd * SCpnt)
{
- int status=0;
- int exit=0;
- int checked;
- int oldto;
- struct Scsi_Host * host = SCpnt->host;
- int result = SCpnt->result;
- SCpnt->serial_number = 0;
- oldto = update_timeout(SCpnt, 0);
-
-#ifdef DEBUG_TIMEOUT
- if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
- result, SCpnt->target, SCpnt->lun);
-#endif
-
- /* If we requested an abort, (and we got it) then fix up the return
- * status to say why
- */
- if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
- SCpnt->result = result = (result & 0xff00ffff) |
- (SCpnt->abort_reason << 16);
-
-
-#define FINISHED 0
-#define MAYREDO 1
-#define REDO 3
-#define PENDING 4
-
-#ifdef DEBUG
- printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
-#endif
-
- if(SCpnt->flags & WAS_SENSE)
- {
- SCpnt->use_sg = SCpnt->old_use_sg;
- SCpnt->cmd_len = SCpnt->old_cmd_len;
- }
-
- switch (host_byte(result))
- {
- case DID_OK:
- if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
- /* Failed to obtain sense information */
- {
- SCpnt->flags &= ~WAS_SENSE;
-#if 0 /* This cannot possibly be correct. */
- SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
-#endif
-
- if (!(SCpnt->flags & WAS_RESET))
- {
- printk("scsi%d : channel %d target %d lun %d request sense"
- " failed, performing reset.\n",
- SCpnt->host->host_no, SCpnt->channel, SCpnt->target,
- SCpnt->lun);
- scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
- return;
- }
- else
- {
- exit = (DRIVER_HARD | SUGGEST_ABORT);
- status = FINISHED;
- }
- }
- else switch(msg_byte(result))
- {
- case COMMAND_COMPLETE:
- switch (status_byte(result))
- {
- case GOOD:
- if (SCpnt->flags & WAS_SENSE)
- {
-#ifdef DEBUG
- printk ("In scsi_done, GOOD status, COMMAND COMPLETE, "
- "parsing sense information.\n");
-#endif
- SCpnt->flags &= ~WAS_SENSE;
-#if 0 /* This cannot possibly be correct. */
- SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
-#endif
+ unsigned long flags;
- switch (checked = check_sense(SCpnt))
- {
- case SUGGEST_SENSE:
- case 0:
-#ifdef DEBUG
- printk("NO SENSE. status = REDO\n");
-#endif
- update_timeout(SCpnt, oldto);
- status = REDO;
- break;
- case SUGGEST_IS_OK:
- break;
- case SUGGEST_REMAP:
-#ifdef DEBUG
- printk("SENSE SUGGEST REMAP - status = FINISHED\n");
-#endif
- status = FINISHED;
- exit = DRIVER_SENSE | SUGGEST_ABORT;
- break;
- case SUGGEST_RETRY:
-#ifdef DEBUG
- printk("SENSE SUGGEST RETRY - status = MAYREDO\n");
-#endif
- status = MAYREDO;
- exit = DRIVER_SENSE | SUGGEST_RETRY;
- break;
- case SUGGEST_ABORT:
-#ifdef DEBUG
- printk("SENSE SUGGEST ABORT - status = FINISHED");
-#endif
- status = FINISHED;
- exit = DRIVER_SENSE | SUGGEST_ABORT;
- break;
- default:
- printk ("Internal error %s %d \n", __FILE__,
- __LINE__);
- }
- } /* end WAS_SENSE */
- else
- {
-#ifdef DEBUG
- printk("COMMAND COMPLETE message returned, "
- "status = FINISHED. \n");
-#endif
- exit = DRIVER_OK;
- status = FINISHED;
- }
- break;
-
- case CHECK_CONDITION:
- case COMMAND_TERMINATED:
- switch (check_sense(SCpnt))
- {
- case 0:
- update_timeout(SCpnt, oldto);
- status = REDO;
- break;
- case SUGGEST_REMAP:
- status = FINISHED;
- exit = DRIVER_SENSE | SUGGEST_ABORT;
- break;
- case SUGGEST_RETRY:
- status = MAYREDO;
- exit = DRIVER_SENSE | SUGGEST_RETRY;
- break;
- case SUGGEST_ABORT:
- status = FINISHED;
- exit = DRIVER_SENSE | SUGGEST_ABORT;
- break;
- case SUGGEST_SENSE:
- scsi_request_sense (SCpnt);
- status = PENDING;
- break;
- }
- break;
-
- case CONDITION_GOOD:
- case INTERMEDIATE_GOOD:
- case INTERMEDIATE_C_GOOD:
- break;
-
- case BUSY:
- case QUEUE_FULL:
- update_timeout(SCpnt, oldto);
- status = REDO;
- break;
-
- case RESERVATION_CONFLICT:
- printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
- " reset.\n", SCpnt->host->host_no, SCpnt->channel);
- scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
- return;
-#if 0
- exit = DRIVER_SOFT | SUGGEST_ABORT;
- status = MAYREDO;
- break;
-#endif
- default:
- printk ("Internal error %s %d \n"
- "status byte = %d \n", __FILE__,
- __LINE__, status_byte(result));
-
- }
- break;
- default:
- panic("scsi: unsupported message byte %d received\n",
- msg_byte(result));
- }
- break;
- case DID_TIME_OUT:
-#ifdef DEBUG
- printk("Host returned DID_TIME_OUT - ");
-#endif
-
- if (SCpnt->flags & WAS_TIMEDOUT)
- {
-#ifdef DEBUG
- printk("Aborting\n");
-#endif
- /*
- Allow TEST_UNIT_READY and INQUIRY commands to timeout early
- without causing resets. All other commands should be retried.
- */
- if (SCpnt->cmnd[0] != TEST_UNIT_READY &&
- SCpnt->cmnd[0] != INQUIRY)
- status = MAYREDO;
- exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
- }
- else
- {
-#ifdef DEBUG
- printk ("Retrying.\n");
-#endif
- SCpnt->flags |= WAS_TIMEDOUT;
- SCpnt->internal_timeout &= ~IN_ABORT;
- status = REDO;
- }
- break;
- case DID_BUS_BUSY:
- case DID_PARITY:
- status = REDO;
- break;
- case DID_NO_CONNECT:
-#ifdef DEBUG
- printk("Couldn't connect.\n");
-#endif
- exit = (DRIVER_HARD | SUGGEST_ABORT);
- break;
- case DID_ERROR:
- status = MAYREDO;
- exit = (DRIVER_HARD | SUGGEST_ABORT);
- break;
- case DID_BAD_TARGET:
- case DID_ABORT:
- exit = (DRIVER_INVALID | SUGGEST_ABORT);
- break;
- case DID_RESET:
- if (SCpnt->flags & IS_RESETTING)
- {
- SCpnt->flags &= ~IS_RESETTING;
- status = REDO;
- break;
- }
-
- if(msg_byte(result) == GOOD &&
- status_byte(result) == CHECK_CONDITION) {
- switch (check_sense(SCpnt)) {
- case 0:
- update_timeout(SCpnt, oldto);
- status = REDO;
- break;
- case SUGGEST_REMAP:
- case SUGGEST_RETRY:
- status = MAYREDO;
- exit = DRIVER_SENSE | SUGGEST_RETRY;
- break;
- case SUGGEST_ABORT:
- status = FINISHED;
- exit = DRIVER_SENSE | SUGGEST_ABORT;
- break;
- case SUGGEST_SENSE:
- scsi_request_sense (SCpnt);
- status = PENDING;
- break;
- }
- } else {
- status=REDO;
- exit = SUGGEST_RETRY;
- }
- break;
- default :
- exit = (DRIVER_ERROR | SUGGEST_DIE);
- }
+ /*
+ * We don't have to worry about this one timing out any more.
+ */
+ scsi_delete_timer(SCpnt);
- switch (status)
+ /*
+ * First, see whether this command already timed out. If so, we ignore
+ * the response. We treat it as if the command never finished.
+ */
+ if( SCpnt->state == SCSI_STATE_TIMEOUT )
{
- case FINISHED:
- case PENDING:
- break;
- case MAYREDO:
-#ifdef DEBUG
- printk("In MAYREDO, allowing %d retries, have %d\n",
- SCpnt->allowed, SCpnt->retries);
-#endif
- if ((++SCpnt->retries) < SCpnt->allowed)
- {
- if ((SCpnt->retries >= (SCpnt->allowed >> 1))
- && !(SCpnt->host->last_reset > 0 &&
- jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
- && !(SCpnt->flags & WAS_RESET))
- {
- printk("scsi%d channel %d : resetting for second half of retries.\n",
- SCpnt->host->host_no, SCpnt->channel);
- scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
- break;
- }
-
- }
- else
- {
- status = FINISHED;
- break;
- }
- /* fall through to REDO */
-
- case REDO:
-
- if (SCpnt->flags & WAS_SENSE)
- scsi_request_sense(SCpnt);
- else
- {
- memcpy ((void *) SCpnt->cmnd,
- (void*) SCpnt->data_cmnd,
- sizeof(SCpnt->data_cmnd));
- SCpnt->request_buffer = SCpnt->buffer;
- SCpnt->request_bufflen = SCpnt->bufflen;
- SCpnt->use_sg = SCpnt->old_use_sg;
- SCpnt->cmd_len = SCpnt->old_cmd_len;
- internal_cmnd (SCpnt);
- }
- break;
- default:
- INTERNAL_ERROR;
+ SCSI_LOG_MLCOMPLETE(1,printk("Ignoring completion of %p due to timeout status", SCpnt));
+ return;
}
- if (status == FINISHED) {
-#ifdef DEBUG
- printk("Calling done function - at address %p\n", SCpnt->done);
-#endif
- host->host_busy--; /* Indicate that we are free */
+ SCpnt->state = SCSI_STATE_BHQUEUE;
+ SCpnt->owner = SCSI_OWNER_BH_HANDLER;
+ SCpnt->bh_next = NULL;
- if (host->block && host->host_busy == 0) {
- host_active = NULL;
-
- /* For block devices "wake_up" is done in end_scsi_request */
- if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
- MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
- struct Scsi_Host * next;
-
- for (next = host->block; next != host; next = next->block)
- wake_up(&next->host_wait);
- }
-
- }
+ /*
+ * Next, put this command in the BH queue. All processing of the command
+ * past this point will take place with interrupts turned on.
+ * We start by atomicly swapping the pointer into the queue head slot.
+ * If it was NULL before, then everything is fine, and we are done
+ * (this is the normal case). If it was not NULL, then we block interrupts,
+ * and link them together.
+ * We need a spinlock here, or compare and exchange if we can reorder incoming
+ * Scsi_Cmnds, as it happens pretty often scsi_done is called multiple times
+ * before bh is serviced. -jj
+ */
- wake_up(&host->host_wait);
- SCpnt->result = result | ((exit & 0xff) << 24);
- SCpnt->use_sg = SCpnt->old_use_sg;
- SCpnt->cmd_len = SCpnt->old_cmd_len;
- SCpnt->done (SCpnt);
- }
+ spin_lock_irqsave(&scsi_bh_queue_spin, flags);
+ if (!scsi_bh_queue_head) {
+ scsi_bh_queue_head = SCpnt;
+ scsi_bh_queue_tail = SCpnt;
+ } else {
+ scsi_bh_queue_tail->bh_next = SCpnt;
+ scsi_bh_queue_tail = SCpnt;
+ }
+ spin_unlock_irqrestore(&scsi_bh_queue_spin, flags);
-#undef FINISHED
-#undef REDO
-#undef MAYREDO
-#undef PENDING
+ /*
+ * Mark the bottom half handler to be run.
+ */
+ mark_bh(SCSI_BH);
}
/*
- * The scsi_abort function interfaces with the abort() function of the host
- * we are aborting, and causes the current command to not complete. The
- * caller should deal with any error messages or status returned on the
- * next call.
+ * Procedure: scsi_bottom_half_handler
*
- * This will not be called reentrantly for a given host.
- */
-
-/*
- * Since we're nice guys and specified that abort() and reset()
- * can be non-reentrant. The internal_timeout flags are used for
- * this.
+ * Purpose: Called after we have finished processing interrupts, it
+ * performs post-interrupt handling for commands that may
+ * have completed.
+ *
+ * Notes: This is called with all interrupts enabled. This should reduce
+ * interrupt latency, stack depth, and reentrancy of the low-level
+ * drivers.
*/
-
-
-int scsi_abort (Scsi_Cmnd * SCpnt, int why)
-{
- int oldto;
- unsigned long flags;
- struct Scsi_Host * host = SCpnt->host;
-
- while(1)
- {
- save_flags(flags);
- cli();
-
- /*
- * Protect against races here. If the command is done, or we are
- * on a different command forget it.
- */
- if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
- restore_flags(flags);
- return 0;
- }
-
- if (SCpnt->internal_timeout & IN_ABORT)
- {
- restore_flags(flags);
- while (SCpnt->internal_timeout & IN_ABORT)
- barrier();
- }
- else
- {
- SCpnt->internal_timeout |= IN_ABORT;
- oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
-
- if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
- /* OK, this command must have died when we did the
- * reset. The device itself must have lied.
- */
- printk("Stale command on %d %d:%d appears to have died when"
- " the bus was reset\n",
- SCpnt->channel, SCpnt->target, SCpnt->lun);
- }
-
- restore_flags(flags);
- if (!host->host_busy) {
- SCpnt->internal_timeout &= ~IN_ABORT;
- update_timeout(SCpnt, oldto);
- return 0;
- }
- printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
- " channel %d, id %d, lun %d ",
- SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel,
- (int) SCpnt->target, (int) SCpnt->lun);
- print_command (SCpnt->cmnd);
- if (SCpnt->serial_number != SCpnt->serial_number_at_timeout)
- return 0;
- SCpnt->abort_reason = why;
- switch(host->hostt->abort(SCpnt)) {
- /* We do not know how to abort. Try waiting another
- * time increment and see if this helps. Set the
- * WAS_TIMEDOUT flag set so we do not try this twice
- */
- case SCSI_ABORT_BUSY: /* Tough call - returning 1 from
- * this is too severe
- */
- case SCSI_ABORT_SNOOZE:
- if(why == DID_TIME_OUT) {
- save_flags(flags);
- cli();
- SCpnt->internal_timeout &= ~IN_ABORT;
- if(SCpnt->flags & WAS_TIMEDOUT) {
- restore_flags(flags);
- return 1; /* Indicate we cannot handle this.
- * We drop down into the reset handler
- * and try again
- */
- } else {
- SCpnt->flags |= WAS_TIMEDOUT;
- oldto = SCpnt->timeout_per_command;
- update_timeout(SCpnt, oldto);
- }
- restore_flags(flags);
- }
- return 0;
- case SCSI_ABORT_PENDING:
- if(why != DID_TIME_OUT) {
- save_flags(flags);
- cli();
- update_timeout(SCpnt, oldto);
- restore_flags(flags);
- }
- return 0;
- case SCSI_ABORT_SUCCESS:
- /* We should have already aborted this one. No
- * need to adjust timeout
- */
- SCpnt->internal_timeout &= ~IN_ABORT;
- return 0;
- case SCSI_ABORT_NOT_RUNNING:
- SCpnt->internal_timeout &= ~IN_ABORT;
- update_timeout(SCpnt, 0);
- return 0;
- case SCSI_ABORT_ERROR:
- default:
- SCpnt->internal_timeout &= ~IN_ABORT;
- return 1;
- }
- }
- }
-}
-
-
-/* Mark a single SCSI Device as having been reset. */
-
-static inline void scsi_mark_device_reset(Scsi_Device *Device)
-{
- Device->was_reset = 1;
- Device->expecting_cc_ua = 1;
-}
-
-
-/* Mark all SCSI Devices on a specific Host as having been reset. */
-
-void scsi_mark_host_reset(struct Scsi_Host *Host)
-{
- Scsi_Cmnd *SCpnt;
- for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
- scsi_mark_device_reset(SCpnt->device);
-}
-
-
-/* Mark all SCSI Devices on a specific Host Bus as having been reset. */
-
-void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel)
-{
- Scsi_Cmnd *SCpnt;
- for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
- if (SCpnt->channel == channel)
- scsi_mark_device_reset(SCpnt->device);
-}
-
-
-int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+void scsi_bottom_half_handler(void)
{
- int temp;
- unsigned long flags;
- Scsi_Cmnd * SCpnt1;
- struct Scsi_Host * host = SCpnt->host;
-
- printk("SCSI bus is being reset for host %d channel %d.\n",
- host->host_no, SCpnt->channel);
-
-#if 0
- /*
- * First of all, we need to make a recommendation to the low-level
- * driver as to whether a BUS_DEVICE_RESET should be performed,
- * or whether we should do a full BUS_RESET. There is no simple
- * algorithm here - we basically use a series of heuristics
- * to determine what we should do.
- */
- SCpnt->host->suggest_bus_reset = FALSE;
-
- /*
- * First see if all of the active devices on the bus have
- * been jammed up so that we are attempting resets. If so,
- * then suggest a bus reset. Forcing a bus reset could
- * result in some race conditions, but no more than
- * you would usually get with timeouts. We will cross
- * that bridge when we come to it.
- *
- * This is actually a pretty bad idea, since a sequence of
- * commands will often timeout together and this will cause a
- * Bus Device Reset followed immediately by a SCSI Bus Reset.
- * If all of the active devices really are jammed up, the
- * Bus Device Reset will quickly timeout and scsi_times_out
- * will follow up with a SCSI Bus Reset anyway.
- */
- SCpnt1 = host->host_queue;
- while(SCpnt1) {
- if( SCpnt1->request.rq_status != RQ_INACTIVE
- && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
- break;
- SCpnt1 = SCpnt1->next;
- }
- if( SCpnt1 == NULL ) {
- reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
- }
-
- /*
- * If the code that called us is suggesting a hard reset, then
- * definitely request it. This usually occurs because a
- * BUS_DEVICE_RESET times out.
- *
- * Passing reset_flags along takes care of this automatically.
- */
- if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) {
- SCpnt->host->suggest_bus_reset = TRUE;
- }
-#endif
-
- while (1) {
- save_flags(flags);
- cli();
-
- /*
- * Protect against races here. If the command is done, or we are
- * on a different command forget it.
- */
- if (reset_flags & SCSI_RESET_ASYNCHRONOUS)
- if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
- restore_flags(flags);
- return 0;
- }
-
- if (SCpnt->internal_timeout & IN_RESET)
- {
- restore_flags(flags);
- while (SCpnt->internal_timeout & IN_RESET)
- barrier();
- }
- else
- {
- SCpnt->internal_timeout |= IN_RESET;
- update_timeout(SCpnt, RESET_TIMEOUT);
-
- if (host->host_busy)
- {
- restore_flags(flags);
- SCpnt1 = host->host_queue;
- while(SCpnt1) {
- if (SCpnt1->request.rq_status != RQ_INACTIVE) {
-#if 0
- if (!(SCpnt1->flags & IS_RESETTING) &&
- !(SCpnt1->internal_timeout & IN_ABORT))
- scsi_abort(SCpnt1, DID_RESET);
-#endif
- SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
- }
- SCpnt1 = SCpnt1->next;
- }
-
- host->last_reset = jiffies;
- temp = host->hostt->reset(SCpnt, reset_flags);
- /*
- This test allows the driver to introduce an additional bus
- settle time delay by setting last_reset up to 20 seconds in
- the future. In the normal case where the driver does not
- modify last_reset, it must be assumed that the actual bus
- reset occurred immediately prior to the return to this code,
- and so last_reset must be updated to the current time, so
- that the delay in internal_cmnd will guarantee at least a
- MIN_RESET_DELAY bus settle time.
- */
- if (host->last_reset - jiffies > 20UL * HZ)
- host->last_reset = jiffies;
- }
- else
- {
- if (!host->block) host->host_busy++;
- restore_flags(flags);
- host->last_reset = jiffies;
- SCpnt->flags |= (WAS_RESET | IS_RESETTING);
- temp = host->hostt->reset(SCpnt, reset_flags);
- if ((host->last_reset < jiffies) ||
- (host->last_reset > (jiffies + 20 * HZ)))
- host->last_reset = jiffies;
- if (!host->block) host->host_busy--;
- }
-
-#ifdef DEBUG
- printk("scsi reset function returned %d\n", temp);
-#endif
-
- /*
- * Now figure out what we need to do, based upon
- * what the low level driver said that it did.
- * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING,
- * or SCSI_RESET_WAKEUP, then the low level driver did a
- * bus device reset or bus reset, so we should go through
- * and mark one or all of the devices on that bus
- * as having been reset.
- */
- switch(temp & SCSI_RESET_ACTION) {
- case SCSI_RESET_SUCCESS:
- if (temp & SCSI_RESET_HOST_RESET)
- scsi_mark_host_reset(host);
- else if (temp & SCSI_RESET_BUS_RESET)
- scsi_mark_bus_reset(host, SCpnt->channel);
- else scsi_mark_device_reset(SCpnt->device);
- save_flags(flags);
- cli();
- SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
- restore_flags(flags);
- return 0;
- case SCSI_RESET_PENDING:
- if (temp & SCSI_RESET_HOST_RESET)
- scsi_mark_host_reset(host);
- else if (temp & SCSI_RESET_BUS_RESET)
- scsi_mark_bus_reset(host, SCpnt->channel);
- else scsi_mark_device_reset(SCpnt->device);
- case SCSI_RESET_NOT_RUNNING:
- return 0;
- case SCSI_RESET_PUNT:
- SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
- scsi_request_sense (SCpnt);
- return 0;
- case SCSI_RESET_WAKEUP:
- if (temp & SCSI_RESET_HOST_RESET)
- scsi_mark_host_reset(host);
- else if (temp & SCSI_RESET_BUS_RESET)
- scsi_mark_bus_reset(host, SCpnt->channel);
- else scsi_mark_device_reset(SCpnt->device);
- SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
- scsi_request_sense (SCpnt);
- /*
- * If a bus reset was performed, we
- * need to wake up each and every command
- * that was active on the bus or if it was a HBA
- * reset all active commands on all channels
- */
- if( temp & SCSI_RESET_HOST_RESET )
- {
- SCpnt1 = host->host_queue;
- while(SCpnt1) {
- if (SCpnt1->request.rq_status != RQ_INACTIVE
- && SCpnt1 != SCpnt)
- scsi_request_sense (SCpnt1);
- SCpnt1 = SCpnt1->next;
- }
- } else if( temp & SCSI_RESET_BUS_RESET ) {
- SCpnt1 = host->host_queue;
- while(SCpnt1) {
- if(SCpnt1->request.rq_status != RQ_INACTIVE
- && SCpnt1 != SCpnt
- && SCpnt1->channel == SCpnt->channel)
- scsi_request_sense (SCpnt);
- SCpnt1 = SCpnt1->next;
- }
- }
- return 0;
- case SCSI_RESET_SNOOZE:
- /* In this case, we set the timeout field to 0
- * so that this command does not time out any more,
- * and we return 1 so that we get a message on the
- * screen.
- */
- save_flags(flags);
- cli();
- SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
- update_timeout(SCpnt, 0);
- restore_flags(flags);
- /* If you snooze, you lose... */
- case SCSI_RESET_ERROR:
- default:
- return 1;
- }
+ Scsi_Cmnd * SCpnt;
+ Scsi_Cmnd * SCnext;
+ static atomic_t recursion_depth;
+ unsigned long flags;
+
+ while(1==1)
+ {
+ /*
+ * If the counter is > 0, that means that there is another interrupt handler
+ * out there somewhere processing commands. We don't want to get these guys
+ * nested as this can lead to stack overflow problems, and there isn't any
+ * real sense in it anyways.
+ */
+ if( atomic_read(&recursion_depth) > 0 )
+ {
+ printk("SCSI bottom half recursion depth = %d \n", atomic_read(&recursion_depth));
+ SCSI_LOG_MLCOMPLETE(1,printk("SCSI bottom half recursion depth = %d \n",
+ atomic_read(&recursion_depth)));
+ break;
+ }
+
+ /*
+ * We need to hold the spinlock, so that nobody is tampering with the queue. -jj
+ * We will process everything we find in the list here.
+ */
+
+ spin_lock_irqsave(&scsi_bh_queue_spin, flags);
+ SCpnt = scsi_bh_queue_head;
+ scsi_bh_queue_head = NULL;
+ spin_unlock_irqrestore(&scsi_bh_queue_spin, flags);
+
+ if( SCpnt == NULL )
+ return;
+
+ atomic_inc(&recursion_depth);
+
+ SCnext = SCpnt->bh_next;
+
+ for(; SCpnt; SCpnt = SCnext)
+ {
+ SCnext = SCpnt->bh_next;
+
+ switch( scsi_decide_disposition(SCpnt) )
+ {
+ case SUCCESS:
+ /*
+ * Add to BH queue.
+ */
+ SCSI_LOG_MLCOMPLETE(3,printk("Command finished %d %d 0x%x\n", SCpnt->host->host_busy,
+ SCpnt->host->host_failed,
+ SCpnt->result));
+
+ scsi_finish_command(SCpnt);
+ break;
+ case NEEDS_RETRY:
+ /*
+ * We only come in here if we want to retry a command. The
+ * test to see whether the command should be retried should be
+ * keeping track of the number of tries, so we don't end up looping,
+ * of course.
+ */
+ SCSI_LOG_MLCOMPLETE(3,printk("Command needs retry %d %d 0x%x\n", SCpnt->host->host_busy,
+ SCpnt->host->host_failed, SCpnt->result));
+
+ scsi_retry_command(SCpnt);
+ break;
+ case ADD_TO_MLQUEUE:
+ /*
+ * This typically happens for a QUEUE_FULL message -
+ * typically only when the queue depth is only
+ * approximate for a given device. Adding a command
+ * to the queue for the device will prevent further commands
+ * from being sent to the device, so we shouldn't end up
+ * with tons of things being sent down that shouldn't be.
+ */
+ scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_DEVICE_BUSY);
+ break;
+ default:
+ /*
+ * Here we have a fatal error of some sort. Turn it over to
+ * the error handler.
+ */
+ SCSI_LOG_MLCOMPLETE(3,printk("Command failed %p %x active=%d busy=%d failed=%d\n",
+ SCpnt, SCpnt->result,
+ atomic_read(&SCpnt->host->host_active),
+ SCpnt->host->host_busy,
+ SCpnt->host->host_failed));
+
+ /*
+ * Dump the sense information too.
+ */
+ if ((status_byte (SCpnt->result) & CHECK_CONDITION) != 0)
+ {
+ SCSI_LOG_MLCOMPLETE(3,print_sense("bh",SCpnt));
+ }
+
+
+ if( SCpnt->host->eh_wait != NULL )
+ {
+ SCpnt->host->host_failed++;
+ SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
+ SCpnt->state = SCSI_STATE_FAILED;
+ SCpnt->host->in_recovery = 1;
+ /*
+ * If the host is having troubles, then look to see if this was the last
+ * command that might have failed. If so, wake up the error handler.
+ */
+ if( atomic_read(&SCpnt->host->host_active) == SCpnt->host->host_failed )
+ {
+ SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
+ atomic_read(&SCpnt->host->eh_wait->count)));
+ up(SCpnt->host->eh_wait);
+ }
+ }
+ else
+ {
+ /*
+ * We only get here if the error recovery thread has died.
+ */
+ scsi_finish_command(SCpnt);
+ }
+ }
+ } /* for(; SCpnt...) */
+
+ atomic_dec(&recursion_depth);
+
+ } /* while(1==1) */
- return temp;
- }
- }
}
-
-static void scsi_main_timeout(void)
+/*
+ * Function: scsi_retry_command
+ *
+ * Purpose: Send a command back to the low level to be retried.
+ *
+ * Notes: This command is always executed in the context of the
+ * bottom half handler, or the error handler thread. Low
+ * level drivers should not become re-entrant as a result of
+ * this.
+ */
+int
+scsi_retry_command(Scsi_Cmnd * SCpnt)
{
- /*
- * We must not enter update_timeout with a timeout condition still pending.
- */
-
- int timed_out;
- unsigned long flags;
- struct Scsi_Host * host;
- Scsi_Cmnd * SCpnt = NULL;
-
- save_flags(flags);
- cli();
-
- update_timeout(NULL, 0);
-
- /*
- * Find all timers such that they have 0 or negative (shouldn't happen)
- * time remaining on them.
- */
- timed_out = 0;
- for (host = scsi_hostlist; host; host = host->next) {
- for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
- if (SCpnt->timeout == -1)
- {
- SCpnt->timeout = 0;
- SCpnt->serial_number_at_timeout = SCpnt->serial_number;
- ++timed_out;
- }
- }
- if (timed_out > 0) {
- for (host = scsi_hostlist; host; host = host->next) {
- for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
- if (SCpnt->serial_number_at_timeout > 0 &&
- SCpnt->serial_number_at_timeout == SCpnt->serial_number)
- {
- restore_flags(flags);
- scsi_times_out(SCpnt);
- SCpnt->serial_number_at_timeout = 0;
- cli();
- }
- }
- }
- restore_flags(flags);
+ memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd,
+ sizeof(SCpnt->data_cmnd));
+ SCpnt->request_buffer = SCpnt->buffer;
+ SCpnt->request_bufflen = SCpnt->bufflen;
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ return internal_cmnd (SCpnt);
}
/*
- * The strategy is to cause the timer code to call scsi_times_out()
- * when the soonest timeout is pending.
- * The arguments are used when we are queueing a new command, because
- * we do not want to subtract the time used from this time, but when we
- * set the timer, we want to take this value into account.
+ * Function: scsi_finish_command
+ *
+ * Purpose: Pass command off to upper layer for finishing of I/O
+ * request, waking processes that are waiting on results,
+ * etc.
*/
-
-int update_timeout(Scsi_Cmnd * SCset, int timeout)
+void
+scsi_finish_command(Scsi_Cmnd * SCpnt)
{
- unsigned int least, used;
- unsigned int oldto;
- unsigned long flags;
struct Scsi_Host * host;
- Scsi_Cmnd * SCpnt = NULL;
-
- save_flags(flags);
- cli();
+ Scsi_Device * device;
- oldto = 0;
-
- /*
- * This routine can be a performance bottleneck under high loads, since
- * it is called twice per SCSI operation: once when internal_cmnd is
- * called, and again when scsi_done completes the command. To limit
- * the load this routine can cause, we shortcut processing if no clock
- * ticks have occurred since the last time it was called.
- */
+ host = SCpnt->host;
+ device = SCpnt->device;
- if (jiffies == time_start && timer_table[SCSI_TIMER].expires > 0) {
- if(SCset){
- oldto = SCset->timeout;
- SCset->timeout = timeout;
- if (timeout > 0 &&
- jiffies + timeout < timer_table[SCSI_TIMER].expires)
- timer_table[SCSI_TIMER].expires = jiffies + timeout;
- }
- restore_flags(flags);
- return oldto;
+ host->host_busy--; /* Indicate that we are free */
+ device->device_busy--; /* Decrement device usage counter. */
+
+ if (host->block && host->host_busy == 0)
+ {
+ host_active = NULL;
+
+ /* For block devices "wake_up" is done in end_scsi_request */
+ if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
+ MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
+ struct Scsi_Host * next;
+
+ for (next = host->block; next != host; next = next->block)
+ wake_up(&next->host_wait);
+ }
+
}
-
- /*
- * Figure out how much time has passed since the last time the timeouts
- * were updated
- */
- used = (time_start) ? (jiffies - time_start) : 0;
-
+
/*
- * Find out what is due to timeout soonest, and adjust all timeouts for
- * the amount of time that has passed since the last time we called
- * update_timeout.
+ * Now try and drain the mid-level queue if any commands have been
+ * inserted. Check to see whether the queue even has anything in
+ * it first, as otherwise this is useless overhead.
*/
-
- oldto = 0;
-
- if(SCset){
- oldto = SCset->timeout - used;
- SCset->timeout = timeout;
+ if( SCpnt->host->pending_commands != NULL )
+ {
+ scsi_mlqueue_finish(SCpnt->host, SCpnt->device);
}
- least = 0xffffffff;
-
- for(host = scsi_hostlist; host; host = host->next)
- for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
- if (SCpnt->timeout > 0) {
- if (SCpnt != SCset)
- SCpnt->timeout -= used;
- if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
- if(SCpnt->timeout > 0 && SCpnt->timeout < least)
- least = SCpnt->timeout;
- }
-
+ wake_up(&host->host_wait);
+
/*
- * If something is due to timeout again, then we will set the next timeout
- * interrupt to occur. Otherwise, timeouts are disabled.
+ * If we have valid sense information, then some kind of recovery
+ * must have taken place. Make a note of this.
*/
-
- if (least != 0xffffffff)
+ if( scsi_sense_valid(SCpnt) )
{
- time_start = jiffies;
- timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
- timer_active |= 1 << SCSI_TIMER;
+ SCpnt->result |= (DRIVER_SENSE << 24);
}
- else
- {
- timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
- timer_active &= ~(1 << SCSI_TIMER);
- }
- restore_flags(flags);
- return oldto;
+
+ SCSI_LOG_MLCOMPLETE(3,printk("Notifying upper driver of completion for device %d %x\n",
+ SCpnt->device->id, SCpnt->result));
+
+ SCpnt->owner = SCSI_OWNER_HIGHLEVEL;
+ SCpnt->state = SCSI_STATE_FINISHED;
+ SCpnt->done (SCpnt);
}
#ifdef CONFIG_MODULES
@@ -2393,9 +1830,10 @@ void *scsi_malloc(unsigned int len)
if ((dma_malloc_freelist[i] & (mask << j)) == 0){
dma_malloc_freelist[i] |= (mask << j);
restore_flags(flags);
- dma_free_sectors -= nbits;
+ scsi_dma_free_sectors -= nbits;
#ifdef DEBUG
- printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9));
+ SCSI_LOG_MLQUEUE(3,printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9)));
+ printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9));
#endif
return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
}
@@ -2418,6 +1856,7 @@ int scsi_free(void *obj, unsigned int len)
ret = __builtin_return_address(0);
#endif
printk("scsi_free %p %d\n",obj, len);
+ SCSI_LOG_MLQUEUE(3,printk("SFree: %p %d\n",obj, len));
#endif
for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
@@ -2443,7 +1882,7 @@ int scsi_free(void *obj, unsigned int len)
#endif
panic("scsi_free:Trying to free unused memory");
}
- dma_free_sectors += nbits;
+ scsi_dma_free_sectors += nbits;
dma_malloc_freelist[page] &= ~(mask << sector);
restore_flags(flags);
return 0;
@@ -2455,7 +1894,7 @@ int scsi_free(void *obj, unsigned int len)
int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
-void * scsi_init_malloc(unsigned int size, int priority)
+void * scsi_init_malloc(unsigned int size, int gfp_mask)
{
void * retval;
@@ -2468,10 +1907,9 @@ void * scsi_init_malloc(unsigned int size, int priority)
for (order = 0, a_size = PAGE_SIZE;
a_size < size; order++, a_size <<= 1)
;
- retval = (void *) __get_dma_pages(priority & GFP_LEVEL_MASK,
- order);
+ retval = (void *) __get_free_pages(gfp_mask | GFP_DMA, order);
} else
- retval = kmalloc(size, priority);
+ retval = kmalloc(size, gfp_mask);
if (retval)
memset(retval, 0, size);
@@ -2512,37 +1950,37 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt)
scsi_init_malloc(sizeof(Scsi_Cmnd),
GFP_ATOMIC |
(host->unchecked_isa_dma ? GFP_DMA : 0));
- SCpnt->host = host;
- SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
- SCpnt->request.rq_status = RQ_INACTIVE;
- SCpnt->use_sg = 0;
- SCpnt->old_use_sg = 0;
- SCpnt->old_cmd_len = 0;
- SCpnt->timeout = 0;
- SCpnt->underflow = 0;
- SCpnt->transfersize = 0;
- SCpnt->serial_number = 0;
- SCpnt->serial_number_at_timeout = 0;
- SCpnt->host_scribble = NULL;
- if(host->host_queue)
- host->host_queue->prev = SCpnt;
- SCpnt->next = host->host_queue;
- SCpnt->prev = NULL;
- host->host_queue = SCpnt;
- SCpnt->device_next = SDpnt->device_queue;
- SDpnt->device_queue = SCpnt;
+ memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
+ SCpnt->host = host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ SCpnt->host_wait = FALSE;
+ SCpnt->device_wait = FALSE;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->old_cmd_len = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->serial_number = 0;
+ SCpnt->serial_number_at_timeout = 0;
+ SCpnt->host_scribble = NULL;
+ SCpnt->next = SDpnt->device_queue;
+ SDpnt->device_queue = SCpnt;
+ SCpnt->state = SCSI_STATE_UNUSED;
+ SCpnt->owner = SCSI_OWNER_NOBODY;
}
SDpnt->has_cmdblocks = 1;
}
+#ifndef MODULE /* { */
/*
* scsi_dev_init() is our initialization routine, which in turn calls host
* initialization, bus scanning, and sd/st initialization routines.
+ * This is only used at boot time.
*/
-
__initfunc(int scsi_dev_init(void))
{
Scsi_Device * SDpnt;
@@ -2560,9 +1998,6 @@ __initfunc(int scsi_dev_init(void))
/* Init a few things so we can "malloc" memory. */
scsi_loadable_module_flag = 0;
- timer_table[SCSI_TIMER].fn = scsi_main_timeout;
- timer_table[SCSI_TIMER].expires = 0;
-
/* Register the /proc/scsi/scsi entry */
#if CONFIG_PROC_FS
proc_scsi_register(0, &proc_scsi_scsi);
@@ -2571,12 +2006,17 @@ __initfunc(int scsi_dev_init(void))
/* initialize all hosts */
scsi_init();
- scsi_devices = (Scsi_Device *) NULL;
+ /*
+ * This is where the processing takes place for most everything
+ * when commands are completed. Until we do this, we will not be able
+ * to queue any commands.
+ */
+ init_bh(SCSI_BH, scsi_bottom_half_handler);
for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
scan_scsis(shpnt,0,0,0,0); /* scan for scsi devices */
if (shpnt->select_queue_depths != NULL)
- (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ (shpnt->select_queue_depths)(shpnt, shpnt->host_queue);
}
printk("scsi : detected ");
@@ -2589,14 +2029,17 @@ __initfunc(int scsi_dev_init(void))
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
- for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
- SDpnt->scsi_request_fn = NULL;
- for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
- if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
+ {
+ /* SDpnt->scsi_request_fn = NULL; */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
}
-
-
+
/*
* This should build the DMA pool.
*/
@@ -2614,6 +2057,7 @@ __initfunc(int scsi_dev_init(void))
return 0;
}
+#endif /* MODULE */ /* } */
static void print_inquiry(unsigned char *data)
{
@@ -2666,7 +2110,7 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
{
Scsi_Cmnd *SCpnt;
struct Scsi_Device_Template *SDTpnt;
- Scsi_Device *scd, *scd_h = NULL;
+ Scsi_Device *scd;
struct Scsi_Host *HBA_ptr;
char *p;
int host, channel, id, lun;
@@ -2674,37 +2118,41 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
off_t begin = 0;
off_t pos = 0;
- scd = scsi_devices;
- HBA_ptr = scsi_hostlist;
-
if(inout == 0) {
- size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ /*
+ * First, see if there are any attached devices or not.
+ */
+ for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next)
+ {
+ if( HBA_ptr->host_queue != NULL )
+ {
+ break;
+ }
+ }
+ size = sprintf(buffer+len,"Attached devices: %s\n", (HBA_ptr)?"":"none");
len += size;
pos = begin + len;
- while (HBA_ptr) {
+ for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next)
+ {
#if 0
size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no,
HBA_ptr->hostt->procname);
len += size;
pos = begin + len;
#endif
- scd = scsi_devices;
- while (scd) {
- if (scd->host == HBA_ptr) {
- 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;
- }
- scd = scd->next;
+ for(scd = HBA_ptr->host_queue; scd; scd = scd->next)
+ {
+ 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;
}
- HBA_ptr = HBA_ptr->next;
}
stop_output:
@@ -2715,10 +2163,126 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
return (len);
}
- if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+ if(!buffer || length < 11 || strncmp("scsi", buffer, 4))
return(-EINVAL);
/*
+ * Usage: echo "scsi dump #N" > /proc/scsi/scsi
+ * to dump status of all scsi commands. The number is used to specify the level
+ * of detail in the dump.
+ */
+ if(!strncmp("dump", buffer + 5, 4))
+ {
+ unsigned int level;
+
+ p = buffer + 10;
+
+ if( *p == '\0' )
+ return (-EINVAL);
+
+ level = simple_strtoul(p, NULL, 0);
+ scsi_dump_status(level);
+ }
+ /*
+ * Usage: echo "scsi log token #N" > /proc/scsi/scsi
+ * where token is one of [error,scan,mlqueue,mlcomplete,llqueue,
+ * llcomplete,hlqueue,hlcomplete]
+ */
+#if CONFIG_SCSI_LOGGING /* { */
+
+ if(!strncmp("log", buffer + 5, 3))
+ {
+ char * token;
+ unsigned int level;
+
+ p = buffer + 9;
+ token = p;
+ while(*p != ' ' && *p != '\t' && *p != '\0')
+ {
+ p++;
+ }
+
+ if( *p == '\0' )
+ {
+ if( strncmp(token, "all", 3) == 0 )
+ {
+ /*
+ * Turn on absolutely everything.
+ */
+ scsi_logging_level = ~0;
+ }
+ else if( strncmp(token, "none", 4) == 0 )
+ {
+ /*
+ * Turn off absolutely everything.
+ */
+ scsi_logging_level = 0;
+ }
+ else
+ {
+ return (-EINVAL);
+ }
+ }
+ else
+ {
+ *p++ = '\0';
+
+ level = simple_strtoul(p, NULL, 0);
+
+ /*
+ * Now figure out what to do with it.
+ */
+ if( strcmp(token, "error") == 0 )
+ {
+ SCSI_SET_ERROR_RECOVERY_LOGGING(level);
+ }
+ else if( strcmp(token, "timeout") == 0 )
+ {
+ SCSI_SET_TIMEOUT_LOGGING(level);
+ }
+ else if( strcmp(token, "scan") == 0 )
+ {
+ SCSI_SET_SCAN_BUS_LOGGING(level);
+ }
+ else if( strcmp(token, "mlqueue") == 0 )
+ {
+ SCSI_SET_MLQUEUE_LOGGING(level);
+ }
+ else if( strcmp(token, "mlcomplete") == 0 )
+ {
+ SCSI_SET_MLCOMPLETE_LOGGING(level);
+ }
+ else if( strcmp(token, "llqueue") == 0 )
+ {
+ SCSI_SET_LLQUEUE_LOGGING(level);
+ }
+ else if( strcmp(token, "llcomplete") == 0 )
+ {
+ SCSI_SET_LLCOMPLETE_LOGGING(level);
+ }
+ else if( strcmp(token, "hlqueue") == 0 )
+ {
+ SCSI_SET_HLQUEUE_LOGGING(level);
+ }
+ else if( strcmp(token, "hlcomplete") == 0 )
+ {
+ SCSI_SET_HLCOMPLETE_LOGGING(level);
+ }
+ else if( strcmp(token, "ioctl") == 0 )
+ {
+ SCSI_SET_IOCTL_LOGGING(level);
+ }
+ else
+ {
+ return (-EINVAL);
+ }
+ }
+
+ printk("scsi logging level set to 0x%8.8x\n", scsi_logging_level);
+ }
+#endif /* CONFIG_SCSI_LOGGING */ /* } */
+
+ /*
* Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
* with "0 1 2 3" replaced by your "Host Channel Id Lun".
* Consider this feature BETA.
@@ -2740,20 +2304,29 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
printk("scsi singledevice %d %d %d %d\n", host, channel,
id, lun);
- while(scd && (scd->host->host_no != host
- || scd->channel != channel
- || scd->id != id
- || scd->lun != lun)) {
- scd = scd->next;
- }
- if(scd)
- return(-ENOSYS); /* We do not yet support unplugging */
- while(HBA_ptr && HBA_ptr->host_no != host)
- HBA_ptr = HBA_ptr->next;
-
+ for(HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next)
+ {
+ if( HBA_ptr->host_no == host )
+ {
+ break;
+ }
+ }
if(!HBA_ptr)
return(-ENXIO);
+ for(scd = HBA_ptr->host_queue; scd; scd = scd->next)
+ {
+ if((scd->channel == channel
+ && scd->id == id
+ && scd->lun == lun))
+ {
+ break;
+ }
+ }
+
+ if(scd)
+ return(-ENOSYS); /* We do not yet support unplugging */
+
scan_scsis (HBA_ptr, 1, channel, id, lun);
return(length);
@@ -2778,15 +2351,25 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
id = simple_strtoul(p+1, &p, 0);
lun = simple_strtoul(p+1, &p, 0);
- while(scd != NULL) {
- if(scd->host->host_no == host
- && scd->channel == channel
- && scd->id == id
- && scd->lun == lun){
+
+ for(HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next)
+ {
+ if( HBA_ptr->host_no == host )
+ {
+ break;
+ }
+ }
+ if(!HBA_ptr)
+ return(-ENODEV);
+
+ for(scd = HBA_ptr->host_queue; scd; scd = scd->next)
+ {
+ if((scd->channel == channel
+ && scd->id == id
+ && scd->lun == lun))
+ {
break;
}
- scd_h = scd;
- scd = scd->next;
}
if(scd == NULL)
@@ -2806,24 +2389,23 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
* Nobody is using this device any more.
* Free all of the command structures.
*/
- for(SCpnt=scd->host->host_queue; SCpnt; SCpnt = SCpnt->next){
- if(SCpnt->device == scd) {
- if(SCpnt->prev != NULL)
- SCpnt->prev->next = SCpnt->next;
- if(SCpnt->next != NULL)
- SCpnt->next->prev = SCpnt->prev;
- if(SCpnt == scd->host->host_queue)
- scd->host->host_queue = SCpnt->next;
- scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
- }
+ for(SCpnt=scd->device_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ scd->device_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
}
/* Now we can remove the device structure */
- if(scd_h != NULL) {
- scd_h->next = scd->next;
- } else if (scsi_devices == scd) {
- /* We had a hit on the first entry of the device list */
- scsi_devices = scd->next;
+ if( scd->next != NULL )
+ scd->next->prev = scd->prev;
+
+ if( scd->prev != NULL )
+ scd->prev->next = scd->next;
+
+ if( HBA_ptr->host_queue == scd )
+ {
+ HBA_ptr->host_queue = scd->next;
}
+
scsi_init_free((char *) scd, sizeof(Scsi_Device));
} else {
return(-EBUSY);
@@ -2851,13 +2433,13 @@ static void resize_dma_pool(void)
unsigned int new_need_isa_buffer = 0;
unsigned char ** new_dma_malloc_pages = NULL;
- if( !scsi_devices )
+ if( !scsi_hostlist )
{
/*
* Free up the DMA pool.
*/
- if( dma_free_sectors != dma_sectors )
- panic("SCSI DMA pool memory leak %d %d\n",dma_free_sectors,dma_sectors);
+ if( scsi_dma_free_sectors != dma_sectors )
+ panic("SCSI DMA pool memory leak %d %d\n",scsi_dma_free_sectors,dma_sectors);
for(i=0; i < dma_sectors / SECTORS_PER_PAGE; i++)
scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
@@ -2870,7 +2452,7 @@ static void resize_dma_pool(void)
(dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_freelist));
dma_malloc_freelist = NULL;
dma_sectors = 0;
- dma_free_sectors = 0;
+ scsi_dma_free_sectors = 0;
return;
}
/* Next, check to see if we need to extend the DMA buffer pool */
@@ -2878,52 +2460,54 @@ static void resize_dma_pool(void)
new_dma_sectors = 2*SECTORS_PER_PAGE; /* Base value we use */
if (__pa(high_memory)-1 > ISA_DMA_THRESHOLD)
- scsi_need_isa_bounce_buffers = 1;
+ need_isa_bounce_buffers = 1;
else
- scsi_need_isa_bounce_buffers = 0;
+ need_isa_bounce_buffers = 0;
if (scsi_devicelist)
for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
- for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
- host = SDpnt->host;
-
- /*
- * sd and sr drivers allocate scatterlists.
- * sr drivers may allocate for each command 1x2048 or 2x1024 extra
- * buffers for 2k sector size and 1k fs.
- * sg driver allocates buffers < 4k.
- * st driver does not need buffers from the dma pool.
- * estimate 4k buffer/command for devices of unknown type (should panic).
- */
- if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
- SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
- new_dma_sectors += ((host->sg_tablesize *
- sizeof(struct scatterlist) + 511) >> 9) *
- SDpnt->queue_depth;
- if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
- new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
- }
- else if (SDpnt->type == TYPE_SCANNER ||
- SDpnt->type == TYPE_PROCESSOR ||
- SDpnt->type == TYPE_MEDIUM_CHANGER) {
- new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
- }
- else {
- if (SDpnt->type != TYPE_TAPE) {
- printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
- new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
- }
+ for (host = scsi_hostlist; host; host = host->next)
+ {
+ for (SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next)
+ {
+ /*
+ * sd and sr drivers allocate scatterlists.
+ * sr drivers may allocate for each command 1x2048 or 2x1024 extra
+ * buffers for 2k sector size and 1k fs.
+ * sg driver allocates buffers < 4k.
+ * st driver does not need buffers from the dma pool.
+ * estimate 4k buffer/command for devices of unknown type (should panic).
+ */
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
+ SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ SDpnt->queue_depth;
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
+ new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
+ }
+ else if (SDpnt->type == TYPE_SCANNER ||
+ SDpnt->type == TYPE_PROCESSOR ||
+ SDpnt->type == TYPE_MEDIUM_CHANGER) {
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ else {
+ if (SDpnt->type != TYPE_TAPE) {
+ printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ }
+
+ if(host->unchecked_isa_dma &&
+ need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ SDpnt->queue_depth;
+ new_need_isa_buffer++;
+ }
}
-
- if(host->unchecked_isa_dma &&
- scsi_need_isa_bounce_buffers &&
- SDpnt->type != TYPE_TAPE) {
- new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
- SDpnt->queue_depth;
- new_need_isa_buffer++;
- }
}
#ifdef DEBUG_INIT
@@ -2981,16 +2565,16 @@ static void resize_dma_pool(void)
scsi_init_free((char *) dma_malloc_pages, size);
}
- dma_free_sectors += new_dma_sectors - dma_sectors;
+ scsi_dma_free_sectors += new_dma_sectors - dma_sectors;
dma_malloc_pages = new_dma_malloc_pages;
dma_sectors = new_dma_sectors;
- need_isa_buffer = new_need_isa_buffer;
+ scsi_need_isa_buffer = new_need_isa_buffer;
restore_flags(flags);
#ifdef DEBUG_INIT
- printk("resize_dma_pool: dma free sectors = %d\n", dma_free_sectors);
+ printk("resize_dma_pool: dma free sectors = %d\n", scsi_dma_free_sectors);
printk("resize_dma_pool: dma sectors = %d\n", dma_sectors);
- printk("resize_dma_pool: need isa buffers = %d\n", need_isa_buffer);
+ printk("resize_dma_pool: need isa buffers = %d\n", scsi_need_isa_buffer);
#endif
}
@@ -3014,13 +2598,16 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
pcount = next_scsi_host;
if ((tpnt->present = tpnt->detect(tpnt)))
{
- if(pcount == next_scsi_host) {
- if(tpnt->present > 1) {
+ if(pcount == next_scsi_host)
+ {
+ if(tpnt->present > 1)
+ {
printk("Failure to register low-level scsi driver");
scsi_unregister_host(tpnt);
return 1;
}
- /* The low-level driver failed to register a driver. We
+ /*
+ * The low-level driver failed to register a driver. We
* can do this now.
*/
scsi_register(tpnt,0);
@@ -3033,16 +2620,46 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
build_proc_dir_entries(tpnt);
#endif
+
+ /*
+ * Add the kernel threads for each host adapter that will
+ * handle error correction.
+ */
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if( shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code )
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+
+ shpnt->eh_notify = &sem;
+ kernel_thread((int (*)(void *))scsi_error_handler,
+ (void *) shpnt, 0);
+
+ /*
+ * Now wait for the kernel error thread to initialize itself
+ * as it might be needed when we scan the bus.
+ */
+ down (&sem);
+ shpnt->eh_notify = NULL;
+ }
+ }
+
for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
if(shpnt->hostt == tpnt)
{
if(tpnt->info)
+ {
name = tpnt->info(shpnt);
+ }
else
+ {
name = tpnt->name;
+ }
printk ("scsi%d : %s\n", /* And print a little message */
shpnt->host_no, name);
}
+ }
printk ("scsi : %d host%s.\n", next_scsi_host,
(next_scsi_host == 1) ? "" : "s");
@@ -3052,26 +2669,36 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
/* The next step is to call scan_scsis here. This generates the
* Scsi_Devices entries
*/
-
for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
- if(shpnt->hostt == tpnt) {
- scan_scsis(shpnt,0,0,0,0);
- if (shpnt->select_queue_depths != NULL)
- (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ {
+ if(shpnt->hostt == tpnt)
+ {
+ scan_scsis(shpnt,0,0,0,0);
+ if (shpnt->select_queue_depths != NULL)
+ {
+ (shpnt->select_queue_depths)(shpnt, shpnt->host_queue);
+ }
}
+ }
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ {
if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+ }
- /* Next we create the Scsi_Cmnd structures for this host */
-
- for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
- if(SDpnt->host->hostt == tpnt)
- {
- for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
- if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
- }
+ /*
+ * Next we create the Scsi_Cmnd structures for this host
+ */
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
+ if(SDpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+ }
/*
* Now that we have all of the devices, resize the DMA pool,
@@ -3081,8 +2708,12 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
/* This does any final handling that is required. */
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ {
if(sdtpnt->finish && sdtpnt->nr_dev)
+ {
(*sdtpnt->finish)();
+ }
+ }
}
#if defined(USE_STATIC_SCSI_MEMORY)
@@ -3099,90 +2730,187 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
/*
* Similarly, this entry point should be called by a loadable module if it
* is trying to remove a low level scsi driver from the system.
+ *
+ * Note - there is a fatal flaw in the deregister module function.
+ * There is no way to return a code that says 'I cannot be unloaded now'.
+ * The system relies entirely upon usage counts that are maintained,
+ * and the assumption is that if the usage count is 0, then the module
+ * can be unloaded.
*/
static void scsi_unregister_host(Scsi_Host_Template * tpnt)
{
- Scsi_Host_Template * SHT, *SHTp;
- Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
- Scsi_Cmnd * SCpnt;
- unsigned long flags;
+ unsigned long flags;
+ int online_status;
+ int pcount;
+ Scsi_Cmnd * SCpnt;
+ Scsi_Device * SDpnt;
+ Scsi_Device * SDpnt1;
struct Scsi_Device_Template * sdtpnt;
- struct Scsi_Host * shpnt, *sh1;
- int pcount;
+ struct Scsi_Host * sh1;
+ struct Scsi_Host * shpnt;
+ Scsi_Host_Template * SHT;
+ Scsi_Host_Template * SHTp;
- /* First verify that this host adapter is completely free with no pending
- * commands */
+ /*
+ * First verify that this host adapter is completely free with no pending
+ * commands
+ */
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ if(SDpnt->host->hostt == tpnt
+ && SDpnt->host->hostt->module
+ && SDpnt->host->hostt->module->usecount) return;
+ /*
+ * FIXME(eric) - We need to find a way to notify the
+ * low level driver that we are shutting down - via the
+ * special device entry that still needs to get added.
+ *
+ * Is detach interface below good enough for this?
+ */
+ }
+ }
- for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
- if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->module
- && sdpnt->host->hostt->module->usecount) return;
+ /*
+ * FIXME(eric) put a spinlock on this. We force all of the devices offline
+ * to help prevent race conditions where other hosts/processors could try and
+ * get in and queue a command.
+ */
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ if(SDpnt->host->hostt == tpnt )
+ SDpnt->online = FALSE;
+
+ }
+ }
for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
{
- if (shpnt->hostt != tpnt) continue;
- for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
- {
- save_flags(flags);
- cli();
- if(SCpnt->request.rq_status != RQ_INACTIVE) {
- restore_flags(flags);
- for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
- if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
- SCpnt->request.rq_status = RQ_INACTIVE;
- printk("Device busy???\n");
- return;
+ if (shpnt->hostt != tpnt)
+ {
+ continue;
+ }
+
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ /*
+ * Loop over all of the commands associated with the device. If any of
+ * them are busy, then set the state back to inactive and bail.
+ */
+ for(SCpnt = SDpnt->device_queue; SCpnt;
+ SCpnt = SCpnt->next)
+ {
+ online_status = SDpnt->online;
+ SDpnt->online = FALSE;
+ save_flags(flags);
+ cli();
+ if(SCpnt->request.rq_status != RQ_INACTIVE)
+ {
+ restore_flags(flags);
+ printk("SCSI device not inactive - state=%d, id=%d\n",
+ SCpnt->request.rq_status, SCpnt->target);
+ for(SDpnt1 = shpnt->host_queue; SDpnt1;
+ SDpnt1 = SDpnt1->next)
+ {
+ for(SCpnt = SDpnt1->device_queue; SCpnt;
+ SCpnt = SCpnt->next)
+ if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ }
+ SDpnt->online = online_status;
+ printk("Device busy???\n");
+ return;
+ }
+ /*
+ * No, this device is really free. Mark it as such, and
+ * continue on.
+ */
+ SCpnt->state = SCSI_STATE_DISCONNECTING;
+ SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
+ restore_flags(flags);
}
- SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
- restore_flags(flags);
- }
+ }
}
/* Next we detach the high level drivers from the Scsi_Device structures */
- for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
- if(sdpnt->host->hostt == tpnt)
- {
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt != tpnt)
+ {
+ continue;
+ }
+
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
- /* If something still attached, punt */
- if (sdpnt->attached) {
- printk("Attached usage count = %d\n", sdpnt->attached);
- return;
- }
+ if(sdtpnt->detach) (*sdtpnt->detach)(SDpnt);
+
+ /* If something still attached, punt */
+ if (SDpnt->attached)
+ {
+ printk("Attached usage count = %d\n", SDpnt->attached);
+ return;
+ }
}
+ }
+
+ /*
+ * Next, kill the kernel error recovery thread for this host.
+ */
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if( shpnt->hostt == tpnt
+ && shpnt->hostt->use_new_eh_code
+ && shpnt->ehandler != NULL )
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+
+ shpnt->eh_notify = &sem;
+ send_sig(SIGKILL, shpnt->ehandler, 1);
+ down(&sem);
+ shpnt->eh_notify = NULL;
+ }
+ }
/* Next we free up the Scsi_Cmnd structures for this host */
- for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
- if(sdpnt->host->hostt == tpnt)
- while (sdpnt->host->host_queue) {
- SCpnt = sdpnt->host->host_queue->next;
- scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
- sdpnt->host->host_queue = SCpnt;
- if (SCpnt) SCpnt->prev = NULL;
- sdpnt->has_cmdblocks = 0;
- }
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt != tpnt)
+ {
+ continue;
+ }
- /* Next free up the Scsi_Device structures for this host */
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = shpnt->host_queue)
+ {
+ while (SDpnt->device_queue)
+ {
+ SCpnt = SDpnt->device_queue->next;
+ scsi_init_free((char *) SDpnt->device_queue, sizeof(Scsi_Cmnd));
+ SDpnt->device_queue = SCpnt;
+ }
+ SDpnt->has_cmdblocks = 0;
- sdppnt = NULL;
- for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
- {
- sdpnt1 = sdpnt->next;
- if (sdpnt->host->hostt == tpnt) {
- if (sdppnt)
- sdppnt->next = sdpnt->next;
- else
- scsi_devices = sdpnt->next;
- scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
- } else
- sdppnt = sdpnt;
+ /* Next free up the Scsi_Device structures for this host */
+ shpnt->host_queue = SDpnt->next;
+ scsi_init_free((char *) SDpnt, sizeof (Scsi_Device));
+
+ }
}
/* Next we go through and remove the instances of the individual hosts
* that were detected */
- shpnt = scsi_hostlist;
- while(shpnt) {
+ for(shpnt = scsi_hostlist; shpnt; shpnt = sh1)
+ {
sh1 = shpnt->next;
if(shpnt->hostt == tpnt) {
if(shpnt->loaded_as_module) {
@@ -3208,7 +2936,6 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
tpnt->present--;
}
}
- shpnt = sh1;
}
/*
@@ -3216,7 +2943,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
* to completely nuke the DMA pool. The resize operation will
* do the right thing and free everything.
*/
- if( !scsi_devices )
+ if( !scsi_hosts )
resize_dma_pool();
printk ("scsi : %d host%s.\n", next_scsi_host,
@@ -3260,7 +2987,8 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
*/
static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
{
- Scsi_Device * SDpnt;
+ Scsi_Device * SDpnt;
+ struct Scsi_Host * shpnt;
if (tpnt->next) return 1;
@@ -3269,8 +2997,14 @@ static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
* First scan the devices that we know about, and see if we notice them.
*/
- for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
- if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+ }
+ }
/*
* If any of the devices would match this driver, then perform the
@@ -3282,15 +3016,22 @@ static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
/*
* Now actually connect the devices to the new driver.
*/
- for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
{
- if(tpnt->attach) (*tpnt->attach)(SDpnt);
- /*
- * If this driver attached to the device, and we no longer
- * have anything attached, release the scsi command blocks.
- */
- if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
- scsi_build_commandblocks(SDpnt);
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ if(tpnt->attach) (*tpnt->attach)(SDpnt);
+ /*
+ * If this driver attached to the device, and don't have any
+ * command blocks for this device, allocate some.
+ */
+ if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
+ {
+ SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
+ }
+ }
}
/*
@@ -3306,6 +3047,7 @@ static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
{
Scsi_Device * SDpnt;
Scsi_Cmnd * SCpnt;
+ struct Scsi_Host * shpnt;
struct Scsi_Device_Template * spnt;
struct Scsi_Device_Template * prev_spnt;
@@ -3318,30 +3060,30 @@ static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
* Next, detach the devices from the driver.
*/
- for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
{
- if(tpnt->detach) (*tpnt->detach)(SDpnt);
- if(SDpnt->attached == 0)
- {
- /*
- * Nobody is using this device any more. Free all of the
- * command structures.
- */
- for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ for(SDpnt = shpnt->host_queue; SDpnt;
+ SDpnt = SDpnt->next)
+ {
+ if(tpnt->detach) (*tpnt->detach)(SDpnt);
+ if(SDpnt->attached == 0)
{
- if(SCpnt->device == SDpnt)
- {
- if(SCpnt->prev != NULL)
- SCpnt->prev->next = SCpnt->next;
- if(SCpnt->next != NULL)
- SCpnt->next->prev = SCpnt->prev;
- if(SCpnt == SDpnt->host->host_queue)
- SDpnt->host->host_queue = SCpnt->next;
+ SDpnt->online = FALSE;
+
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ for(SCpnt = SDpnt->device_queue; SCpnt;
+ SCpnt = SCpnt->next)
+ {
+ if(SCpnt == SDpnt->device_queue)
+ SDpnt->device_queue = SCpnt->next;
scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
- }
+ }
+ SDpnt->has_cmdblocks = 0;
}
- SDpnt->has_cmdblocks = 0;
- }
+ }
}
/*
* Extract the template from the linked list.
@@ -3369,26 +3111,27 @@ static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
int scsi_register_module(int module_type, void * ptr)
{
- switch(module_type){
+ switch(module_type)
+ {
case MODULE_SCSI_HA:
return scsi_register_host((Scsi_Host_Template *) ptr);
-
+
/* Load upper level device handler of some kind */
case MODULE_SCSI_DEV:
#ifdef CONFIG_KERNELD
if (scsi_hosts == NULL)
- request_module("scsi_hostadapter");
+ request_module("scsi_hostadapter");
#endif
return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
/* The rest of these are not yet implemented */
-
+
/* Load constants.o */
case MODULE_SCSI_CONST:
-
+
/* Load specialized ioctl handler for some device. Intended for
* cdroms that have non-SCSI2 audio command sets. */
case MODULE_SCSI_IOCTL:
-
+
default:
return 1;
}
@@ -3396,7 +3139,8 @@ int scsi_register_module(int module_type, void * ptr)
void scsi_unregister_module(int module_type, void * ptr)
{
- switch(module_type) {
+ switch(module_type)
+ {
case MODULE_SCSI_HA:
scsi_unregister_host((Scsi_Host_Template *) ptr);
break;
@@ -3414,65 +3158,123 @@ void scsi_unregister_module(int module_type, void * ptr)
#endif /* CONFIG_MODULES */
-#ifdef DEBUG_TIMEOUT
+/*
+ * Function: scsi_dump_status
+ *
+ * Purpose: Brain dump of scsi system, used for problem solving.
+ *
+ * Arguments: level - used to indicate level of detail.
+ *
+ * Notes: The level isn't used at all yet, but we need to find some way
+ * of sensibly logging varying degrees of information. A quick one-line
+ * display of each command, plus the status would be most useful.
+ *
+ * This does depend upon CONFIG_SCSI_LOGGING - I do want some way of turning
+ * it all off if the user wants a lean and mean kernel. It would probably
+ * also be useful to allow the user to specify one single host to be dumped.
+ * A second argument to the function would be useful for that purpose.
+ *
+ * FIXME - some formatting of the output into tables would be very handy.
+ */
static void
-scsi_dump_status(void)
+scsi_dump_status(int level)
{
+#if CONFIG_PROC_FS
+#if CONFIG_SCSI_LOGGING /* { */
int i;
struct Scsi_Host * shpnt;
Scsi_Cmnd * SCpnt;
- printk("Dump of scsi parameters:\n");
+ Scsi_Device * SDpnt;
+ printk("Dump of scsi host parameters:\n");
i = 0;
for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
- for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ printk(" %d %d %d : %d %p\n",
+ shpnt->host_failed,
+ shpnt->host_busy,
+ atomic_read(&shpnt->host_active),
+ shpnt->host_blocked,
+ shpnt->pending_commands);
+
+ }
+
+ printk("\n\n");
+ printk("Dump of scsi command parameters:\n");
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ printk("h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n");
+ for(SDpnt=shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
{
- /* (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */
- printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
- i++, SCpnt->host->host_no,
- SCpnt->channel,
- SCpnt->target,
- SCpnt->lun,
- kdevname(SCpnt->request.rq_dev),
- SCpnt->request.sector,
- SCpnt->request.nr_sectors,
- SCpnt->request.current_nr_sectors,
- SCpnt->use_sg,
- SCpnt->retries,
- SCpnt->allowed,
- SCpnt->flags,
- SCpnt->timeout_per_command,
- SCpnt->timeout,
- SCpnt->internal_timeout,
- SCpnt->cmnd[0],
- SCpnt->sense_buffer[2],
- SCpnt->result);
- }
- printk("wait_for_request = %p\n", wait_for_request);
- /* Now dump the request lists for each block device */
- printk("Dump of pending block device requests\n");
- for(i=0; i<MAX_BLKDEV; i++)
- if(blk_dev[i].current_request)
+ for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ /* (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x */
+ printk("(%3d) %2d:%1d:%2d:%2d (%6s %4ld %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n",
+ i++,
+
+ SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun,
+
+ kdevname(SCpnt->request.rq_dev),
+ SCpnt->request.sector,
+ SCpnt->request.nr_sectors,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->request.rq_status,
+ SCpnt->use_sg,
+
+ SCpnt->retries,
+ SCpnt->allowed,
+ SCpnt->flags,
+
+ SCpnt->timeout_per_command,
+ SCpnt->timeout,
+ SCpnt->internal_timeout,
+
+ SCpnt->cmnd[0],
+ SCpnt->sense_buffer[2],
+ SCpnt->result);
+ }
+ }
+ }
+
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ for(SDpnt=shpnt->host_queue; SDpnt; SDpnt = SDpnt->next)
{
- struct request * req;
- printk("%d: ", i);
- req = blk_dev[i].current_request;
- while(req) {
- printk("(%s %d %ld %ld %ld) ",
- kdevname(req->rq_dev),
- req->cmd,
- req->sector,
- req->nr_sectors,
- req->current_nr_sectors);
- req = req->next;
- }
- printk("\n");
- }
+ /* Now dump the request lists for each block device */
+ printk("Dump of pending block device requests\n");
+ for(i=0; i<MAX_BLKDEV; i++)
+ {
+ if(blk_dev[i].current_request)
+ {
+ struct request * req;
+ printk("%d: ", i);
+ req = blk_dev[i].current_request;
+ while(req)
+ {
+ printk("(%s %d %ld %ld %ld) ",
+ kdevname(req->rq_dev),
+ req->cmd,
+ req->sector,
+ req->nr_sectors,
+ req->current_nr_sectors);
+ req = req->next;
+ }
+ printk("\n");
+ }
+ }
+ }
+ }
+ printk("wait_for_request = %p\n", wait_for_request);
+#endif /* CONFIG_SCSI_LOGGING */ /* } */
+#endif /* CONFIG_PROC_FS */
}
-#endif
#ifdef MODULE
-int init_module(void) {
+int init_module(void)
+{
unsigned long size;
/*
@@ -3482,8 +3284,6 @@ int init_module(void) {
dispatch_scsi_info_ptr = dispatch_scsi_info;
#endif
- timer_table[SCSI_TIMER].fn = scsi_main_timeout;
- timer_table[SCSI_TIMER].expires = 0;
scsi_loadable_module_flag = 1;
/* Register the /proc/scsi/scsi entry */
@@ -3493,7 +3293,7 @@ int init_module(void) {
dma_sectors = PAGE_SIZE / SECTOR_SIZE;
- dma_free_sectors= dma_sectors;
+ scsi_dma_free_sectors= dma_sectors;
/*
* Set up a minimal DMA buffer list - this will be used during scan_scsis
* in some cases.
@@ -3509,12 +3309,20 @@ int init_module(void) {
scsi_init_malloc((dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages), GFP_ATOMIC);
dma_malloc_pages[0] = (unsigned char *)
scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+
+ /*
+ * This is where the processing takes place for most everything
+ * when commands are completed.
+ */
+ init_bh(SCSI_BH, scsi_bottom_half_handler);
+
return 0;
}
void cleanup_module( void)
{
- timer_active &= ~(1 << SCSI_TIMER);
+ remove_bh(SCSI_BH);
+
#if CONFIG_PROC_FS
proc_scsi_unregister(0, PROC_SCSI_SCSI);
@@ -3527,8 +3335,6 @@ void cleanup_module( void)
*/
resize_dma_pool();
- timer_table[SCSI_TIMER].fn = NULL;
- timer_table[SCSI_TIMER].expires = 0;
}
#endif /* MODULE */