patch-2.0.33 linux/drivers/scsi/aic7xxx.c
Next file: linux/drivers/scsi/aic7xxx.h
Previous file: linux/drivers/scsi/aic7xxx/aic7xxx.seq
Back to the patch index
Back to the overall index
- Lines: 3415
- Date:
Fri Dec 12 09:19:52 1997
- Orig file:
v2.0.32/linux/drivers/scsi/aic7xxx.c
- Orig date:
Tue Dec 2 13:52:32 1997
diff -u --recursive --new-file v2.0.32/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
@@ -139,7 +139,7 @@
0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "$Revision: 4.1 $"
+#define AIC7XXX_C_VERSION "$Revision: 4.1.1 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
@@ -681,7 +681,7 @@
/*
* Maximum number of SG segments these cards can support.
*/
-#define AIC7XXX_MAX_SG 27
+#define AIC7XXX_MAX_SG 122
/*
* The maximum number of SCBs we could have for ANY type
@@ -737,7 +737,9 @@
SCB_MSGOUT_SDTR = 0x0400,
SCB_MSGOUT_WDTR = 0x0800,
SCB_ABORT = 0x1000,
- SCB_QUEUED_ABORT = 0x2000
+ SCB_QUEUED_ABORT = 0x2000,
+ SCB_RESET = 0x4000,
+ SCB_WAS_BUSY = 0x8000
} scb_flag_type;
struct aic7xxx_scb {
@@ -810,10 +812,14 @@
#define USE_DEFAULTS 0x0080
#define BIOS_ENABLED 0x0100
#define IN_ISR 0x0200
-#define IN_TIMEOUT 0x0400
+#define ABORT_PENDING 0x0400
#define SHARED_SCBDATA 0x0800
#define HAVE_SEEPROM 0x1000
+#define RESET_PENDING 0x2000
+#define IN_ABORT 0x4000
unsigned int flags;
+ unsigned long last_reset;
+ unsigned long reset_start;
unsigned int isr_count; /* Interrupt count */
unsigned short needsdtr_copy; /* default config */
unsigned short needsdtr;
@@ -846,12 +852,21 @@
Scsi_Cmnd *tail;
} completeq;
struct aic7xxx_device_status {
+ unsigned char active_cmds;
+ unsigned char max_queue_depth;
+ unsigned char temp_queue_depth;
+ unsigned char last_queue_full;
+ unsigned char last_queue_full_count;
+ struct timer_list timer;
long last_reset;
#define DEVICE_SUCCESS 0x01
#define BUS_DEVICE_RESET_PENDING 0x02
+#define DEVICE_TIMEOUT 0x04
int flags;
int commands_sent;
- int active_cmds;
+ scb_queue_type delayed_scbs;
+ Scsi_Cmnd *scsi_cmnd0;
+ Scsi_Cmnd *scsi_cmnd1;
} device_status[16];
#ifdef AIC7XXX_PROC_STATS
/*
@@ -939,14 +954,50 @@
# define debug_scb(x)
#endif AIC7XXX_DEBUG
-#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
- (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \
+ (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
((scb->hscb)->target_channel_lun & 0x07)
-#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
- (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01), \
+ ((cmd->target) & 0x0f), \
+ ((cmd->lun) & 0x07)
+
+static inline int
+CHAN_TO_INT(char chan)
+{
+ switch(chan)
+ {
+ case ALL_CHANNELS:
+ return(-1);
+ case 'B':
+ case 'b':
+ return(1);
+ case 'A':
+ case 'a':
+ return(0);
+ default:
+ printk(KERN_WARNING "aic7xxx: Bad usage of char channel.\n");
+ return(0);
+ }
+}
+
+static inline char
+INT_TO_CHAN(int chan)
+{
+ switch(chan)
+ {
+ case -1:
+ return(ALL_CHANNELS);
+ case 1:
+ return('B');
+ case 0:
+ return('A');
+ default:
+ printk(KERN_WARNING "aic7xxx: Bad usage of int channel.\n");
+ return('A');
+ }
+}
-#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
@@ -1060,7 +1111,7 @@
}
else
{
- *(options[i].flag) = !0;
+ *(options[i].flag) += 1;
}
}
}
@@ -1448,10 +1499,10 @@
unsigned char ultra_enb, sxfrctl0;
/*
- * If the period is 0, then the device is requesting asynchronous
+ * If the offset is 0, then the device is requesting asynchronous
* transfers.
*/
- if (*period != 0)
+ if ((*period != 0) && (*offset != 0))
{
for (i = 0; i < num_aic7xxx_syncrates; i++)
{
@@ -1476,8 +1527,8 @@
if (aic7xxx_verbose)
{
- printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
- "offset %d.\n", p->host_no, target, channel,
+ printk("(scsi%d:%d:%d:%d) Synchronous at %sMHz, "
+ "offset %d.\n", p->host_no, CHAN_TO_INT(channel), target, 0,
aic7xxx_syncrates[i].english, *offset);
}
break;
@@ -1495,8 +1546,8 @@
*offset = 0;
if (aic7xxx_verbose)
{
- printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
- p->host_no, target, channel);
+ printk("(scsi%d:%d:%d:%d) Using asynchronous transfers.\n",
+ p->host_no, CHAN_TO_INT(channel), target, 0);
}
}
@@ -1550,10 +1601,14 @@
static inline void
scbq_insert_head(scb_queue_type *queue, struct aic7xxx_scb *scb)
{
+ unsigned long processor_flags;
+ save_flags(processor_flags);
+ cli();
scb->q_next = queue->head;
queue->head = scb;
if (queue->tail == NULL) /* If list was empty, update tail. */
queue->tail = queue->head;
+ restore_flags(processor_flags);
}
/*+F*************************************************************************
@@ -1567,10 +1622,14 @@
static inline void
scbq_remove_head(scb_queue_type *queue)
{
+ unsigned long processor_flags;
+ save_flags(processor_flags);
+ cli();
if (queue->head != NULL)
queue->head = queue->head->q_next;
if (queue->head == NULL) /* If list is now empty, update tail. */
queue->tail = NULL;
+ restore_flags(processor_flags);
}
/*+F*************************************************************************
@@ -1584,6 +1643,9 @@
static inline void
scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
{
+ unsigned long processor_flags;
+ save_flags(processor_flags);
+ cli();
if (queue->head == scb)
{
/* At beginning of queue, remove from head. */
@@ -1612,6 +1674,7 @@
}
}
}
+ restore_flags(processor_flags);
}
/*+F*************************************************************************
@@ -1625,6 +1688,9 @@
static inline void
scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
{
+ unsigned long processor_flags;
+ save_flags(processor_flags);
+ cli();
scb->q_next = NULL;
if (queue->tail != NULL) /* Add the scb at the end of the list. */
queue->tail->q_next = scb;
@@ -1632,6 +1698,7 @@
queue->tail = scb; /* Update the tail. */
if (queue->head == NULL) /* If list was empty, update head. */
queue->head = queue->tail;
+ restore_flags(processor_flags);
}
/*+F*************************************************************************
@@ -1653,10 +1720,6 @@
int slun = scb->hscb->target_channel_lun & 0x07;
int match;
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
- scb->cmd->device->host->host_no, target, channel, targ, chan);
-#endif
match = ((chan == channel) || (channel == ALL_CHANNELS));
if (match != 0)
match = ((targ == target) || (target == ALL_TARGETS));
@@ -1665,6 +1728,24 @@
if (match != 0)
match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+ if (aic7xxx_verbose > 4)
+ {
+ if (match)
+ {
+ printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) matches search criteria"
+ " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
+ CHAN_TO_INT(channel), target, lun, tag);
+ }
+ else
+ {
+ printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) doesn't match search criteria"
+ " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
+ CHAN_TO_INT(channel), target, lun, tag);
+ }
+ }
+
return (match);
}
@@ -1707,6 +1788,7 @@
prev = inb(p->base + SCB_PREV);
outb(0, p->base + SCB_CONTROL);
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
aic7xxx_add_curscb_to_free_list(p);
@@ -1834,12 +1916,6 @@
{
struct aic7xxx_scb *scbp = NULL;
struct aic7xxx_hwscb *hscbp = NULL;
-#ifdef AGRESSIVE
- long processor_flags;
-
- save_flags(processor_flags);
- cli();
-#endif
scbp = p->scb_data->free_scbs.head;
if (scbp != NULL)
@@ -1871,16 +1947,7 @@
}
}
}
-#ifdef AIC7XXX_DEBUG
- if (scbp != NULL)
- {
- p->activescbs++;
- }
-#endif
-#ifdef AGRESSIVE
- restore_flags(processor_flags);
-#endif
return (scbp);
}
@@ -1896,11 +1963,12 @@
static inline void
aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
- if (p->completeq.tail == NULL)
- p->completeq.head = cmd;
- else
- p->completeq.tail->host_scribble = (char *) cmd;
- p->completeq.tail = cmd;
+ unsigned int flags;
+ save_flags(flags);
+ cli();
+ cmd->host_scribble = (char *)p->completeq.head;
+ p->completeq.head = cmd;
+ restore_flags(flags);
}
/*+F*************************************************************************
@@ -1914,16 +1982,20 @@
aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
{
Scsi_Cmnd *cmd;
-
+ unsigned int processor_flags;
+
+ save_flags(processor_flags);
+ cli();
while (p->completeq.head != NULL)
{
cmd = p->completeq.head;
p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
cmd->host_scribble = NULL;
- p->device_status[TARGET_INDEX(cmd)].active_cmds--;
+ restore_flags(processor_flags);
cmd->scsi_done(cmd);
+ cli();
}
- p->completeq.tail = NULL;
+ restore_flags(processor_flags);
}
/*+F*************************************************************************
@@ -1937,11 +2009,8 @@
aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
struct aic7xxx_hwscb *hscb;
- long flags;
hscb = scb->hscb;
- save_flags(flags);
- cli();
scb->flags = SCB_FREE;
scb->cmd = NULL;
@@ -1949,11 +2018,6 @@
hscb->target_status = 0;
scbq_insert_head(&p->scb_data->free_scbs, scb);
-#ifdef AIC7XXX_DEBUG
- p->activescbs--; /* For debugging purposes. */
-#endif
-
- restore_flags(flags);
}
/*+F*************************************************************************
@@ -1967,17 +2031,23 @@
aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
Scsi_Cmnd *cmd = scb->cmd;
+ int tindex = TARGET_INDEX(cmd);
+ struct aic7xxx_scb *scbp;
+ unsigned char queue_depth;
if (scb->flags & SCB_RECOVERY_SCB)
{
- p->flags &= ~IN_TIMEOUT;
+ p->flags &= ~ABORT_PENDING;
}
- if (cmd->result == DID_OK)
+ if (scb->flags & SCB_RESET)
{
- if (scb->flags & SCB_ABORTED)
- {
- cmd->result = (DID_RESET << 16);
- }
+ cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) |
+ (cmd->result & 0xffff);
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) |
+ (cmd->result & 0xffff);
}
if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
{
@@ -1985,7 +2055,7 @@
int message_error = FALSE;
mask = 0x01 << TARGET_INDEX(scb->cmd);
-
+
/*
* Check to see if we get an invalid message or a message error
* after failing to negotiate a wide or sync transfer message.
@@ -2016,6 +2086,34 @@
}
}
}
+ queue_depth = (p->device_status[tindex].timer.expires) ?
+ p->device_status[tindex].temp_queue_depth :
+ p->device_status[tindex].max_queue_depth;
+ scbp = p->device_status[tindex].delayed_scbs.head;
+ if ( (scbp != NULL) &&
+ (queue_depth > p->device_status[tindex].active_cmds) )
+ {
+ scbq_remove_head(&p->device_status[tindex].delayed_scbs);
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ scbp = p->device_status[tindex].delayed_scbs.head;
+ if ( (scbp != NULL) &&
+ (queue_depth > (p->device_status[tindex].active_cmds + 1)) )
+ {
+ scbq_remove_head(&p->device_status[tindex].delayed_scbs);
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ }
+ }
+ if ( (p->device_status[tindex].timer.expires) &&
+ ((p->device_status[tindex].active_cmds == 1) ||
+ (p->device_status[tindex].max_queue_depth ==
+ p->device_status[tindex].temp_queue_depth)) )
+ {
+ del_timer(&p->device_status[tindex].timer);
+ p->device_status[tindex].timer.expires = 0;
+ p->device_status[tindex].temp_queue_depth =
+ p->device_status[tindex].max_queue_depth;
+ }
+ p->device_status[tindex].active_cmds--;
aic7xxx_free_scb(p, scb);
aic7xxx_queue_cmd_complete(p, cmd);
@@ -2030,8 +2128,7 @@
* XXX: for each command, but apparently that's too difficult.
*/
actual = aic7xxx_length(cmd, 0);
- if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
- && (aic7xxx_error(cmd) == 0))
+ if ((actual > 0) && (((cmd->result >> 16) & 0xf) == DID_OK))
{
struct aic7xxx_xferstats *sp;
long *ptr;
@@ -2082,20 +2179,25 @@
aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
{
struct aic7xxx_scb *scb;
- int i;
+ int i, found = 0;
for (i = 0; i < p->scb_data->numscbs; i++)
{
scb = p->scb_data->scb_array[i];
if (scb->flags & SCB_QUEUED_FOR_DONE)
{
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("(scsi%d:%d:%d) Aborting scb %d\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
-#endif
+ if (aic7xxx_verbose > 3)
+ printk("(scsi%d:%d:%d:%d) Aborting scb %d\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+ found++;
aic7xxx_done(p, scb);
}
}
+ if (aic7xxx_verbose > 1)
+ {
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) %d commands found and queued for "
+ "completion.\n", p->host_no, found);
+ }
if (complete)
{
aic7xxx_done_cmds_complete(p);
@@ -2127,6 +2229,7 @@
* Clear the necessary fields
*/
outb(0, p->base + SCB_CONTROL);
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
aic7xxx_add_curscb_to_free_list(p);
@@ -2153,9 +2256,8 @@
* system that the command has been aborted.
*/
outb(curscb, p->base + SCBPTR);
- scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scb->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
scb->flags &= ~SCB_ACTIVE;
- scb->cmd->result = (DID_RESET << 16);
return (next);
}
@@ -2170,7 +2272,7 @@
*-F*************************************************************************/
static int
aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
- int lun, unsigned char tag, int flags, int requeue)
+ int lun, unsigned char tag, int flags, int requeue, scb_queue_type *queue)
{
unsigned char saved_queue[AIC7XXX_MAXSCB];
int queued = inb(p->base + QINCNT) & p->qcntmask;
@@ -2196,12 +2298,7 @@
}
else
{
- scbp->flags = flags;
- scbp->flags &= ~SCB_ACTIVE;
- /*
- * XXX - Don't know what error to use here.
- */
- aic7xxx_error(scbp->cmd) = DID_RESET;
+ scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
}
i--;
found++;
@@ -2220,8 +2317,12 @@
/*
* XXX - Shouldn't we be adding this to the free list?
*/
- scbq_insert_head(&p->waiting_scbs, scbp);
- scbp->flags |= SCB_WAITINGQ;
+ if ( !(scbp->flags & SCB_WAITINGQ) )
+ { /* OK...we aren't already on a queue, so put us on there */
+ p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds--;
+ scbq_insert_head(queue, scbp);
+ scbp->flags |= SCB_WAITINGQ;
+ }
scbp = removed_scbs.head;
}
}
@@ -2235,33 +2336,38 @@
*
* Description:
* The device at the given target/channel has been reset. Abort
- * all active and queued scbs for that target/channel.
+ * all active and queued scbs for that target/channel. This function
+ * need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ * then we had a tagged command (no linked next), if it was MSG_ABORT or
+ * MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ * and no busy commands will exist, and if it was a bus reset, then nothing
+ * knows about any linked next commands any more. In all cases, we don't
+ * need to worry about the linked next or busy scb, we just need to clear
+ * them.
*-F*************************************************************************/
-static int
+static void
aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
int lun, unsigned char tag)
{
struct aic7xxx_scb *scbp;
unsigned char active_scb;
- int i = 0;
- int found;
+ int i = 0, j, init_lists = FALSE;
/*
* Restore this when we're done
*/
active_scb = inb(p->base + SCBPTR);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
- p->host_no, target, CHAN_TO_INT(channel), active_scb);
-#endif
-
+ if (aic7xxx_verbose > 2)
+ printk("(scsi%d:%d:%d:%d) Reset device, active_scb %d\n",
+ p->host_no, CHAN_TO_INT(channel), target, lun, active_scb);
/*
* Deal with the busy target and linked next issues.
*/
{
int min_target, max_target;
unsigned char busy_scbid;
+ struct aic7xxx_scb *scbp, *prev_scbp;
/* Make all targets 'relative' to bus A. */
if (target == ALL_TARGETS)
@@ -2285,103 +2391,166 @@
}
else
{
- min_target = target + channel == 'B' ? 8 : 0;
+ min_target = target + ((channel == 'B') ? 8 : 0);
max_target = min_target;
}
+
for (i = min_target; i <= max_target; i++)
{
- busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
- if (busy_scbid < p->scb_data->numscbs)
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning up status information "
+ "and delayed_scbs.\n", p->host_no, CHAN_TO_INT(channel), i, lun);
+ busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/ TRUE);
+ p->device_status[i].flags &= ~BUS_DEVICE_RESET_PENDING;
+ p->device_status[i].last_reset = jiffies;
+ p->device_status[i].last_queue_full_count = 0;
+ p->device_status[i].last_queue_full = 0;
+ p->device_status[i].temp_queue_depth =
+ p->device_status[i].max_queue_depth;
+ j = 0;
+ prev_scbp = NULL;
+ scbp = p->device_status[i].delayed_scbs.head;
+ while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if ( prev_scbp == scbp )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! scb->q_next == scb "
+ "in the delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
+ i, lun);
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->device_status[i].delayed_scbs.tail = prev_scbp;
+ }
+ if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&p->device_status[i].delayed_scbs, prev_scbp);
+ if ( prev_scbp->flags & SCB_WAITINGQ )
+ p->device_status[i].active_cmds++;
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ }
+ if ( j > p->scb_data->numscbs )
{
- struct aic7xxx_scb *busy_scb;
- struct aic7xxx_scb *next_scb;
- unsigned char next_scbid;
-
- busy_scb = p->scb_data->scb_array[busy_scbid];
-
- next_scbid = busy_scb->hscb->data_count >> 24;
-
- if (next_scbid == SCB_LIST_NULL)
- {
- busy_scbid = aic7xxx_find_scb(p, busy_scb);
-
- if (busy_scbid != SCB_LIST_NULL)
- {
- outb(busy_scbid, p->base + SCBPTR);
- next_scbid = inb(p->base + SCB_LINKED_NEXT);
- }
- }
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! There's a loop in the "
+ "delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
+ i, lun);
+ scbq_init(&p->device_status[i].delayed_scbs);
+ }
+ if ( (p->device_status[i].delayed_scbs.head == NULL) &&
+ (p->device_status[i].timer.expires) )
+ {
+ del_timer(&p->device_status[i].timer);
+ p->device_status[i].timer.expires = 0;
+ }
+ }
+ }
- if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
- {
- aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
- }
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning QINFIFO.\n", p->host_no,
+ CHAN_TO_INT(channel), target, lun );
+ aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+ SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
- if (next_scbid != SCB_LIST_NULL)
- {
- next_scb = p->scb_data->scb_array[next_scbid];
- if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
- {
- continue;
- }
- /* Requeue for later processing */
- scbq_insert_head(&p->waiting_scbs, next_scb);
- next_scb->flags |= SCB_WAITINGQ;
- }
+/*
+ * Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ * ABORT/RESET commands.
+ */
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting_scbs.\n",
+ p->host_no, CHAN_TO_INT(channel), target, lun );
+ {
+ struct aic7xxx_scb *scbp, *prev_scbp;
+
+ j = 0;
+ prev_scbp = NULL;
+ scbp = p->waiting_scbs.head;
+ while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if ( prev_scbp == scbp )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! scb->q_next == scb "
+ "in the waiting_scbs queue!\n", p->host_no);
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->waiting_scbs.tail = prev_scbp;
+ }
+ if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ if ( prev_scbp->flags & SCB_WAITINGQ )
+ p->device_status[TARGET_INDEX(prev_scbp->cmd)].active_cmds++;
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
}
}
+ if ( j > p->scb_data->numscbs )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There's a loop in the "
+ "waiting_scbs queue!\n", p->host_no);
+ scbq_init(&p->waiting_scbs);
+ }
}
- found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
- SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
/*
* Search waiting for selection list.
*/
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting for selection "
+ "list.\n", p->host_no, CHAN_TO_INT(channel), target, lun);
{
unsigned char next, prev, scb_index;
next = inb(p->base + WAITING_SCBH); /* Start at head of list. */
prev = SCB_LIST_NULL;
-
- while (next != SCB_LIST_NULL)
+ j = 0;
+ while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
{
outb(next, p->base + SCBPTR);
scb_index = inb(p->base + SCB_TAG);
if (scb_index >= p->scb_data->numscbs)
{
- panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
- scb_index, p->scb_data->numscbs);
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
+ "SCB index=%d, numscbs=%d\n", p->host_no,
+ CHAN_TO_INT(channel), target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = inb(p->base + SCB_NEXT);
+ aic7xxx_add_curscb_to_free_list(p);
}
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ else
{
- unsigned char linked_next;
-
- next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
- linked_next = inb(p->base + SCB_LINKED_NEXT);
- if (linked_next != SCB_LIST_NULL)
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- struct aic7xxx_scb *next_scb;
- /*
- * Requeue the waiting SCB via the waiting list.
- */
- next_scb = p->scb_data->scb_array[linked_next];
- if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
- {
- scbq_insert_head(&p->waiting_scbs, next_scb);
- next_scb->flags |= SCB_WAITINGQ;
- }
+ next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+ scbp->flags &= ~SCB_ACTIVE;
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ else
+ {
+ prev = next;
+ next = inb(p->base + SCB_NEXT);
}
- found++;
- }
- else
- {
- prev = next;
- next = inb(p->base + SCB_NEXT);
}
}
+ if ( j > p->scb_data->maxhscbs )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
+ "waiting for selection list!\n", p->host_no);
+ init_lists = TRUE;
+ }
}
/*
@@ -2393,45 +2562,106 @@
next = inb(p->base + DISCONNECTED_SCBH);
prev = SCB_LIST_NULL;
-
- while (next != SCB_LIST_NULL)
+ j = 0;
+ while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
{
outb(next, p->base + SCBPTR);
scb_index = inb(p->base + SCB_TAG);
if (scb_index > p->scb_data->numscbs)
{
- panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
- "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
- }
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
- {
- next = aic7xxx_rem_scb_from_disc_list(p, next);
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
+ "SCB index=%d, numscbs=%d\n", p->host_no,
+ CHAN_TO_INT(channel), target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
}
else
{
- prev = next;
- next = inb(p->base + SCB_NEXT);
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
+ scbp->flags = SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->hscb->control = 0;
+ }
+ else
+ {
+ prev = next;
+ next = inb(p->base + SCB_NEXT);
+ }
}
}
+ if ( j > p->scb_data->maxhscbs )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
+ "disconnected list!\n", p->host_no);
+ init_lists = TRUE;
+ }
+ }
+
+ /*
+ * Walk the free list making sure no entries on the free list have
+ * a valid SCB_TAG value or SCB_CONTROL byte.
+ */
+ {
+ unsigned char next;
+
+ j = 0;
+ next = inb(p->base + FREE_SCBH);
+ while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
+ {
+ outb(next, p->base + SCBPTR);
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ outb(0, p->base + SCB_CONTROL);
+ next = inb(p->base + SCB_NEXT);
+ }
+ if ( j > p->scb_data->maxhscbs )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
+ "free list!\n", p->host_no);
+ init_lists = TRUE;
+ }
}
/*
* Go through the hardware SCB array looking for commands that
* were active but not on any list.
*/
- for (i = 0; i < p->scb_data->maxhscbs; i++)
+ if (init_lists)
+ {
+ outb(SCB_LIST_NULL, p->base + FREE_SCBH);
+ outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+ outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+ }
+ for (i = p->scb_data->maxhscbs; i >= 0; --i)
{
unsigned char scbid;
- outb(i, p->base + SCBPTR);
- scbid = inb(p->base + SCB_TAG);
- if (scbid < p->scb_data->numscbs)
+ if (init_lists)
+ {
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+ outb(SCB_LIST_NULL, p->base + SCB_PREV);
+ outb(SCB_LIST_NULL, p->base + SCB_LINKED_NEXT);
+ outb(0, p->base + SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ else
{
- scbp = p->scb_data->scb_array[scbid];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ outb(i, p->base + SCBPTR);
+ scbid = inb(p->base + SCB_TAG);
+ if (scbid < p->scb_data->numscbs)
{
- aic7xxx_add_curscb_to_free_list(p);
+ scbp = p->scb_data->scb_array[scbid];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ outb(0, p->base + SCB_CONTROL);
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
}
}
}
@@ -2447,24 +2677,24 @@
if (((scbp->flags & SCB_ACTIVE) != 0) &&
aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
scbp->flags &= ~SCB_ACTIVE;
- aic7xxx_error(scbp->cmd) = DID_RESET;
-
- found++;
if ((scbp->flags & SCB_WAITINGQ) != 0)
{
scbq_remove(&p->waiting_scbs, scbp);
+ scbq_remove(&p->device_status[TARGET_INDEX(scbp->cmd)].delayed_scbs,
+ scbp);
scbp->flags &= ~SCB_WAITINGQ;
+ p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds++;
}
}
}
outb(active_scb, p->base + SCBPTR);
- return (found);
}
+
/*+F*************************************************************************
* Function:
* aic7xxx_clear_intstat
@@ -2521,20 +2751,18 @@
* Description:
* Reset the channel.
*-F*************************************************************************/
-static int
+static void
aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
{
unsigned long offset, offset_max;
- int found;
unsigned char sblkctl;
char cur_channel;
- pause_sequencer(p);
- /*
- * Clean up all the state information for the pending transactions
- * on this bus.
- */
- found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:-1:-1) Reset channel called, %s initiate "
+ "reset.\n", p->host_no, CHAN_TO_INT(channel),
+ (initiate_reset == TRUE) ? "will" : "won't" );
+
if (channel == 'B')
{
@@ -2587,10 +2815,9 @@
/*
* Case 1: Command for another bus is active
*/
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: Stealthily resetting channel %c\n",
- p->host_no, channel);
-#endif
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:-1:-1) Stealthily resetting idle "
+ "channel.\n", p->host_no, CHAN_TO_INT(channel));
/*
* Stealthily reset the other bus without upsetting the current bus.
*/
@@ -2599,52 +2826,50 @@
if (initiate_reset)
{
aic7xxx_reset_current_bus(p);
- /*
- * Cause the mid-level SCSI code to delay any further
- * queueing by the bus settle time for us.
- */
- p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
}
outb(0, p->base + SCSISEQ);
aic7xxx_clear_intstat(p);
outb(sblkctl, p->base + SBLKCTL);
- unpause_sequencer(p, /* unpause_always */ FALSE);
}
else
{
/*
* Case 2: A command from this bus is active or we're idle.
*/
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: Resetting current channel %c\n",
- p->host_no, channel);
-#endif
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:-1:-1) Resetting currently active "
+ "channel.\n", p->host_no, CHAN_TO_INT(channel));
outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
aic7xxx_reset_current_bus(p);
- /*
- * Cause the mid-level SCSI code to delay any further
- * queueing by the bus settle time for us.
- */
-#if 0
- p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-#endif
}
outb(0, p->base + SCSISEQ);
aic7xxx_clear_intstat(p);
+ }
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:-1:-1) Channel reset\n",
+ p->host_no, CHAN_TO_INT(channel));
+ /*
+ * Clean up all the state information for the pending transactions
+ * on this bus.
+ */
+ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+ if ( p->bus_type != AIC_TWIN )
+ {
restart_sequencer(p);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
-#endif
+ pause_sequencer(p);
}
+ p->host->last_reset = jiffies + (HZ * AIC7XXX_RESET_DELAY);
+
/*
* Now loop through all the SCBs that have been marked for abortion,
* and call the scsi_done routines.
*/
aic7xxx_run_done_queue(p, /*complete*/ TRUE);
- return (found);
+ return;
}
/*+F*************************************************************************
@@ -2659,14 +2884,16 @@
aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
{
struct aic7xxx_scb *scb;
+ int tindex;
+
if (p->waiting_scbs.head == NULL)
return;
- pause_sequencer(p);
/*
* First handle SCBs that are waiting but have been assigned a slot.
*/
+ pause_sequencer(p);
scb = p->waiting_scbs.head;
while (scb != NULL)
{
@@ -2682,26 +2909,73 @@
/*
* We have some space.
*/
- scbq_remove_head(&(p->waiting_scbs));
- scb->flags &= ~SCB_WAITINGQ;
-
- outb(scb->hscb->tag, p->base + QINFIFO);
-
- if ((p->flags & PAGE_ENABLED) != 0)
+ scbq_remove_head(&p->waiting_scbs);
+ tindex = TARGET_INDEX(scb->cmd);
+ if ( p->device_status[tindex].active_cmds >=
+ p->device_status[tindex].temp_queue_depth )
{
- /*
- * We only care about this statistic when paging
- * since it's impossible to overflow the qinfifo
- * in the non-paging case.
- */
- p->curqincnt++;
+ scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+ }
+ else
+ {
+ scb->flags &= ~SCB_WAITINGQ;
+ p->device_status[tindex].active_cmds++;
+ outb(scb->hscb->tag, p->base + QINFIFO);
+ p->curqincnt++;
}
scb = p->waiting_scbs.head;
}
-
unpause_sequencer(p, FALSE);
}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_timer
+ *
+ * Description:
+ * Take expired extries off of delayed queues and place on waiting queue
+ * then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+ int i;
+ unsigned long processor_flags;
+ struct aic7xxx_scb *scb;
+
+ save_flags(processor_flags);
+ cli();
+ if (aic7xxx_verbose > 3)
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Timer running.\n", p->host_no);
+ for(i=0; i<MAX_TARGETS; i++)
+ {
+ if ( (p->device_status[i].timer.expires) &&
+ (p->device_status[i].timer.expires <= jiffies) )
+ {
+ p->device_status[i].timer.expires = 0;
+ if ( (p->device_status[i].timer.prev != NULL) ||
+ (p->device_status[i].timer.next != NULL) )
+ {
+ del_timer(&p->device_status[i].timer);
+ }
+ p->device_status[i].temp_queue_depth =
+ p->device_status[i].max_queue_depth;
+ scb = p->device_status[i].delayed_scbs.head;
+ while ( scb != NULL )
+ {
+ scbq_remove_head(&p->device_status[i].delayed_scbs);
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ scb = p->device_status[i].delayed_scbs.head;
+ }
+ }
+ }
+ aic7xxx_run_waiting_queues(p);
+ unpause_sequencer(p, FALSE);
+ restore_flags(processor_flags);
+}
+
+
/*+F*************************************************************************
* Function:
* aic7xxx_construct_sdtr
@@ -2777,11 +3051,11 @@
(hscb->residual_data_count[1] << 8) |
hscb->residual_data_count[0];
- if (actual < cmd->underflow)
+ if ( (actual < cmd->underflow) && (aic7xxx_verbose) )
{
- printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Underflow - "
"Wanted at least %u, got %u, residual SG count %d.\n",
- p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+ p->host_no, CTL_OF_SCB(scb), cmd->underflow, actual,
hscb->residual_SG_segment_count);
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
aic7xxx_status(cmd) = hscb->target_status;
@@ -2811,7 +3085,6 @@
unsigned short targ_mask;
unsigned char targ_scratch;
int scratch_offset = target;
- int found;
if (channel == 'B')
{
@@ -2828,9 +3101,10 @@
targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
targ_scratch &= SXFR;
outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
- found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
- printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
- "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:-1) Bus Device Reset delivered.\n",
+ p->host_no, CHAN_TO_INT(channel), target);
aic7xxx_run_done_queue(p, /*complete*/ TRUE);
}
@@ -2846,7 +3120,8 @@
{
struct aic7xxx_scb *scb;
unsigned short target_mask;
- unsigned char target, scratch_offset;
+ unsigned char target, scratch_offset, lun;
+ unsigned char queue_flag = FALSE;
char channel;
if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
@@ -2859,6 +3134,7 @@
}
scratch_offset = target;
channel = 'A';
+ lun = inb(p->base + SAVED_TCL) & 0x07;
if (inb(p->base + SBLKCTL) & SELBUSB)
{
channel = 'B';
@@ -2900,19 +3176,20 @@
{
/*
* We expected this. Let the busfree handler take care
- * of this when we the abort is finially sent. Set
+ * of this when we the abort is finally sent. Set
* IDENTIFY_SEEN so that the busfree handler knows that
* there is an SCB to cleanup.
*/
outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
- printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
- p->host_no, TC_OF_SCB(scb));
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reconnect SCB abort "
+ "successful\n", p->host_no, CTL_OF_SCB(scb));
break;
}
}
- printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) No active SCB for reconnecting "
"target - Issuing BUS DEVICE RESET.\n",
- p->host_no, target, CHAN_TO_INT(channel));
+ p->host_no, CHAN_TO_INT(channel), target, lun);
printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
inb(p->base + SAVED_TCL), arg1,
@@ -2932,9 +3209,9 @@
scb_index = inb(p->base + CUR_SCBID);
scb = p->scb_data->scb_array[scb_index];
- panic("scsi%d: Target %d, channel %c, Target busy link failure, "
+ panic("(scsi%d:%d:%d:%d) Target busy link failure, "
"but busy SCB exists!\n",
- p->host_no, target, channel);
+ p->host_no, CHAN_TO_INT(channel), target, lun );
}
break;
@@ -2943,10 +3220,11 @@
unsigned char rej_byte;
rej_byte = inb(p->base + REJBYTE);
- printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
- "received from target, SEQ_FLAGS=0x%x\n",
- p->host_no, target, CHAN_TO_INT(channel), rej_byte,
- inb(p->base + SEQ_FLAGS));
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting unknown message "
+ "(0x%x) received from target, SEQ_FLAGS=0x%x\n",
+ p->host_no, CHAN_TO_INT(channel), target, lun,
+ rej_byte, inb(p->base + SEQ_FLAGS));
}
break;
@@ -2959,31 +3237,28 @@
* The only safe thing to do is to blow it away with a bus
* reset.
*/
- int found;
-
- printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
- "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
- p->host_no, target, CHAN_TO_INT(channel),
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target did not send an "
+ "IDENTIFY message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+ p->host_no, CHAN_TO_INT(channel), target, lun,
inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
- found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+ aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
- printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
- "%d SCBs aborted\n", p->host_no, channel, found);
}
break;
case BAD_PHASE:
if (inb(p->base + LASTPHASE) == P_BUSFREE)
{
- printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
- p->host_no, CHAN_TO_INT(channel), target);
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Missed busfree.\n",
+ p->host_no, CHAN_TO_INT(channel), target, lun);
restart_sequencer(p);
}
else
{
- printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
- "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unknown scsi bus phase, "
+ "continuing\n", p->host_no, CHAN_TO_INT(channel), target, lun);
}
break;
@@ -3054,11 +3329,15 @@
}
else
{
- /* We went too low - force async. */
+ /*
+ * We went too low - force async and disable future
+ * sync negotiation
+ */
+ p->needsdtr_copy &= ~target_mask;
outb(SEND_REJ, p->base + RETURN_1);
}
}
- else
+ else if ( offset != 0 )
{
/*
* Send our own SDTR in reply.
@@ -3066,14 +3345,26 @@
* We want to see this message as we don't expect a target
* to send us a SDTR request first.
*/
- printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Sending reply SDTR.\n",
+ p->host_no, CTL_OF_SCB(scb));
aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
outb(SEND_MSG, p->base + RETURN_1);
}
+ else
+ {
+ /*
+ * The incoming SDTR was too low, reject it.
+ */
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting SDTR request.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ outb(SEND_REJ, p->base + RETURN_1);
+ p->needsdtr_copy &= ~target_mask;
+ }
/*
* Clear the flags.
*/
p->needsdtr &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
break;
}
@@ -3099,14 +3390,15 @@
switch (bus_width)
{
case BUS_8_BIT:
+ p->needwdtr_copy &= ~target_mask;
scratch &= 0x7F;
break;
case BUS_16_BIT:
if (aic7xxx_verbose)
{
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
- "bit transfers.\n", p->host_no, target, channel);
+ printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
+ "transfers.\n", p->host_no, CTL_OF_SCB(scb));
}
scratch |= WIDEXFER;
break;
@@ -3114,9 +3406,9 @@
case BUS_32_BIT:
outb(SEND_REJ, p->base + RETURN_1);
/* No verbose here! We want to see this condition. */
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
"requesting 32 bit transfers, rejecting...\n",
- p->host_no, target, channel);
+ p->host_no, CTL_OF_SCB(scb));
break;
default:
@@ -3133,6 +3425,7 @@
switch (bus_width)
{
case BUS_8_BIT:
+ p->needwdtr_copy &= ~target_mask;
scratch &= 0x7F;
break;
@@ -3140,8 +3433,8 @@
case BUS_16_BIT:
if (p->bus_type == AIC_WIDE)
{
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
- "bit transfers.\n", p->host_no, target, channel);
+ printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
+ "transfers.\n", p->host_no, CTL_OF_SCB(scb));
bus_width = BUS_16_BIT;
scratch |= WIDEXFER;
}
@@ -3159,9 +3452,9 @@
if (send_reject)
{
outb(SEND_REJ, p->base + RETURN_1);
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, initiating "
- "wide negotiation on a narrow bus - rejecting!\n",
- p->host_no, target, channel);
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target initiated "
+ "wide negotiation on a narrow bus - rejecting!",
+ p->host_no, CTL_OF_SCB(scb));
}
else
{
@@ -3170,6 +3463,7 @@
}
}
p->needwdtr &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
outb(scratch, p->base + SCSIRATE);
break;
@@ -3206,9 +3500,12 @@
*/
targ_scratch &= 0x7F;
p->needwdtr &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+ p->needwdtr_copy &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing WIDE "
"negotiation; using 8 bit transfers.\n",
- p->host_no, target, channel);
+ p->host_no, CTL_OF_SCB(scb));
}
else
{
@@ -3219,9 +3516,12 @@
*/
targ_scratch &= 0xF0;
p->needsdtr &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+ p->needsdtr_copy &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing "
"synchronous negotiation; using asynchronous transfers.\n",
- p->host_no, target, channel);
+ p->host_no, CTL_OF_SCB(scb));
}
/*
* Otherwise, we ignore it.
@@ -3256,9 +3556,10 @@
outb(0, p->base + RETURN_1);
if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Invalid SCB during "
"SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
- intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
+ CHAN_TO_INT(channel), target, lun, intstat, scb_index,
+ scb->flags, (unsigned int) scb->cmd);
}
else
{
@@ -3271,8 +3572,8 @@
switch (status_byte(hscb->target_status))
{
case GOOD:
- printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
- "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Interrupted for status of "
+ "GOOD???\n", p->host_no, CTL_OF_SCB(scb));
break;
case CHECK_CONDITION:
@@ -3331,7 +3632,7 @@
aic7xxx_busy_target(p, target, channel, hscb->tag);
outb(SEND_SENSE, p->base + RETURN_1);
} /* first time sense, no errors */
- else
+ else
{
if (aic7xxx_error(cmd) == 0)
{
@@ -3341,56 +3642,94 @@
break;
case QUEUE_FULL:
-#ifdef NOT_YET
- if (scb->hscb->control & TAG_ENB)
+ queue_flag = TRUE; /* Mark that this is a QUEUE_FULL and */
+ case BUSY: /* drop through to here */
+ {
+ struct aic7xxx_scb *next_scbp, *prev_scbp;
+ int ti = TARGET_INDEX(scb->cmd);
+
+ aic7xxx_search_qinfifo(p, target, channel, lun,
+ SCB_LIST_NULL, 0, TRUE,
+ &p->device_status[ti].delayed_scbs);
+ next_scbp = p->waiting_scbs.head;
+ while ( next_scbp != NULL )
{
- if (cmd->device->queue_depth > 2)
- {
- cmd->device->queue_depth--; /* Not correct */
- printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
- "reduced to %d\n", p->host_no,
- TC_OF_SCB(scb), cmd->device->queue_depth);
- }
- /*
- * XXX - Requeue this unconditionally?
- */
-
- /*
- * We'd like to be able to give the SCB some more time
- * (untimeout, then timeout).
- */
- break;
+ prev_scbp = next_scbp;
+ next_scbp = next_scbp->q_next;
+ if ( aic7xxx_match_scb(prev_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ scbq_insert_tail(&p->device_status[ti].delayed_scbs,
+ prev_scbp);
+ }
}
-#endif
- printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+ scbq_insert_head(&p->device_status[ti].delayed_scbs, scb);
+ p->device_status[ti].active_cmds--;
+ scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+
+ if (p->device_status[ti].timer.expires == 0)
+ {
+ if ( p->device_status[ti].active_cmds )
+ {
+ p->device_status[ti].timer.expires = jiffies + (HZ * 2);
+ add_timer(&p->device_status[ti].timer);
+ }
+ else
+ {
+ p->device_status[ti].timer.expires = jiffies + (HZ / 2);
+ add_timer(&p->device_status[ti].timer);
+ }
+ }
+ if (aic7xxx_verbose > 1)
+ {
+ if (queue_flag)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue full received; "
"queue depth %d, active %d\n", p->host_no,
- TC_OF_SCB(scb), cmd->device->queue_depth,
- p->device_status[TARGET_INDEX(cmd)].active_cmds);
+ CTL_OF_SCB(scb), p->device_status[ti].max_queue_depth,
+ p->device_status[ti].active_cmds);
+ else
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target busy\n",
+ p->host_no, CTL_OF_SCB(scb));
- /* Else treat this as if it was a BUSY condition. */
- scb->hscb->target_status = (BUSY << 1) |
- (scb->hscb->target_status & 0x01);
- /* Fall through to the BUSY case. */
-
- case BUSY:
- printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
- p->host_no, TC_OF_SCB(scb));
- if (!aic7xxx_error(cmd))
+ }
+ if (queue_flag)
{
- /*
- * The mid-level SCSI code should be fixed to
- * retry the command at a later time instead of
- * trying right away.
- */
- aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+ p->device_status[ti].temp_queue_depth =
+ p->device_status[ti].active_cmds;
+ if ( (p->device_status[ti].last_queue_full <
+ (p->device_status[ti].active_cmds - 1)) ||
+ (p->device_status[ti].last_queue_full >
+ (p->device_status[ti].active_cmds + 1)) )
+ {
+ p->device_status[ti].last_queue_full =
+ p->device_status[ti].active_cmds;
+ p->device_status[ti].last_queue_full_count = 0;
+ }
+ else
+ {
+ p->device_status[ti].last_queue_full_count++;
+ }
+ if ( (p->device_status[ti].last_queue_full_count > 14) &&
+ (p->device_status[ti].active_cmds > 4) )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue depth reduced "
+ "to %d\n", p->host_no, CTL_OF_SCB(scb),
+ p->device_status[ti].active_cmds);
+ p->device_status[ti].max_queue_depth =
+ p->device_status[ti].active_cmds;
+ p->device_status[ti].last_queue_full = 0;
+ p->device_status[ti].last_queue_full_count = 0;
+ }
}
- udelay(1000); /* A small pause (1ms) to help the drive */
break;
-
+ }
+
default:
- printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unexpected target "
"status 0x%x.\n", p->host_no,
- TC_OF_SCB(scb), scb->hscb->target_status);
+ CTL_OF_SCB(scb), scb->hscb->target_status);
if (!aic7xxx_error(cmd))
{
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
@@ -3419,8 +3758,9 @@
{
outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
outb(1, p->base + MSG_LEN);
- printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
- p->host_no, TC_OF_SCB(scb));
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset mailed.\n",
+ p->host_no, CTL_OF_SCB(scb));
}
else if (scb->flags & SCB_ABORT)
{
@@ -3433,8 +3773,9 @@
outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
}
outb(message_offset + 1, p->base + MSG_LEN);
- printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
- p->host_no, TC_OF_SCB(scb));
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort message mailed.\n",
+ p->host_no, CTL_OF_SCB(scb));
}
else if (scb->flags & SCB_MSGOUT_WDTR)
{
@@ -3485,18 +3826,21 @@
overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
(inb(p->base + STCNT + 2) << 16);
overrun = 0x00FFFFFF - overrun;
- printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
- "in %s phase, tag %d; forcing a retry.\n",
- p->host_no, TC_OF_SCB(scb), overrun,
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Data overrun of %d bytes "
+ "detected in %s phase, tag %d; forcing a retry.\n",
+ p->host_no, CTL_OF_SCB(scb), overrun,
lastphase == P_DATAIN ? "Data-In" : "Data-Out",
scb->hscb->tag);
- printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n",
+ printk(KERN_WARNING "%s seen Data Phase. Length=%d, NumSGs=%d.\n",
inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
aic7xxx_length(scb->cmd, 0), scb->sg_count);
- for (i = 0; i < scb->sg_count; i++)
- {
- printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n",
- i, scb->sg_list[i].address, scb->sg_list[i].length);
+ for (i = 0; i < scb->sg_count; i++)
+ {
+ printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
+ i, scb->sg_list[i].address, scb->sg_list[i].length);
+ }
}
/*
* XXX - What do we really want to do on an overrun? The
@@ -3606,21 +3950,14 @@
sindex = inb(p->base + SINDEX);
message = inb(p->base + sindex - 1);
- if (message == MSG_ABORT)
+ if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
{
- printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
- aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
- aic7xxx_run_done_queue(p, /* complete */ TRUE);
- scb = NULL;
- printerror = 0;
- }
- else if (message == MSG_ABORT_TAG)
- {
- printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
- aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
- aic7xxx_run_done_queue(p, /* complete */ TRUE);
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB %d abort delivered.\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+ (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+ aic7xxx_run_done_queue(p, FALSE);
scb = NULL;
printerror = 0;
}
@@ -3645,15 +3982,18 @@
{
tag = SCB_LIST_NULL;
}
- aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+ aic7xxx_run_done_queue(p, FALSE);
}
else
- {
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ { /* Since we don't really know what happened here, we'll wait */
+ /* for the commands to timeout and get aborted if need be */
+ aic7xxx_add_curscb_to_free_list(p);
}
printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
"SEQADDR = 0x%x\n", p->host_no, lastphase,
(inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ scb = NULL;
}
outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
outb(CLRBUSFREE, p->base + CLRSINT1);
@@ -3784,8 +4124,8 @@
* A parity error has occurred during a data
* transfer phase. Flag it and continue.
*/
- printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
- p->host_no, TC_OF_SCB(scb), phase);
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Parity error during phase %s.\n",
+ p->host_no, CTL_OF_SCB(scb), phase);
/*
* We've set the hardware to assert ATN if we get a parity
@@ -3825,7 +4165,6 @@
if (scb != NULL)
{
aic7xxx_done(p, scb);
- aic7xxx_done_cmds_complete(p);
}
}
@@ -3845,6 +4184,7 @@
struct aic7xxx_host *p;
unsigned char intstat;
unsigned long flags;
+ unsigned int interrupts_cleared = 0;
p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
@@ -3897,7 +4237,7 @@
if (p->flags & IN_ISR)
{
- printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+ panic(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
p->host_no);
return;
}
@@ -3906,7 +4246,6 @@
* Indicate that we're in the interrupt handler.
*/
save_flags(flags);
- cli();
p->flags |= IN_ISR;
if (intstat & CMDCMPLT)
@@ -3915,31 +4254,28 @@
Scsi_Cmnd *cmd;
unsigned char qoutcnt;
unsigned char scb_index;
- int i, interrupts_cleared = 0;
+ int i;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
+ cli();
qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
#if 1
if (qoutcnt >= p->qfullcount - 1)
- printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
- "qoutcnt = %d.\n", qoutcnt);
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) Command complete near Qfull count, "
+ "qoutcnt = %d.\n", p->host_no, qoutcnt);
#endif
while (qoutcnt > 0)
{
- if ((p->flags & PAGE_ENABLED) != 0)
+ if (p->flags & PAGE_ENABLED)
{
p->cmdoutcnt += qoutcnt;
- if (p->cmdoutcnt >= p->qfullcount)
+ if ( p->cmdoutcnt >= p->qfullcount )
{
- /*
- * Since paging only occurs on aic78x0 chips, we can use
- * Auto Access Pause to clear the command count.
- */
outb(0, p->base + CMDOUTCNT);
p->cmdoutcnt = 0;
}
@@ -3947,56 +4283,43 @@
for (i = 0; i < qoutcnt; i++)
{
scb_index = inb(p->base + QOUTFIFO);
- scb = p->scb_data->scb_array[scb_index];
+ if ( scb_index >= p->scb_data->numscbs )
+ scb = NULL;
+ else
+ scb = p->scb_data->scb_array[scb_index];
if (scb == NULL)
{
- printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
- "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT with invalid SCB "
+ "index %d, QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
inb(p->base + QOUTCNT), inb(p->base + QINCNT));
continue;
}
else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
- printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
- "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+ printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT without command for "
+ "SCB %d, QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
p->host_no, scb_index, inb(p->base + QOUTCNT),
inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
continue;
}
- cmd = scb->cmd;
- if (scb->hscb->residual_SG_segment_count != 0)
- {
- aic7xxx_calculate_residual(p, scb);
- }
- if ((scb->flags & SCB_QUEUED_ABORT) != 0)
- {
- /*
- * Have to clean up any possible entries in the
- * waiting queue and the QINFIFO.
- */
- int target;
- char channel;
- int lun;
- unsigned char tag;
-
- tag = SCB_LIST_NULL;
- target = cmd->target;
- lun = cmd->lun;
- channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
- if (scb->hscb->control & TAG_ENB)
- {
- tag = scb->hscb->tag;
- }
- aic7xxx_reset_device(p, target, channel, lun, tag);
- /*
- * Run the done queue, but don't complete the commands; we
- * do this once at the end of the loop.
- */
- aic7xxx_run_done_queue(p, /*complete*/ FALSE);
- }
- cmd->result |= (aic7xxx_error(cmd) << 16);
- p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
- aic7xxx_done(p, scb);
+ switch (status_byte(scb->hscb->target_status))
+ {
+ case QUEUE_FULL:
+ case BUSY:
+ scb->hscb->target_status = 0;
+ scb->cmd->result = 0;
+ aic7xxx_error(scb->cmd) = DID_OK;
+ break;
+ default:
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+ aic7xxx_done(p, scb);
+ }
}
/*
* Clear interrupt status before checking the output queue again.
@@ -4014,8 +4337,7 @@
{
outb(CLRCMDINT, p->base + CLRINT);
}
-
- aic7xxx_done_cmds_complete(p);
+ restore_flags(flags);
}
if (intstat & BRKADRINT)
@@ -4031,32 +4353,39 @@
printk(KERN_ERR " %s\n", hard_error[i].errmesg);
}
}
- printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
- inb(p->base + ERROR),
- (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
- aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
- aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ aic7xxx_reset_channel(p, 'A', TRUE);
+ if ( p->bus_type == AIC_TWIN )
+ {
+ aic7xxx_reset_channel(p, 'B', TRUE);
+ restart_sequencer(p);
+ pause_sequencer(p);
+ }
+ outb(CLRBRKADRINT, p->base + CLRINT);
}
if (intstat & SEQINT)
{
+ cli();
aic7xxx_handle_seqint(p, intstat);
+ restore_flags(flags);
}
if (intstat & SCSIINT)
{
+ cli();
aic7xxx_handle_scsiint(p, intstat);
+ restore_flags(flags);
}
- if (p->waiting_scbs.head != NULL)
- {
- aic7xxx_run_waiting_queues(p);
- }
-
+ aic7xxx_done_cmds_complete(p);
+ cli();
+ aic7xxx_run_waiting_queues(p);
+ unpause_sequencer(p, TRUE);
p->flags &= ~IN_ISR;
restore_flags(flags);
}
+
/*+F*************************************************************************
* Function:
* aic7xxx_device_queue_depth
@@ -4078,6 +4407,9 @@
aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
{
int default_depth = 2;
+ unsigned char tindex;
+
+ tindex = device->id | (device->channel << 3);
device->queue_depth = default_depth;
#ifdef AIC7XXX_TAGGED_QUEUEING
@@ -4103,9 +4435,9 @@
if (!(p->discenable & target_mask))
{
- printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+ printk(KERN_INFO "(scsi%d:%d:%d:%d) Disconnection disabled, unable to "
"enable tagged queueing.\n",
- p->host_no, device->id, device->channel);
+ p->host_no, device->channel, device->id, device->lun);
}
else
{
@@ -4118,9 +4450,7 @@
}
else
{
- unsigned char tindex;
- tindex = device->id | (device->channel << 3);
if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
{
tag_enabled = FALSE;
@@ -4141,10 +4471,12 @@
{
if (aic7xxx_verbose)
{
- printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+ printk(KERN_INFO "(scsi%d:%d:%d:%d) Enabled tagged queuing, "
"queue depth %d.\n", p->host_no,
- device->id, device->channel, device->queue_depth);
+ device->channel, device->id, device->lun, device->queue_depth);
}
+ p->device_status[tindex].max_queue_depth = device->queue_depth;
+ p->device_status[tindex].temp_queue_depth = device->queue_depth;
device->tagged_queue = 1;
device->current_tag = SCB_LIST_NULL;
}
@@ -5064,6 +5396,7 @@
}
p->host = host;
+ p->last_reset = 0;
p->host_no = host->host_no;
p->isr_count = 0;
p->next = NULL;
@@ -5078,6 +5411,15 @@
p->device_status[i].flags = 0;
p->device_status[i].active_cmds = 0;
p->device_status[i].last_reset = 0;
+ p->device_status[i].last_queue_full = 0;
+ p->device_status[i].last_queue_full_count = 0;
+ p->device_status[i].max_queue_depth = 2;
+ p->device_status[i].temp_queue_depth = 2;
+ scbq_init(&p->device_status[i].delayed_scbs);
+ init_timer(&p->device_status[i].timer);
+ p->device_status[i].timer.expires = 0;
+ p->device_status[i].timer.data = (unsigned long)p;
+ p->device_status[i].timer.function = (void *)aic7xxx_timer;
}
if (aic7xxx_boards[p->irq] == NULL)
{
@@ -5085,7 +5427,7 @@
int irq_flags = 0;
#ifdef AIC7XXX_OLD_ISR_TYPE
- irg_flags = SA_INTERRUPT;
+ irq_flags = SA_INTERRUPT;
#endif
/*
* Warning! This must be done before requesting the irq. It is
@@ -5108,6 +5450,12 @@
{
result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
"aic7xxx", NULL));
+ if (result < 0)
+ {
+ irq_flags = (irq_flags & SA_INTERRUPT) ? 0 : SA_INTERRUPT;
+ result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+ "aic7xxx", NULL));
+ }
}
if (result < 0)
{
@@ -5365,6 +5713,7 @@
{
outb(p->qfullcount, p->base + FIFODEPTH);
outb(0, p->base + CMDOUTCNT);
+ p->cmdoutcnt = 0;
}
/*
@@ -5849,7 +6198,6 @@
else
hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
outb(hcntrl | PAUSE, base + HCNTRL);
-
p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
if (p == NULL)
{
@@ -6303,10 +6651,40 @@
}
}
#endif CONFIG_PCI
-
return (found);
}
+static void
+aic7xxx_fake_scsi_done(Scsi_Cmnd *cmd)
+{
+ memset(&cmd->sense_buffer[0], '\0', sizeof(cmd->sense_buffer));
+}
+
+static void
+aic7xxx_make_fake_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *cmd, int which)
+{
+ Scsi_Cmnd *cmd2;
+
+ if (which == 0)
+ p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 = cmd2 =
+ kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+ else
+ p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 = cmd2 =
+ kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+ if (cmd2 != NULL)
+ {
+ memcpy(cmd2, cmd, sizeof(Scsi_Cmnd));
+ memset(&cmd2->cmnd[0], '\0', sizeof(cmd2->cmnd));
+ cmd2->cmnd[0] = TEST_UNIT_READY;
+ cmd2->cmd_len = 6;
+ cmd2->bufflen = 0;
+ cmd2->request_bufflen = 0;
+ cmd2->buffer = NULL;
+ cmd2->request_buffer = NULL;
+ cmd2->use_sg = 0;
+ cmd2->underflow = 0;
+ }
+}
/*+F*************************************************************************
* Function:
@@ -6337,7 +6715,7 @@
{
cmd->tag = hscb->tag;
p->device_status[TARGET_INDEX(cmd)].commands_sent++;
- if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+ if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 200)
{
hscb->control |= MSG_SIMPLE_Q_TAG;
}
@@ -6349,12 +6727,27 @@
}
#endif /* Tagged queueing */
}
-
if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
{
- p->wdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_WDTR;
+ if ( cmd->cmnd[0] == TEST_UNIT_READY )
+ {
+ p->wdtr_pending |= mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ }
+ else
+ {
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
+ aic7xxx_make_fake_cmnd(p, cmd, 0);
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
+ aic7xxx_make_fake_cmnd(p, cmd, 1);
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 != NULL )
+ aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0,
+ aic7xxx_fake_scsi_done);
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
+ aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
+ aic7xxx_fake_scsi_done);
+ }
#if 0
printk("scsi%d: Sending WDTR request to target %d.\n",
p->host_no, cmd->target);
@@ -6364,9 +6757,22 @@
{
if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
{
- p->sdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_SDTR;
+ if ( cmd->cmnd[0] == TEST_UNIT_READY )
+ {
+ p->sdtr_pending |= mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ else
+ {
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
+ aic7xxx_make_fake_cmnd(p, cmd, 0);
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
+ aic7xxx_make_fake_cmnd(p, cmd, 1);
+ if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
+ aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
+ aic7xxx_fake_scsi_done);
+ }
#if 0
printk("scsi%d: Sending SDTR request to target %d.\n",
p->host_no, cmd->target);
@@ -6465,13 +6871,9 @@
long processor_flags;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
+ int tindex = TARGET_INDEX(cmd);
p = (struct aic7xxx_host *) cmd->host->hostdata;
- if (p->host != cmd->host)
- {
- printk(KERN_INFO "scsi%d: Internal host structure != scsi.c host "
- "structure.\n", p->host_no);
- }
/*
* Check to see if channel was scanned.
@@ -6496,11 +6898,14 @@
cmd->lun & 0x07);
#endif
- if (p->device_status[TARGET_INDEX(cmd)].active_cmds
- > cmd->device->queue_depth)
- {
- printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
- p->host_no, cmd->target, cmd->channel);
+ if ( (p->device_status[tindex].active_cmds > cmd->device->queue_depth) &&
+ !(p->wdtr_pending & (0x1 << tindex)) &&
+ !(p->sdtr_pending & (0x1 << tindex)) )
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Commands queued exceeds queue "
+ "depth, active=%d\n",
+ p->host_no, CTL_OF_CMD(cmd),
+ p->device_status[tindex].active_cmds);
}
scb = aic7xxx_allocate_scb(p);
if (scb == NULL)
@@ -6548,13 +6953,20 @@
save_flags(processor_flags);
cli();
- scbq_insert_tail(&p->waiting_scbs, scb);
- if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
+ if (p->device_status[tindex].delayed_scbs.head != NULL)
+ {
+ scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+ }
+ else
+ {
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ }
+ if ((p->flags & (IN_ISR | IN_ABORT | RESET_PENDING)) == 0)
{
aic7xxx_run_waiting_queues(p);
}
-
restore_flags(processor_flags);
+
#if 0
printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
(long) cmd, (long) scb->cmd, scb->hscb->tag);
@@ -6579,103 +6991,57 @@
{
struct aic7xxx_scb *scb;
struct aic7xxx_hwscb *hscb;
- unsigned char bus_state;
int result = -1;
char channel;
+ unsigned char saved_scbptr, lastphase;
+ unsigned char hscb_index, linked_next;
+ int disconnected;
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
hscb = scb->hscb;
- /*
- * Ensure that the card doesn't do anything behind our back.
- * Also make sure that we didn't just miss an interrupt that
- * could affect this abort/reset.
- */
- pause_sequencer(p);
- while (inb(p->base + INTSTAT) & INT_PEND);
- {
- aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
- pause_sequencer(p);
- }
- if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
- {
- result = SCSI_RESET_NOT_RUNNING;
- unpause_sequencer(p, /* unpause_always */ TRUE);
- return(result);
- }
-
-
- printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
- p->host_no, TC_OF_SCB(scb), scb->flags);
- bus_state = inb(p->base + LASTPHASE);
-
- switch (bus_state)
+ lastphase = inb(p->base + LASTPHASE);
+ if (aic7xxx_verbose > 1)
{
- case P_DATAOUT:
- printk("Data-Out phase, ");
- break;
- case P_DATAIN:
- printk("Data-In phase, ");
- break;
- case P_COMMAND:
- printk("Command phase, ");
- break;
- case P_MESGOUT:
- printk("Message-Out phase, ");
- break;
- case P_STATUS:
- printk("Status phase, ");
- break;
- case P_MESGIN:
- printk("Message-In phase, ");
- break;
- default:
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus Device reset, scb flags 0x%x, ",
+ p->host_no, CTL_OF_SCB(scb), scb->flags);
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ printk("Data-Out phase, ");
+ break;
+ case P_DATAIN:
+ printk("Data-In phase, ");
+ break;
+ case P_COMMAND:
+ printk("Command phase, ");
+ break;
+ case P_MESGOUT:
+ printk("Message-Out phase, ");
+ break;
+ case P_STATUS:
+ printk("Status phase, ");
+ break;
+ case P_MESGIN:
+ printk("Message-In phase, ");
+ break;
+ default:
/*
* We're not in a valid phase, so assume we're idle.
*/
- printk("while idle, LASTPHASE = 0x%x, ", bus_state);
- break;
- }
- printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+ printk("while idle, LASTPHASE = 0x%x, ", lastphase);
+ break;
+ }
+ printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
inb(p->base + SCSISIGI),
inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+ }
channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
- /*
- * Determine our course of action.
- */
- if (scb->flags & SCB_ABORT)
- {
- /*
- * Been down this road before; do a full bus reset.
- */
- scb->flags |= SCB_RECOVERY_SCB;
- unpause_sequencer(p, /* unpause_always */ TRUE);
- result = -1;
- }
-#if 0
- else if (hscb->control & TAG_ENB)
- {
- /*
- * We could be starving this command; try sending and ordered tag
- * command to the target we come from.
- */
- scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
- p->orderedtag = p->orderedtag | 0xFF;
- result = SCSI_RESET_PENDING;
- unpause_sequencer(p, /* unpause_always */ TRUE);
- printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
- p->host_no);
- }
-#endif
- else
- {
- unsigned char active_scb_index, saved_scbptr;
- struct aic7xxx_scb *active_scb;
/*
- * Send an Abort Message:
+ * Send a Device Reset Message:
* The target that is holding up the bus may not be the same as
* the one that triggered this timeout (different commands have
* different timeout lengths). Our strategy here is to queue an
@@ -6686,148 +7052,136 @@
* fails, we'll get another timeout a few seconds later which will
* attempt a bus reset.
*/
- saved_scbptr = inb(p->base + SCBPTR);
- active_scb_index = inb(p->base + SCB_TAG);
- active_scb = p->scb_data->scb_array[active_scb_index];
+ saved_scbptr = inb(p->base + SCBPTR);
+ disconnected = FALSE;
- if (bus_state != P_BUSFREE)
+ if (lastphase != P_BUSFREE)
+ {
+ if (inb(p->base + SCB_TAG) >= p->scb_data->numscbs)
{
- if (active_scb_index >= p->scb_data->numscbs)
- {
/*
* Perform a bus reset.
*
* XXX - We want to queue an abort for the timedout SCB
* instead.
*/
- result = -1;
- printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
- "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
- }
- else
+ printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
+ return(SCSI_RESET_ERROR);
+ }
+ if (scb->hscb->tag == inb(p->base + SCB_TAG))
+ {
+ if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
{
/* Send the abort message to the active SCB. */
outb(1, p->base + MSG_LEN);
- if (active_scb->hscb->control & TAG_ENB)
- {
- outb(MSG_ABORT_TAG, p->base + MSG_OUT);
- }
- else
- {
- outb(MSG_ABORT, p->base + MSG_OUT);
- }
- outb(bus_state | ATNO, p->base + SCSISIGO);
- printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
- p->host_no);
- active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
- if (active_scb != scb)
- {
- /*
- * XXX - We would like to increment the timeout on scb, but
- * access to that routine is denied because it is hidden
- * in scsi.c. If we were able to do this, it would give
- * scb a new lease on life.
- */
- result = SCSI_RESET_PENDING;
- aic7xxx_error(active_scb->cmd) = DID_RESET;
- }
- else
- {
- aic7xxx_error(scb->cmd) = DID_RESET;
- result = SCSI_RESET_PENDING;
- }
- unpause_sequencer(p, /* unpause_always */ TRUE);
- }
- }
- else
- {
- unsigned char hscb_index, linked_next;
- int disconnected;
-
- disconnected = FALSE;
- hscb_index = aic7xxx_find_scb(p, scb);
- if (hscb_index == SCB_LIST_NULL)
- {
- disconnected = TRUE;
- linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+ outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+ outb(lastphase | ATNO, p->base + SCSISIGO);
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset message in "
+ "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags &=
+ ~DEVICE_SUCCESS;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags |=
+ BUS_DEVICE_RESET_PENDING;
+ return(SCSI_RESET_PENDING);
}
else
{
- outb(hscb_index, p->base + SCBPTR);
- if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
- {
- disconnected = TRUE;
- }
- linked_next = inb(p->base + SCB_LINKED_NEXT);
+ /* We want to send out the message, but it could screw an already */
+ /* in place and being used message. Instead, we return an error */
+ /* to try and start the bus reset phase since this command is */
+ /* probably hung (aborts failed, and now reset is failing). We */
+ /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
+ /* any more on this device, but instead will escalate to a bus or */
+ /* host reset (additionally, we won't try to abort any more). */
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset, Message buffer "
+ "in use\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags &=
+ ~DEVICE_SUCCESS;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags |=
+ BUS_DEVICE_RESET_PENDING;
+ return(SCSI_RESET_ERROR);
}
- if (disconnected)
- {
+ }
+ }
+ hscb_index = aic7xxx_find_scb(p, scb);
+ if (hscb_index == SCB_LIST_NULL)
+ {
+ disconnected = TRUE;
+ linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+ }
+ else
+ {
+ outb(hscb_index, p->base + SCBPTR);
+ if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+ {
+ disconnected = TRUE;
+ }
+ linked_next = inb(p->base + SCB_LINKED_NEXT);
+ }
+ if (disconnected)
+ {
/*
* Simply set the ABORT_SCB control bit and preserve the
* linked next pointer.
*/
- scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
- scb->hscb->data_count &= ~0xFF000000;
- scb->hscb->data_count |= linked_next << 24;
- if ((p->flags & PAGE_ENABLED) == 0)
- {
- scb->hscb->control &= ~DISCONNECTED;
- }
- scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
- if (hscb_index != SCB_LIST_NULL)
- {
- unsigned char scb_control;
+ scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+ scb->hscb->data_count &= ~0xFF000000;
+ scb->hscb->data_count |= linked_next << 24;
+ if ((p->flags & PAGE_ENABLED) == 0)
+ {
+ scb->hscb->control &= ~DISCONNECTED;
+ }
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET | SCB_QUEUED_ABORT;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags &= ~DEVICE_SUCCESS;
+ p->device_status[TARGET_INDEX(scb->cmd)].flags |=
+ BUS_DEVICE_RESET_PENDING;
+ if (hscb_index != SCB_LIST_NULL)
+ {
+ unsigned char scb_control;
- scb_control = inb(p->base + SCB_CONTROL);
- outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
- }
+ scb_control = inb(p->base + SCB_CONTROL);
+ outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+ }
/*
* Actually requeue this SCB in case we can select the
* device before it reconnects. If the transaction we
* want to abort is not tagged, unbusy it first so that
* we don't get held back from sending the command.
*/
- if ((scb->hscb->control & TAG_ENB) == 0)
- {
- unsigned char target;
- int lun;
-
- target = scb->cmd->target;
- lun = scb->cmd->lun;
- aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
- 0, /* requeue */ TRUE);
- }
- printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
- p->host_no, TC_OF_SCB(scb));
- scbq_insert_head(&p->waiting_scbs, scb);
- scb->flags |= SCB_WAITINGQ;
- outb(saved_scbptr, p->base + SCBPTR);
- if ((p->flags & IN_ISR) == 0)
- {
- /*
- * Processing the waiting queue may unpause us.
- */
- aic7xxx_run_waiting_queues(p);
- /*
- * If we are using AAP, aic7xxx_run_waiting_queues() will not
- * unpause us, so ensure we are unpaused.
- */
- unpause_sequencer(p, /*unpause_always*/ FALSE);
- }
- else
- {
- unpause_sequencer(p, /*unpause_always*/ TRUE);
- }
- result = SCSI_RESET_PENDING;
- }
- else
- {
- scb->flags |= SCB_RECOVERY_SCB;
- unpause_sequencer(p, /* unpause_always */ TRUE);
- result = -1;
- }
+ if ((scb->hscb->control & TAG_ENB) == 0)
+ {
+ aic7xxx_search_qinfifo(p, cmd->target, channel, cmd->lun,
+ SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
+ }
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queueing device reset command.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ if ( !(scb->flags & SCB_WAITINGQ) ) /* Make sure we don't already have */
+ { /* an abort scb queued, or else we */
+ /* corrupt the waiting queue and */
+ /* active_cmds counter by queueing */
+ /* again. */
+ scbq_insert_head(&p->waiting_scbs, scb);
+ scb->flags |= SCB_WAITINGQ;
+ p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
+ }
+ else
+ {
+ scb->flags &= ~SCB_ABORT;
}
+ result = SCSI_RESET_PENDING;
+ }
+ else if (result == -1)
+ {
+ result = SCSI_RESET_ERROR;
}
+ outb(saved_scbptr, p->base + SCBPTR);
return (result);
}
@@ -6842,115 +7196,297 @@
int
aic7xxx_abort(Scsi_Cmnd *cmd)
{
-#if 0
struct aic7xxx_scb *scb = NULL;
-#endif
struct aic7xxx_host *p;
-#if 0
- int base, result;
+ int result, found=0;
+ unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
unsigned long processor_flags;
-#endif
+ Scsi_Cmnd *cmd_next, *cmd_prev;
p = (struct aic7xxx_host *) cmd->host->hostdata;
-#if 0
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- base = p->base;
save_flags(processor_flags);
+ pause_sequencer(p);
cli();
-#endif
-#if 1
- switch (aic7xxx_reset(cmd, 0) & 0x0f)
- {
- case SCSI_RESET_SNOOZE:
- return(SCSI_ABORT_SNOOZE);
- case SCSI_RESET_PENDING:
- return(SCSI_ABORT_PENDING);
- case SCSI_RESET_SUCCESS:
- return(SCSI_ABORT_SUCCESS);
- case SCSI_RESET_NOT_RUNNING:
- return(SCSI_ABORT_NOT_RUNNING);
- case SCSI_RESET_ERROR:
- return(SCSI_ABORT_ERROR);
- default:
- printk(KERN_WARNING "scsi%d Unknown abort/reset return state.\n",
- p->host_no);
- return(SCSI_ABORT_ERROR);
- } /* NOT REACHED */
-#else
+/*
+ * Run the isr to grab any command in the QOUTFIFO and any other misc.
+ * assundry tasks. This should also set up the bh handler if there is
+ * anything to be done, but it won't run until we are done here since
+ * we are following a straight code path without entering the scheduler
+ * code.
+ */
-#ifdef AIC7XXX_DEBUG_ABORT
- if (scb != NULL)
+ while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
{
- printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL);
+ pause_sequencer(p);
}
- else
+
+ if (scb == NULL) /* Totally bogus cmd since it points beyond our */
+ { /* valid SCB range. The suspect scb hasn't been */
+ /* allocated yet. */
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called with bogus Scsi_Cmnd->"
+ "SCB mapping.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_ABORT_NOT_RUNNING);
+ }
+ if (scb->cmd != cmd) /* Hmmm...either this SCB is currently free with a */
+ { /* NULL cmd pointer (NULLed out when freed) or it */
+ /* has already been recycled for another command */
+ /* Either way, this SCB has nothing to do with this*/
+ /* command and we need to deal with cmd without */
+ /* touching the SCB. */
+ /* The theory here is to return a value that will */
+ /* make the queued for complete command actually */
+ /* finish successfully, or to indicate that we */
+ /* don't have this cmd any more and the mid level */
+ /* code needs to find it. */
+ cmd_next = p->completeq.head;
+ cmd_prev = NULL;
+ while (cmd_next != NULL)
+ {
+ if (cmd_next == cmd)
+ {
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for command "
+ "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( cmd_prev == NULL )
+ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+ else
+ cmd_prev->host_scribble = cmd_next->host_scribble;
+ cmd_next->done(cmd_next);
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_ABORT_SUCCESS);
+ }
+ cmd_prev = cmd_next;
+ cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+ }
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for already completed"
+ " command.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_ABORT_NOT_RUNNING);
+ }
+
+/* At this point we know the following:
+ * the SCB pointer is valid
+ * the command pointer passed in to us and the scb->cmd pointer match
+ * this then means that the command we need to abort is the same as the
+ * command held by the scb pointer and is a valid abort request.
+ * Now, we just have to figure out what to do from here. Current plan is:
+ * if we have already been here on this command, escalate to a reset
+ * if scb is on waiting list or QINFIFO, send it back as aborted
+ * if scb is on WAITING_SCB list in sequencer, free scb and send back
+ * if scb is disconnected and not completed, abort with abort message
+ * if scb is currently running, then it may be causing the bus to hang
+ * so we want a return value that indicates a reset would be appropriate
+ * if the command does not finish shortly
+ * if scb is already complete but not on completeq, we're screwed because
+ * this can't happen (except if the command is in the QOUTFIFO, in which
+ * case we would like it to complete successfully instead of having to
+ * to be re-done)
+ * All other scenarios already dealt with by previous code.
+ */
+
+ if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
{
- printk("aic7xxx: Abort called with no SCB for cmd.\n");
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB aborted once already, "
+ "escalating.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_ABORT_SNOOZE);
}
-#endif
+ if ( (p->flags & (RESET_PENDING | ABORT_PENDING)) ||
+ (p->device_status[TARGET_INDEX(scb->cmd)].flags &
+ BUS_DEVICE_RESET_PENDING) )
+ {
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset/Abort pending for this "
+ "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_ABORT_PENDING);
+ }
+
+ found = 0;
+ p->flags |= IN_ABORT;
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+
+/*
+ * First, let's check to see if the currently running command is our target
+ * since if it is, the return is fairly easy and quick since we don't want
+ * to touch the command in case it might complete, but we do want a timeout
+ * in case it's actually hung, so we really do nothing, but tell the mid
+ * level code to reset the timeout.
+ */
- if (p->flags & IN_TIMEOUT)
+ if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
{
- /*
- * We've already started a recovery operation.
- */
- if ((scb->flags & SCB_RECOVERY_SCB) == 0)
- {
- restore_flags(processor_flags);
- return (SCSI_ABORT_PENDING);
- }
- else
+ /*
+ * Check to see if the sequencer is just sitting on this command, or
+ * if it's actively being run.
+ */
+ result = inb(p->base + LASTPHASE);
+ switch (result)
{
- /*
- * This is the second time we've tried to abort the recovery
- * SCB. We want the mid-level SCSI code to call the reset
- * function to reset the SCSI bus.
- */
- restore_flags(processor_flags);
- return (SCSI_ABORT_NOT_RUNNING);
+ case P_DATAOUT: /* For any of these cases, we can assume we are */
+ case P_DATAIN: /* an active command and act according. For */
+ case P_COMMAND: /* anything else we are going to fall on through*/
+ case P_STATUS: /* The SCSI_ABORT_SNOOZE will give us two abort */
+ case P_MESGOUT: /* chances to finish and then escalate to a */
+ case P_MESGIN: /* reset call */
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB is currently active. "
+ "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, TRUE);
+ p->flags &= ~IN_ABORT;
+ scb->flags |= SCB_RECOVERY_SCB; /* Note the fact that we've been */
+ p->flags |= ABORT_PENDING; /* here so we will know not to */
+ restore_flags(processor_flags); /* muck with other SCBs if this */
+ return(SCSI_ABORT_PENDING); /* one doesn't complete and clear */
+ break; /* out. */
+ default:
+ break;
}
}
- if (cmd->serial_number != cmd->serial_number_at_timeout)
+
+ if ((found == 0) && (scb->flags & SCB_WAITINGQ))
{
- result = SCSI_ABORT_NOT_RUNNING;
+ int tindex = TARGET_INDEX(cmd);
+
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on waiting list and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ scbq_remove(&p->waiting_scbs, scb);
+ scbq_remove(&p->device_status[tindex].delayed_scbs, scb);
+ p->device_status[tindex].active_cmds++;
+ scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+ scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ found = 1;
}
- else if (scb == NULL)
+
+/*
+ * We just checked the waiting_q, now for the QINFIFO
+ */
+ if ( found == 0 )
+ {
+ if ( ((found = aic7xxx_search_qinfifo(p, cmd->target,
+ INT_TO_CHAN(cmd->channel),
+ cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+ FALSE, NULL)) != 0) && (aic7xxx_verbose > 1))
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found in QINFIFO and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+
+/*
+ * QINFIFO, waitingq, completeq done. Next, check WAITING_SCB list in card
+ */
+
+ if ( found == 0 )
{
- result = SCSI_ABORT_NOT_RUNNING;
+ unsigned char scb_next_ptr;
+ prev_hscbptr = SCB_LIST_NULL;
+ saved_hscbptr = inb(p->base + SCBPTR);
+ next_hscbptr = inb(p->base + WAITING_SCBH);
+ while ( next_hscbptr != SCB_LIST_NULL )
+ {
+ outb( next_hscbptr, p->base + SCBPTR );
+ if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
+ {
+ found = 1;
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on hardware waiting"
+ " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ if ( prev_hscbptr == SCB_LIST_NULL )
+ outb(inb(p->base + SCB_NEXT), p->base + WAITING_SCBH);
+ else
+ {
+ scb_next_ptr = inb(p->base + SCB_NEXT);
+ outb(prev_hscbptr, p->base + SCBPTR);
+ outb(scb_next_ptr, p->base + SCB_NEXT);
+ outb(next_hscbptr, p->base + SCBPTR);
+ }
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ break;
+ }
+ prev_hscbptr = next_hscbptr;
+ next_hscbptr = inb(p->base + SCB_NEXT);
+ }
+ outb( saved_hscbptr, p->base + SCBPTR );
}
- else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
+
+/*
+ * Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
+ * OK...the sequencer's paused, interrupts are off, and we haven't found the
+ * command anyplace where it could be easily aborted. Time for the hard
+ * work. We also know the command is valid. This essentially means the
+ * command is disconnected, or connected but not into any phases yet, which
+ * we know due to the tests we ran earlier on the current active scb phase.
+ * At this point we can queue the abort tag and go on with life.
+ */
+
+ if ( found == 0 )
{
- result = SCSI_ABORT_NOT_RUNNING;
+ p->flags |= ABORT_PENDING;
+ scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+ scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+ result=aic7xxx_find_scb(p, scb);
+ if ( result != SCB_LIST_NULL )
+ {
+ saved_hscbptr = inb(p->base + SCBPTR);
+ outb(result, p->base + SCBPTR);
+ tmp_char = inb(p->base + SCB_CONTROL);
+ outb( tmp_char | MK_MESSAGE | ABORT_SCB, p->base + SCB_CONTROL);
+ outb(saved_hscbptr, p->base + SCBPTR);
+ }
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB disconnected. Queueing Abort"
+ " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+ if ( (scb->hscb->control & TAG_ENB) == 0 )
+ {
+ aic7xxx_search_qinfifo(p, cmd->target, INT_TO_CHAN(cmd->channel),
+ cmd->lun, SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
+ }
+ if ( !(scb->flags & SCB_WAITINGQ) )
+ {
+ scbq_insert_head(&p->waiting_scbs, scb);
+ scb->flags |= SCB_WAITINGQ;
+ p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
+ }
}
else
- {
- /*
- * XXX - Check use of IN_TIMEOUT to see if we're Doing the
- * Right Thing with it.
- */
- p->flags |= IN_TIMEOUT;
- result = aic7xxx_bus_device_reset(p, scb->cmd);
- switch (result)
- {
- case SCSI_RESET_NOT_RUNNING:
- p->flags &= ~IN_TIMEOUT;
- result = SCSI_ABORT_NOT_RUNNING;
- break;
- case SCSI_RESET_PENDING:
- result = SCSI_ABORT_PENDING;
- break;
- default:
- p->flags &= ~IN_TIMEOUT;
- result = SCSI_ABORT_SNOOZE;
- break;
- }
+ {
+ scb->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
}
+ aic7xxx_run_done_queue(p, TRUE);
+ aic7xxx_run_waiting_queues(p);
+ p->flags &= ~IN_ABORT;
restore_flags(processor_flags);
- return (result);
-#endif
+
+/*
+ * On the return value. If we found the command and aborted it, then we know
+ * it's already sent back and there is no reason for a further timeout, so
+ * we use SCSI_ABORT_SUCCESS. On the queued abort side, we aren't so certain
+ * there hasn't been a bus hang or something that might keep the abort from
+ * from completing. Therefore, we use SCSI_ABORT_PENDING. The first time this
+ * is passed back, the timeout on the command gets extended, the second time
+ * we pass this back, the mid level SCSI code calls our reset function, which
+ * would shake loose a hung bus.
+ */
+ if ( found != 0 )
+ return(SCSI_ABORT_SUCCESS);
+ else
+ return(SCSI_ABORT_PENDING);
}
@@ -6969,189 +7505,225 @@
{
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
- int base, found, tindex, min_target, max_target;
+ int tindex;
int result = -1;
char channel = 'A';
unsigned long processor_flags;
+#define DEVICE_RESET 0x01
+#define BUS_RESET 0x02
+#define HOST_RESET 0x04
+#define FAIL 0x08
+#define RESET_DELAY 0x10
+ int action;
+ Scsi_Cmnd *cmd_prev, *cmd_next;
+
+
+ if ( cmd == NULL )
+ {
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(aic7xxx) Reset called with NULL Scsi_Cmnd "
+ "pointer, failing.\n");
+ return(SCSI_RESET_SNOOZE);
+ }
p = (struct aic7xxx_host *) cmd->host->hostdata;
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- base = p->base;
- channel = cmd->channel ? 'B': 'A';
+ channel = INT_TO_CHAN(cmd->channel);
tindex = TARGET_INDEX(cmd);
-#ifdef 0 /* AIC7XXX_DEBUG_ABORT */
- if (scb != NULL)
- {
- printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
- }
- else
- {
- printk("aic7xxx: Reset called with no SCB for cmd.\n");
- }
-#endif
-
- /*
- * This routine is called by scsi.c, in which case the interrupts
- * very well may be on when we are called. As such, we need to save
- * the flags to be sure, then turn interrupts off, and then call our
- * various method funtions which all assume interrupts are off.
- */
save_flags(processor_flags);
+ pause_sequencer(p);
cli();
+ while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
+ {
+ aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL );
+ pause_sequencer(p);
+ }
- if (scb->cmd != cmd)
- scb = NULL;
-
- if (p->flags & IN_TIMEOUT)
+ if (scb == NULL)
{
- /*
- * We've already started a recovery operation.
- */
- if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with bogus Scsi_Cmnd"
+ "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
{
- restore_flags(processor_flags);
- return (SCSI_RESET_PENDING);
+ action = HOST_RESET;
+ }
+ else
+ {
+ action = BUS_RESET;
}
}
- else
+ else if (scb->cmd != cmd)
{
- if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
- && (scb != NULL))
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with recycled SCB "
+ "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+ cmd_prev = NULL;
+ cmd_next = p->completeq.head;
+ while ( cmd_next != NULL )
{
- /*
- * Attempt a bus device reset if commands have completed successfully
- * since the last bus device reset, or it has been less than 100ms
- * since the last reset.
- */
- if ((p->flags & DEVICE_SUCCESS) ||
- ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+ if (cmd_next == cmd)
{
- if (cmd->serial_number != cmd->serial_number_at_timeout)
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else if (scb == NULL)
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else if (flags & SCSI_RESET_ASYNCHRONOUS)
- {
- if (scb->flags & SCB_ABORTED)
- {
- result = SCSI_RESET_PENDING;
- }
- else if (!(scb->flags & SCB_ACTIVE))
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- }
-
- if (result == -1)
- {
- if ((flags & SCSI_RESET_SYNCHRONOUS) &&
- (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
- {
- scb->flags |= SCB_ABORTED;
- result = SCSI_RESET_PENDING;
- }
- else
- {
- p->flags |= IN_TIMEOUT;
- result = aic7xxx_bus_device_reset(p, cmd);
- if (result == 0)
- {
- p->flags &= ~IN_TIMEOUT;
- result = SCSI_RESET_PENDING;
- }
- }
- }
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, found cmd on completeq"
+ ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( cmd_prev == NULL )
+ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+ else
+ cmd_prev->host_scribble = cmd_next->host_scribble;
+ cmd_next->done(cmd_next);
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_RESET_SUCCESS);
}
+ cmd_prev = cmd_next;
+ cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+ }
+ if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, cmd not found,"
+ " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, TRUE);
+ restore_flags(processor_flags);
+ return(SCSI_RESET_NOT_RUNNING);
+ }
+ else
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, no scb, "
+ "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
+ scb = NULL;
+ action = HOST_RESET;
}
}
- if (result == -1)
+ else
{
- /*
- * The bus device reset failed; try resetting the channel.
- */
- if (!(flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
- && (flags & SCSI_RESET_ASYNCHRONOUS))
+ if (aic7xxx_verbose)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, scb %d, flags "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
{
- if (scb == NULL)
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else if (!(scb->flags & SCB_ACTIVE))
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else if ((scb->flags & SCB_ABORTED) &&
- (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
- {
- result = SCSI_RESET_PENDING;
- }
+ action = HOST_RESET;
}
-
- if (result == -1)
+ else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
{
- /*
- * The reset channel function assumes that the sequencer is paused.
- */
- pause_sequencer(p);
- found = aic7xxx_reset_channel(p, channel, TRUE);
- p->flags = p->flags & ~IN_TIMEOUT;
-
- /*
- * If this is a synchronous reset and there is no SCB for this
- * command, perform completion processing.
- *
- */
- if ((flags & SCSI_RESET_SYNCHRONOUS) && (scb == NULL))
+ action = BUS_RESET;
+ }
+ else
+ {
+ action = DEVICE_RESET;
+ }
+ }
+ if ( ((jiffies - p->last_reset) < (HZ * AIC7XXX_RESET_DELAY)) &&
+ (action & (HOST_RESET | BUS_RESET | DEVICE_RESET)) )
+ {
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after "
+ "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = RESET_DELAY;
+ }
+ if ( ((jiffies - p->device_status[tindex].last_reset) <
+ (HZ * AIC7XXX_RESET_DELAY)) && !(action & (HOST_RESET | BUS_RESET)))
+ {
+ if (aic7xxx_verbose > 1)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after last "
+ "reset without requesting\n"
+ "(scsi%d:%d:%d:%d) bus or host reset, escalating.\n", p->host_no,
+ CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & DEVICE_RESET) &&
+ (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING) )
+ {
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset already sent to "
+ "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & DEVICE_RESET) &&
+ (scb->flags & SCB_QUEUED_ABORT) )
+ {
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Have already attempted to reach "
+ "device with queued\n(scsi%d:%d:%d:%d) message, will escalate to bus "
+ "reset.\n", p->host_no, CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & DEVICE_RESET) && (p->flags & (RESET_PENDING | ABORT_PENDING)) )
+ {
+ if (aic7xxx_verbose > 2)
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset stupid when "
+ "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & BUS_RESET) && (p->bus_type != AIC_TWIN) )
+ {
+ action = HOST_RESET;
+ }
+ if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & RESET_PENDING)
+ && ((jiffies - p->reset_start) > (2 * HZ * AIC7XXX_RESET_DELAY)) )
+ {
+ printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!! Card must have left to go "
+ "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
+ restore_flags(processor_flags);
+ return(SCSI_RESET_SNOOZE);
+ }
+/*
+ * By this point, we want to already know what we are going to do and
+ * only have the following code implement our course of action.
+ */
+ switch (action)
+ {
+ case RESET_DELAY:
+ return(SCSI_RESET_PENDING);
+ break;
+ case FAIL:
+ return(SCSI_RESET_ERROR);
+ break;
+ case DEVICE_RESET:
+ p->flags |= RESET_PENDING;
+ result = aic7xxx_bus_device_reset(p, cmd);
+ aic7xxx_run_done_queue(p, TRUE);
+ aic7xxx_run_waiting_queues(p);
+ p->flags &= ~RESET_PENDING;
+ restore_flags(processor_flags);
+ return(result);
+ break;
+ case BUS_RESET:
+ case HOST_RESET:
+ default:
+ p->reset_start = jiffies;
+ p->flags |= RESET_PENDING;
+ aic7xxx_reset_channel(p, channel, TRUE);
+ if ( (p->bus_type == AIC_TWIN) && (action & HOST_RESET) )
{
- cmd->result = DID_RESET << 16;
- cmd->scsi_done(cmd);
+ aic7xxx_reset_channel(p, (channel == 'A') ? 'B' : 'A', TRUE);
+ restart_sequencer(p);
+ pause_sequencer(p);
}
-
- switch (p->bus_type)
+ if (scb == NULL)
{
- case AIC_TWIN:
- if (channel == 'B')
- {
- min_target = 8;
- max_target = 15;
- }
- else
- {
- min_target = 0;
- max_target = 7;
- }
- break;
-
- case AIC_WIDE:
- min_target = 0;
- max_target = 15;
- break;
-
- case AIC_SINGLE:
- default:
- min_target = 0;
- max_target = 7;
- break;
+ cmd->result = DID_RESET << 16;
+ cmd->done(cmd);
}
-
- for (tindex = min_target; tindex <= max_target; tindex++)
+ p->last_reset = jiffies;
+ if (action != HOST_RESET)
+ result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+ else
{
- p->device_status[tindex].last_reset = jiffies;
+ result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+ while (inb(p->base + QOUTCNT)) inb(p->base + QOUTFIFO);
+ if (p->flags & PAGE_ENABLED) outb(0, p->base + CMDOUTCNT);
+ aic7xxx_clear_intstat(p);
}
-
- result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
- p->flags &= ~IN_TIMEOUT;
- }
+ p->flags &= ~RESET_PENDING;
+ aic7xxx_run_waiting_queues(p);
+ restore_flags(processor_flags);
+ return(result);
+ break;
}
- aic7xxx_run_waiting_queues(p);
- restore_flags(processor_flags);
- return (result);
}
/*+F*************************************************************************
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov