Commit f27db8eb authored by Finn Thain's avatar Finn Thain Committed by Martin K. Petersen

ncr5380: Fix autosense bugs

NCR5380_information_transfer() may re-queue a command for autosense,
after calling scsi_eh_prep_cmnd(). This creates several possibilities:

1. Reselection may intervene before the re-queued command gets processed.
   If the reconnected command then undergoes autosense, this causes the
   scsi_eh_save data from the previous command to be overwritten.

2. After NCR5380_information_transfer() calls scsi_eh_prep_cmnd(),
   a new REQUEST SENSE command may arrive. This would be queued ahead
   of any command already undergoing autosense, which means the
   scsi_eh_save data might be restored to the wrong command.

3. After NCR5380_information_transfer() calls scsi_eh_prep_cmnd(),
   eh_abort_handler() may abort the command. But the scsi_eh_save data is
   not discarded, which means the scsi_eh_save data might be incorrectly
   restored to the next REQUEST SENSE command issued.

This patch adds a new autosense list so that commands that are re-queued
because of a CHECK CONDITION result can be kept apart from the REQUEST
SENSE commands that arrive via queuecommand.

This patch also adds a function dedicated to dequeueing and preparing the
next command for processing. By refactoring the main loop in this way,
scsi_eh_save takes place when an autosense command is dequeued rather
than when re-queued.
Signed-off-by: default avatarFinn Thain <fthain@telegraphics.com.au>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Tested-by: default avatarOndrej Zary <linux@rainbow-software.org>
Tested-by: default avatarMichael Schmitz <schmitzmic@gmail.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 677e0194
...@@ -244,6 +244,9 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd) ...@@ -244,6 +244,9 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd)
cmd->SCp.ptr = NULL; cmd->SCp.ptr = NULL;
cmd->SCp.this_residual = 0; cmd->SCp.this_residual = 0;
} }
cmd->SCp.Status = 0;
cmd->SCp.Message = 0;
} }
/** /**
...@@ -622,6 +625,8 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags) ...@@ -622,6 +625,8 @@ static int NCR5380_init(struct Scsi_Host *instance, int flags)
#endif #endif
spin_lock_init(&hostdata->lock); spin_lock_init(&hostdata->lock);
hostdata->connected = NULL; hostdata->connected = NULL;
hostdata->sensing = NULL;
INIT_LIST_HEAD(&hostdata->autosense);
INIT_LIST_HEAD(&hostdata->unissued); INIT_LIST_HEAD(&hostdata->unissued);
INIT_LIST_HEAD(&hostdata->disconnected); INIT_LIST_HEAD(&hostdata->disconnected);
...@@ -738,6 +743,16 @@ static void complete_cmd(struct Scsi_Host *instance, ...@@ -738,6 +743,16 @@ static void complete_cmd(struct Scsi_Host *instance,
dsprintk(NDEBUG_QUEUES, instance, "complete_cmd: cmd %p\n", cmd); dsprintk(NDEBUG_QUEUES, instance, "complete_cmd: cmd %p\n", cmd);
if (hostdata->sensing == cmd) {
/* Autosense processing ends here */
if ((cmd->result & 0xff) != SAM_STAT_GOOD) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
set_host_byte(cmd, DID_ERROR);
} else
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
hostdata->sensing = NULL;
}
hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun); hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
cmd->scsi_done(cmd); cmd->scsi_done(cmd);
...@@ -797,6 +812,64 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, ...@@ -797,6 +812,64 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
return 0; return 0;
} }
/**
* dequeue_next_cmd - dequeue a command for processing
* @instance: the scsi host instance
*
* Priority is given to commands on the autosense queue. These commands
* need autosense because of a CHECK CONDITION result.
*
* Returns a command pointer if a command is found for a target that is
* not already busy. Otherwise returns NULL.
*/
static struct scsi_cmnd *dequeue_next_cmd(struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
struct NCR5380_cmd *ncmd;
struct scsi_cmnd *cmd;
if (list_empty(&hostdata->autosense)) {
list_for_each_entry(ncmd, &hostdata->unissued, list) {
cmd = NCR5380_to_scmd(ncmd);
dsprintk(NDEBUG_QUEUES, instance, "dequeue: cmd=%p target=%d busy=0x%02x lun=%llu\n",
cmd, scmd_id(cmd), hostdata->busy[scmd_id(cmd)], cmd->device->lun);
if (!(hostdata->busy[scmd_id(cmd)] & (1 << cmd->device->lun))) {
list_del(&ncmd->list);
dsprintk(NDEBUG_QUEUES, instance,
"dequeue: removed %p from issue queue\n", cmd);
return cmd;
}
}
} else {
/* Autosense processing begins here */
ncmd = list_first_entry(&hostdata->autosense,
struct NCR5380_cmd, list);
list_del(&ncmd->list);
cmd = NCR5380_to_scmd(ncmd);
dsprintk(NDEBUG_QUEUES, instance,
"dequeue: removed %p from autosense queue\n", cmd);
scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
hostdata->sensing = cmd;
return cmd;
}
return NULL;
}
static void requeue_cmd(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd);
if (hostdata->sensing) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
list_add(&ncmd->list, &hostdata->autosense);
hostdata->sensing = NULL;
} else
list_add(&ncmd->list, &hostdata->unissued);
}
/** /**
* NCR5380_main - NCR state machines * NCR5380_main - NCR state machines
* *
...@@ -814,63 +887,40 @@ static void NCR5380_main(struct work_struct *work) ...@@ -814,63 +887,40 @@ static void NCR5380_main(struct work_struct *work)
struct NCR5380_hostdata *hostdata = struct NCR5380_hostdata *hostdata =
container_of(work, struct NCR5380_hostdata, main_task); container_of(work, struct NCR5380_hostdata, main_task);
struct Scsi_Host *instance = hostdata->host; struct Scsi_Host *instance = hostdata->host;
struct NCR5380_cmd *ncmd, *n; struct scsi_cmnd *cmd;
int done; int done;
spin_lock_irq(&hostdata->lock); spin_lock_irq(&hostdata->lock);
do { do {
done = 1; done = 1;
if (!hostdata->connected) { while (!hostdata->connected &&
dprintk(NDEBUG_MAIN, "scsi%d : not connected\n", instance->host_no); (cmd = dequeue_next_cmd(instance))) {
/*
* Search through the issue_queue for a command destined
* for a target that's not busy.
*/
list_for_each_entry_safe(ncmd, n, &hostdata->unissued,
list) {
struct scsi_cmnd *tmp = NCR5380_to_scmd(ncmd);
dsprintk(NDEBUG_QUEUES, instance, "main: tmp=%p target=%d busy=%d lun=%llu\n",
tmp, scmd_id(tmp), hostdata->busy[scmd_id(tmp)],
tmp->device->lun);
/* When we find one, remove it from the issue queue. */
if (!(hostdata->busy[tmp->device->id] &
(1 << (u8)(tmp->device->lun & 0xff)))) {
list_del(&ncmd->list);
dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES,
instance, "main: removed %p from issue queue\n",
tmp);
/* dsprintk(NDEBUG_MAIN, instance, "main: dequeued %p\n", cmd);
* Attempt to establish an I_T_L nexus here.
* On success, instance->hostdata->connected is set.
* On failure, we must add the command back to the
* issue queue so we can keep trying.
*/
/*
* REQUEST SENSE commands are issued without tagged
* queueing, even on SCSI-II devices because the
* contingent allegiance condition exists for the
* entire unit.
*/
if (!NCR5380_select(instance, tmp)) { /*
/* OK or bad target */ * Attempt to establish an I_T_L nexus here.
} else { * On success, instance->hostdata->connected is set.
/* Need to retry */ * On failure, we must add the command back to the
list_add(&ncmd->list, &hostdata->unissued); * issue queue so we can keep trying.
dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, */
instance, "main: select() failed, %p returned to issue queue\n", /*
tmp); * REQUEST SENSE commands are issued without tagged
done = 0; * queueing, even on SCSI-II devices because the
} * contingent allegiance condition exists for the
if (hostdata->connected) * entire unit.
break; */
} /* if target/lun is not busy */
} /* for */
} /* if (!hostdata->connected) */
if (!NCR5380_select(instance, cmd)) {
dsprintk(NDEBUG_MAIN, instance, "main: selected target %d for command %p\n",
scmd_id(cmd), cmd);
} else {
dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance,
"main: select failed, returning %p to queue\n", cmd);
requeue_cmd(instance, cmd);
}
}
if (hostdata->connected if (hostdata->connected
#ifdef REAL_DMA #ifdef REAL_DMA
&& !hostdata->dmalen && !hostdata->dmalen
...@@ -1853,43 +1903,21 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { ...@@ -1853,43 +1903,21 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) {
hostdata->connected = NULL; hostdata->connected = NULL;
/* cmd->result &= ~0xffff;
* I'm not sure what the correct thing to do here is : cmd->result |= cmd->SCp.Status;
* cmd->result |= cmd->SCp.Message << 8;
* If the command that just executed is NOT a request
* sense, the obvious thing to do is to set the result
* code to the values of the stored parameters.
*
* If it was a REQUEST SENSE command, we need some way
* to differentiate between the failure code of the original
* and the failure code of the REQUEST sense - the obvious
* case is success, where we fall through and leave the result
* code unchanged.
*
* The non-obvious place is where the REQUEST SENSE failed
*/
if (cmd->cmnd[0] != REQUEST_SENSE)
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
else if (status_byte(cmd->SCp.Status) != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
if ((cmd->cmnd[0] == REQUEST_SENSE) &&
hostdata->ses.cmd_len) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
hostdata->ses.cmd_len = 0 ;
}
if ((cmd->cmnd[0] != REQUEST_SENSE) && (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
list_add(&ncmd->list, &hostdata->unissued); if (cmd->cmnd[0] == REQUEST_SENSE)
dsprintk(NDEBUG_AUTOSENSE | NDEBUG_QUEUES,
instance, "REQUEST SENSE cmd %p added to head of issue queue\n",
cmd);
hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xFF));
} else {
complete_cmd(instance, cmd); complete_cmd(instance, cmd);
else {
if (cmd->SCp.Status == SAM_STAT_CHECK_CONDITION ||
cmd->SCp.Status == SAM_STAT_COMMAND_TERMINATED) {
dsprintk(NDEBUG_QUEUES, instance, "autosense: adding cmd %p to tail of autosense queue\n",
cmd);
list_add_tail(&ncmd->list,
&hostdata->autosense);
} else
complete_cmd(instance, cmd);
} }
/* /*
......
...@@ -256,10 +256,12 @@ struct NCR5380_hostdata { ...@@ -256,10 +256,12 @@ struct NCR5380_hostdata {
unsigned char last_message; /* last message OUT */ unsigned char last_message; /* last message OUT */
struct scsi_cmnd *connected; /* currently connected cmnd */ struct scsi_cmnd *connected; /* currently connected cmnd */
struct list_head unissued; /* waiting to be issued */ struct list_head unissued; /* waiting to be issued */
struct list_head autosense; /* priority issue queue */
struct list_head disconnected; /* waiting for reconnect */ struct list_head disconnected; /* waiting for reconnect */
spinlock_t lock; /* protects this struct */ spinlock_t lock; /* protects this struct */
int flags; int flags;
struct scsi_eh_save ses; struct scsi_eh_save ses;
struct scsi_cmnd *sensing;
char info[256]; char info[256];
int read_overruns; /* number of bytes to cut from a int read_overruns; /* number of bytes to cut from a
* transfer to handle chip overruns */ * transfer to handle chip overruns */
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment