diff options
Diffstat (limited to 'drivers/scsi/atari_NCR5380.c')
-rw-r--r-- | drivers/scsi/atari_NCR5380.c | 287 |
1 files changed, 46 insertions, 241 deletions
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index 809ef25b7049..12b98cbe7b44 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c @@ -175,10 +175,6 @@ * possible) function may be used. */ -#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble) -#define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next)) -#define NEXTADDR(cmd) ((struct scsi_cmnd **)&(cmd)->host_scribble) - #define HOSTNO instance->host_no static int do_abort(struct Scsi_Host *); @@ -665,8 +661,9 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) #endif spin_lock_init(&hostdata->lock); hostdata->connected = NULL; - hostdata->issue_queue = NULL; - hostdata->disconnected_queue = NULL; + INIT_LIST_HEAD(&hostdata->unissued); + INIT_LIST_HEAD(&hostdata->disconnected); + hostdata->flags = flags; INIT_WORK(&hostdata->main_task, NCR5380_main); @@ -781,7 +778,7 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, struct scsi_cmnd *cmd) { struct NCR5380_hostdata *hostdata = shost_priv(instance); - struct scsi_cmnd *tmp; + struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd); unsigned long flags; #if (NDEBUG & NDEBUG_NO_WRITE) @@ -795,12 +792,6 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, } #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ - /* - * We use the host_scribble field as a pointer to the next command - * in a queue - */ - - SET_NEXT(cmd, NULL); cmd->result = 0; /* @@ -834,15 +825,11 @@ static int NCR5380_queue_command(struct Scsi_Host *instance, * sense data is only guaranteed to be valid while the condition exists. */ - if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { - SET_NEXT(cmd, hostdata->issue_queue); - hostdata->issue_queue = cmd; - } else { - for (tmp = (struct scsi_cmnd *)hostdata->issue_queue; - NEXT(tmp); tmp = NEXT(tmp)) - ; - SET_NEXT(tmp, cmd); - } + if (cmd->cmnd[0] == REQUEST_SENSE) + list_add(&ncmd->list, &hostdata->unissued); + else + list_add_tail(&ncmd->list, &hostdata->unissued); + spin_unlock_irqrestore(&hostdata->lock, flags); dsprintk(NDEBUG_QUEUES, instance, "command %p added to %s of queue\n", @@ -858,8 +845,8 @@ static inline void maybe_release_dma_irq(struct Scsi_Host *instance) struct NCR5380_hostdata *hostdata = shost_priv(instance); /* Caller does the locking needed to set & test these data atomically */ - if (!hostdata->disconnected_queue && - !hostdata->issue_queue && + if (list_empty(&hostdata->disconnected) && + list_empty(&hostdata->unissued) && !hostdata->connected && !hostdata->retain_dma_intr) NCR5380_release_dma_irq(instance); @@ -881,7 +868,7 @@ 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 *tmp, *prev; + struct NCR5380_cmd *ncmd, *n; int done; /* @@ -900,17 +887,9 @@ static void NCR5380_main(struct work_struct *work) * Search through the issue_queue for a command destined * for a target that's not busy. */ -#if (NDEBUG & NDEBUG_LISTS) - for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; - tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) - ; - /*printk("%p ", tmp);*/ - if ((tmp == prev) && tmp) - printk(" LOOP\n"); - /* else printk("\n"); */ -#endif - for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, - prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) { + list_for_each_entry_safe(ncmd, n, &hostdata->unissued, + list) { + struct scsi_cmnd *tmp = NCR5380_to_scmd(ncmd); u8 lun = tmp->device->lun; dsprintk(NDEBUG_QUEUES, instance, "main: tmp=%p target=%d busy=%d lun=%d\n", @@ -923,15 +902,10 @@ static void NCR5380_main(struct work_struct *work) !(hostdata->busy[tmp->device->id] & (1 << lun)) #endif ) { - if (prev) { - SET_NEXT(prev, NEXT(tmp)); - } else { - hostdata->issue_queue = NEXT(tmp); - } - SET_NEXT(tmp, NULL); + list_del(&ncmd->list); dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, - instance, "main: removed %p from issue queue %p\n", - tmp, prev); + instance, "main: removed %p from issue queue\n", + tmp); hostdata->retain_dma_intr++; @@ -960,8 +934,7 @@ static void NCR5380_main(struct work_struct *work) maybe_release_dma_irq(instance); } else { /* Need to retry */ - SET_NEXT(tmp, hostdata->issue_queue); - hostdata->issue_queue = tmp; + list_add(&ncmd->list, &hostdata->unissued); dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance, "main: select() failed, %p returned to issue queue\n", tmp); @@ -1834,6 +1807,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) #endif while ((cmd = hostdata->connected)) { + struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd); + tmp = NCR5380_read(STATUS_REG); /* We only have a valid SCSI phase when REQ is asserted */ if (tmp & SR_REQ) { @@ -2042,8 +2017,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) { scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0); - SET_NEXT(cmd, hostdata->issue_queue); - hostdata->issue_queue = (struct scsi_cmnd *) cmd; + list_add(&ncmd->list, &hostdata->unissued); dsprintk(NDEBUG_AUTOSENSE | NDEBUG_QUEUES, instance, "REQUEST SENSE cmd %p added to head of issue queue\n", cmd); @@ -2092,9 +2066,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) case DISCONNECT: /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - SET_NEXT(cmd, hostdata->disconnected_queue); hostdata->connected = NULL; - hostdata->disconnected_queue = cmd; + list_add(&ncmd->list, &hostdata->disconnected); dsprintk(NDEBUG_INFORMATION | NDEBUG_QUEUES, instance, "connected command %p for target %d lun %llu moved to disconnected queue\n", cmd, scmd_id(cmd), cmd->device->lun); @@ -2288,7 +2261,8 @@ static void NCR5380_reselect(struct Scsi_Host *instance) unsigned char msg[3]; int __maybe_unused len; unsigned char __maybe_unused *data, __maybe_unused phase; - struct scsi_cmnd *tmp = NULL, *prev; + struct NCR5380_cmd *ncmd; + struct scsi_cmnd *tmp; /* * Disable arbitration, etc. since the host adapter obviously @@ -2379,19 +2353,18 @@ static void NCR5380_reselect(struct Scsi_Host *instance) * just reestablished, and remove it from the disconnected queue. */ - for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL; - tmp; prev = tmp, tmp = NEXT(tmp)) { - if ((target_mask == (1 << tmp->device->id)) && (lun == (u8)tmp->device->lun) + tmp = NULL; + list_for_each_entry(ncmd, &hostdata->disconnected, list) { + struct scsi_cmnd *cmd = NCR5380_to_scmd(ncmd); + + if (target_mask == (1 << scmd_id(cmd)) && + lun == (u8)cmd->device->lun #ifdef SUPPORT_TAGS - && (tag == tmp->tag) + && (tag == cmd->tag) #endif ) { - if (prev) { - SET_NEXT(prev, NEXT(tmp)); - } else { - hostdata->disconnected_queue = NEXT(tmp); - } - SET_NEXT(tmp, NULL); + list_del(&ncmd->list); + tmp = cmd; break; } } @@ -2489,188 +2462,18 @@ int NCR5380_abort(struct scsi_cmnd *cmd) { struct Scsi_Host *instance = cmd->device->host; struct NCR5380_hostdata *hostdata = shost_priv(instance); - struct scsi_cmnd *tmp, **prev; unsigned long flags; - scmd_printk(KERN_NOTICE, cmd, "aborting command\n"); - spin_lock_irqsave(&hostdata->lock, flags); +#if (NDEBUG & NDEBUG_ANY) + scmd_printk(KERN_INFO, cmd, "aborting command\n"); +#endif NCR5380_dprint(NDEBUG_ANY, instance); NCR5380_dprint_phase(NDEBUG_ANY, instance); - dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, - NCR5380_read(BUS_AND_STATUS_REG), - NCR5380_read(STATUS_REG)); - -#if 1 - /* - * Case 1 : If the command is the currently executing command, - * we'll set the aborted flag and return control so that - * information transfer routine can exit cleanly. - */ - - if (hostdata->connected == cmd) { - - dprintk(NDEBUG_ABORT, "scsi%d: aborting connected command\n", HOSTNO); - /* - * We should perform BSY checking, and make sure we haven't slipped - * into BUS FREE. - */ - - /* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */ - /* - * Since we can't change phases until we've completed the current - * handshake, we have to source or sink a byte of data if the current - * phase is not MSGOUT. - */ - - /* - * Return control to the executing NCR drive so we can clear the - * aborted flag and get back into our main loop. - */ - - if (do_abort(instance) == 0) { - hostdata->connected = NULL; - cmd->result = DID_ABORT << 16; -#ifdef SUPPORT_TAGS - cmd_free_tag(cmd); -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - maybe_release_dma_irq(instance); - spin_unlock_irqrestore(&hostdata->lock, flags); - cmd->scsi_done(cmd); - return SUCCESS; - } else { - spin_unlock_irqrestore(&hostdata->lock, flags); - printk("scsi%d: abort of connected command failed!\n", HOSTNO); - return FAILED; - } - } -#endif - - /* - * Case 2 : If the command hasn't been issued yet, we simply remove it - * from the issue queue. - */ - for (prev = (struct scsi_cmnd **)&(hostdata->issue_queue), - tmp = (struct scsi_cmnd *)hostdata->issue_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) { - if (cmd == tmp) { - (*prev) = NEXT(tmp); - SET_NEXT(tmp, NULL); - tmp->result = DID_ABORT << 16; - maybe_release_dma_irq(instance); - spin_unlock_irqrestore(&hostdata->lock, flags); - dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n", - HOSTNO); - /* Tagged queuing note: no tag to free here, hasn't been assigned - * yet... */ - tmp->scsi_done(tmp); - return SUCCESS; - } - } - - /* - * Case 3 : If any commands are connected, we're going to fail the abort - * and let the high level SCSI driver retry at a later time or - * issue a reset. - * - * Timeouts, and therefore aborted commands, will be highly unlikely - * and handling them cleanly in this situation would make the common - * case of noresets less efficient, and would pollute our code. So, - * we fail. - */ - - if (hostdata->connected) { - spin_unlock_irqrestore(&hostdata->lock, flags); - dprintk(NDEBUG_ABORT, "scsi%d: abort failed, command connected.\n", HOSTNO); - return FAILED; - } - - /* - * Case 4: If the command is currently disconnected from the bus, and - * there are no connected commands, we reconnect the I_T_L or - * I_T_L_Q nexus associated with it, go into message out, and send - * an abort message. - * - * This case is especially ugly. In order to reestablish the nexus, we - * need to call NCR5380_select(). The easiest way to implement this - * function was to abort if the bus was busy, and let the interrupt - * handler triggered on the SEL for reselect take care of lost arbitrations - * where necessary, meaning interrupts need to be enabled. - * - * When interrupts are enabled, the queues may change - so we - * can't remove it from the disconnected queue before selecting it - * because that could cause a failure in hashing the nexus if that - * device reselected. - * - * Since the queues may change, we can't use the pointers from when we - * first locate it. - * - * So, we must first locate the command, and if NCR5380_select() - * succeeds, then issue the abort, relocate the command and remove - * it from the disconnected queue. - */ - - for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; - tmp = NEXT(tmp)) { - if (cmd == tmp) { - dprintk(NDEBUG_ABORT, "scsi%d: aborting disconnected command.\n", HOSTNO); - - if (NCR5380_select(instance, cmd)) { - spin_unlock_irq(&hostdata->lock); - return FAILED; - } - dprintk(NDEBUG_ABORT, "scsi%d: nexus reestablished.\n", HOSTNO); - - do_abort(instance); - - for (prev = (struct scsi_cmnd **)&(hostdata->disconnected_queue), - tmp = (struct scsi_cmnd *)hostdata->disconnected_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) { - if (cmd == tmp) { - *prev = NEXT(tmp); - SET_NEXT(tmp, NULL); - tmp->result = DID_ABORT << 16; - /* We must unlock the tag/LUN immediately here, since the - * target goes to BUS FREE and doesn't send us another - * message (COMMAND_COMPLETE or the like) - */ -#ifdef SUPPORT_TAGS - cmd_free_tag(tmp); -#else - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); -#endif - maybe_release_dma_irq(instance); - spin_unlock_irqrestore(&hostdata->lock, flags); - tmp->scsi_done(tmp); - return SUCCESS; - } - } - } - } - - /* Maybe it is sufficient just to release the ST-DMA lock... (if - * possible at all) At least, we should check if the lock could be - * released after the abort, in case it is kept due to some bug. - */ - maybe_release_dma_irq(instance); spin_unlock_irqrestore(&hostdata->lock, flags); - /* - * Case 5 : If we reached this point, the command was not found in any of - * the queues. - * - * We probably reached this point because of an unlikely race condition - * between the command completing successfully and the abortion code, - * so we won't panic, but we will notify the user in case something really - * broke. - */ - - printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO); - return FAILED; } @@ -2710,16 +2513,18 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) * commands! */ - if (hostdata->issue_queue) - dsprintk(NDEBUG_ABORT, instance, "reset aborted issued command(s)\n"); if (hostdata->connected) dsprintk(NDEBUG_ABORT, instance, "reset aborted a connected command\n"); - if (hostdata->disconnected_queue) - dsprintk(NDEBUG_ABORT, instance, "reset aborted disconnected command(s)\n"); - - hostdata->issue_queue = NULL; hostdata->connected = NULL; - hostdata->disconnected_queue = NULL; + + if (!list_empty(&hostdata->unissued)) + dsprintk(NDEBUG_ABORT, instance, "reset aborted unissued list\n"); + INIT_LIST_HEAD(&hostdata->unissued); + + if (!list_empty(&hostdata->disconnected)) + dsprintk(NDEBUG_ABORT, instance, "reset aborted disconnected list\n"); + INIT_LIST_HEAD(&hostdata->disconnected); + #ifdef SUPPORT_TAGS free_all_tags(hostdata); #endif |