diff options
Diffstat (limited to 'drivers/scsi/NCR5380.c')
-rw-r--r-- | drivers/scsi/NCR5380.c | 133 |
1 files changed, 59 insertions, 74 deletions
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index d72867257346..3eff2a69fe08 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -760,7 +760,7 @@ static struct scsi_cmnd *dequeue_next_cmd(struct Scsi_Host *instance) struct NCR5380_cmd *ncmd; struct scsi_cmnd *cmd; - if (list_empty(&hostdata->autosense)) { + if (hostdata->sensing || 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", @@ -793,7 +793,7 @@ 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) { + if (hostdata->sensing == cmd) { scsi_eh_restore_cmnd(cmd, &hostdata->ses); list_add(&ncmd->list, &hostdata->autosense); hostdata->sensing = NULL; @@ -815,15 +815,17 @@ static void NCR5380_main(struct work_struct *work) struct NCR5380_hostdata *hostdata = container_of(work, struct NCR5380_hostdata, main_task); struct Scsi_Host *instance = hostdata->host; - struct scsi_cmnd *cmd; int done; do { done = 1; spin_lock_irq(&hostdata->lock); - while (!hostdata->connected && - (cmd = dequeue_next_cmd(instance))) { + while (!hostdata->connected && !hostdata->selecting) { + struct scsi_cmnd *cmd = dequeue_next_cmd(instance); + + if (!cmd) + break; dsprintk(NDEBUG_MAIN, instance, "main: dequeued %p\n", cmd); @@ -840,8 +842,7 @@ static void NCR5380_main(struct work_struct *work) * entire unit. */ - cmd = NCR5380_select(instance, cmd); - if (!cmd) { + if (!NCR5380_select(instance, cmd)) { dsprintk(NDEBUG_MAIN, instance, "main: select complete\n"); } else { dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance, @@ -1056,6 +1057,11 @@ static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *instance, /* Reselection interrupt */ goto out; } + if (!hostdata->selecting) { + /* Command was aborted */ + NCR5380_write(MODE_REG, MR_BASE); + goto out; + } if (err < 0) { NCR5380_write(MODE_REG, MR_BASE); shost_printk(KERN_ERR, instance, @@ -1759,9 +1765,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) unsigned char msgout = NOP; int sink = 0; int len; -#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) int transfersize; -#endif unsigned char *data; unsigned char phase, tmp, extended_msg[10], old_phase = 0xff; struct scsi_cmnd *cmd; @@ -1798,6 +1802,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) do_abort(instance); cmd->result = DID_ERROR << 16; complete_cmd(instance, cmd); + hostdata->connected = NULL; return; #endif case PHASE_DATAIN: @@ -1847,20 +1852,23 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) sink = 1; do_abort(instance); cmd->result = DID_ERROR << 16; - complete_cmd(instance, cmd); /* XXX - need to source or sink data here, as appropriate */ } else cmd->SCp.this_residual -= transfersize - len; } else #endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */ { - spin_unlock_irq(&hostdata->lock); - NCR5380_transfer_pio(instance, &phase, - (int *)&cmd->SCp.this_residual, + /* Break up transfer into 3 ms chunks, + * presuming 6 accesses per handshake. + */ + transfersize = min((unsigned long)cmd->SCp.this_residual, + hostdata->accesses_per_ms / 2); + len = transfersize; + NCR5380_transfer_pio(instance, &phase, &len, (unsigned char **)&cmd->SCp.ptr); - spin_lock_irq(&hostdata->lock); + cmd->SCp.this_residual -= transfersize - len; } - break; + return; case PHASE_MSGIN: len = 1; data = &tmp; @@ -2292,14 +2300,17 @@ static bool list_del_cmd(struct list_head *haystack, * [disconnected -> connected ->]... * [autosense -> connected ->] done * - * If cmd is unissued then just remove it. - * If cmd is disconnected, try to select the target. - * If cmd is connected, try to send an abort message. - * If cmd is waiting for autosense, give it a chance to complete but check - * that it isn't left connected. * If cmd was not found at all then presumably it has already been completed, * in which case return SUCCESS to try to avoid further EH measures. + * * If the command has not completed yet, we must not fail to find it. + * We have no option but to forget the aborted command (even if it still + * lacks sense data). The mid-layer may re-issue a command that is in error + * recovery (see scsi_send_eh_cmnd), but the logic and data structures in + * this driver are such that a command can appear on one queue only. + * + * The lock protects driver data structures, but EH handlers also use it + * to serialize their own execution and prevent their own re-entry. */ static int NCR5380_abort(struct scsi_cmnd *cmd) @@ -2322,6 +2333,7 @@ static int NCR5380_abort(struct scsi_cmnd *cmd) "abort: removed %p from issue queue\n", cmd); cmd->result = DID_ABORT << 16; cmd->scsi_done(cmd); /* No tag or busy flag to worry about */ + goto out; } if (hostdata->selecting == cmd) { @@ -2336,58 +2348,21 @@ static int NCR5380_abort(struct scsi_cmnd *cmd) if (list_del_cmd(&hostdata->disconnected, cmd)) { dsprintk(NDEBUG_ABORT, instance, "abort: removed %p from disconnected list\n", cmd); - cmd->result = DID_ERROR << 16; - if (!hostdata->connected) - NCR5380_select(instance, cmd); - if (hostdata->connected != cmd) { - complete_cmd(instance, cmd); - result = FAILED; - goto out; - } + /* Can't call NCR5380_select() and send ABORT because that + * means releasing the lock. Need a bus reset. + */ + set_host_byte(cmd, DID_ERROR); + complete_cmd(instance, cmd); + result = FAILED; + goto out; } if (hostdata->connected == cmd) { dsprintk(NDEBUG_ABORT, instance, "abort: cmd %p is connected\n", cmd); hostdata->connected = NULL; - if (do_abort(instance)) { - set_host_byte(cmd, DID_ERROR); - complete_cmd(instance, cmd); - result = FAILED; - goto out; - } - set_host_byte(cmd, DID_ABORT); #ifdef REAL_DMA hostdata->dma_len = 0; #endif - if (cmd->cmnd[0] == REQUEST_SENSE) - complete_cmd(instance, cmd); - else { - struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd); - - /* Perform autosense for this command */ - list_add(&ncmd->list, &hostdata->autosense); - } - } - - if (list_find_cmd(&hostdata->autosense, cmd)) { - dsprintk(NDEBUG_ABORT, instance, - "abort: found %p on sense queue\n", cmd); - spin_unlock_irqrestore(&hostdata->lock, flags); - queue_work(hostdata->work_q, &hostdata->main_task); - msleep(1000); - spin_lock_irqsave(&hostdata->lock, flags); - if (list_del_cmd(&hostdata->autosense, cmd)) { - dsprintk(NDEBUG_ABORT, instance, - "abort: removed %p from sense queue\n", cmd); - set_host_byte(cmd, DID_ABORT); - complete_cmd(instance, cmd); - goto out; - } - } - - if (hostdata->connected == cmd) { - dsprintk(NDEBUG_ABORT, instance, "abort: cmd %p is connected\n", cmd); - hostdata->connected = NULL; if (do_abort(instance)) { set_host_byte(cmd, DID_ERROR); complete_cmd(instance, cmd); @@ -2395,9 +2370,14 @@ static int NCR5380_abort(struct scsi_cmnd *cmd) goto out; } set_host_byte(cmd, DID_ABORT); -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif + complete_cmd(instance, cmd); + goto out; + } + + if (list_del_cmd(&hostdata->autosense, cmd)) { + dsprintk(NDEBUG_ABORT, instance, + "abort: removed %p from sense queue\n", cmd); + set_host_byte(cmd, DID_ERROR); complete_cmd(instance, cmd); } @@ -2450,7 +2430,16 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) * commands! */ - hostdata->selecting = NULL; + if (list_del_cmd(&hostdata->unissued, cmd)) { + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + } + + if (hostdata->selecting) { + hostdata->selecting->result = DID_RESET << 16; + complete_cmd(instance, hostdata->selecting); + hostdata->selecting = NULL; + } list_for_each_entry(ncmd, &hostdata->disconnected, list) { struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd); @@ -2458,6 +2447,7 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) set_host_byte(cmd, DID_RESET); cmd->scsi_done(cmd); } + INIT_LIST_HEAD(&hostdata->disconnected); list_for_each_entry(ncmd, &hostdata->autosense, list) { struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd); @@ -2465,6 +2455,7 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) set_host_byte(cmd, DID_RESET); cmd->scsi_done(cmd); } + INIT_LIST_HEAD(&hostdata->autosense); if (hostdata->connected) { set_host_byte(hostdata->connected, DID_RESET); @@ -2472,12 +2463,6 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) hostdata->connected = NULL; } - if (hostdata->sensing) { - set_host_byte(hostdata->connected, DID_RESET); - complete_cmd(instance, hostdata->sensing); - hostdata->sensing = NULL; - } - for (i = 0; i < 8; ++i) hostdata->busy[i] = 0; #ifdef REAL_DMA |