[PATCH 5/5] AIC7xxx : Update error recovery Port of Hannes Reinecke patch to update error recovery. Routines for TARGET RESET and ABORT COMMAND are split up as the logic is quite dissimilar. Signed-off-by: Emmanuel Fusté <emmanuel.fuste@xxxxxxxxxxx> --- Accédez au courrier électronique de La Poste : www.laposte.net ; 3615 LAPOSTENET (0,34 ?/mn) ; tél : 08 92 68 13 50 (0,34?/mn)
diff -upN linux-source-2.6.16-orig2/drivers/scsi/aic7xxx/aic7xxx_osm.c linux-source-2.6.16/drivers/scsi/aic7xxx/aic7xxx_osm.c --- linux-source-2.6.16-orig2/drivers/scsi/aic7xxx/aic7xxx_osm.c 2006-03-28 10:49:09.000000000 +0200 +++ linux-source-2.6.16/drivers/scsi/aic7xxx/aic7xxx_osm.c 2006-03-28 13:55:40.000000000 +0200 @@ -373,7 +373,7 @@ static void ahc_linux_handle_scsi_status struct scb *); static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, struct scsi_cmnd *cmd); -static int ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag); +static int ahc_linux_queue_abort_cmd(struct scsi_cmnd *cmd); static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc); static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo); @@ -712,10 +712,8 @@ ahc_linux_abort(struct scsi_cmnd *cmd) { int error; - error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT); - if (error != 0) - printf("aic7xxx_abort returns 0x%x\n", error); - return (error); + error = ahc_linux_queue_abort_cmd(cmd); + return error; } /* @@ -724,12 +722,88 @@ ahc_linux_abort(struct scsi_cmnd *cmd) static int ahc_linux_dev_reset(struct scsi_cmnd *cmd) { - int error; + struct ahc_softc *ahc; + struct ahc_linux_device *dev; + struct scb *reset_scb; + u_int cdb_byte; + int retval = SUCCESS; + int paused; + int wait; + unsigned long flags; + DECLARE_COMPLETION(done); + + reset_scb = NULL; + paused = FALSE; + wait = FALSE; + ahc = *(struct ahc_softc **)cmd->device->host->hostdata; - error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET); - if (error != 0) - printf("aic7xxx_dev_reset returns 0x%x\n", error); - return (error); + scmd_printk(KERN_INFO, cmd, + "Attempting to queue a TARGET RESET message:"); + + printf("CDB:"); + for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) + printf(" 0x%x", cmd->cmnd[cdb_byte]); + printf("\n"); + + /* + * Determine if we currently own this command. + */ + dev = scsi_transport_device_data(cmd->device); + + if (dev == NULL) { + /* + * No target device for this command exists, + * so we must not still own the command. + */ + scmd_printk(KERN_INFO, cmd, "Is not an active device\n"); + return SUCCESS; + } + + /* + * Generate us a new SCB + */ + reset_scb = ahc_get_scb(ahc); + if (!reset_scb) { + scmd_printk(KERN_INFO, cmd, "No SCB available\n"); + return FAILED; + } + + reset_scb->io_ctx = cmd; + reset_scb->platform_data->dev = dev; + reset_scb->sg_count = 0; + ahc_set_residual(reset_scb, 0); + ahc_set_sense_residual(reset_scb, 0); + reset_scb->platform_data->xfer_len = 0; + reset_scb->hscb->control = MK_MESSAGE; + reset_scb->hscb->scsiid = BUILD_SCSIID(ahc, cmd); + reset_scb->hscb->lun = cmd->device->lun; + reset_scb->hscb->cdb_len = 0; + reset_scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE; + + dev->openings--; + dev->active++; + dev->commands_issued++; + + ahc_lock(ahc, &flags); + + LIST_INSERT_HEAD(&ahc->pending_scbs, reset_scb, pending_links); + ahc_queue_scb(ahc, reset_scb); + + ahc->platform_data->eh_done = &done; + ahc_unlock(ahc, &flags); + + printf("%s: Device reset code sleeping\n", ahc_name(ahc)); + if (!wait_for_completion_timeout(&done, 5 * HZ)) { + ahc_lock(ahc, &flags); + ahc->platform_data->eh_done = NULL; + ahc_unlock(ahc, &flags); + printf("%s: Device reset timer expired (active %d)\n", + ahc_name(ahc), dev->active); + retval = FAILED; + } + printf("%s: Device reset returning 0x%x\n", ahc_name(ahc), retval); + + return retval; } /* @@ -1954,75 +2028,113 @@ ahc_linux_handle_scsi_status(struct ahc_ static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, struct scsi_cmnd *cmd) { + int status; + int new_status = DID_OK; + int do_fallback = 0; + int scsi_status; + /* * Map CAM error codes into Linux Error codes. We * avoid the conversion so that the DV code has the * full error information available when making * state change decisions. */ - { - u_int new_status; - switch (ahc_cmd_get_transaction_status(cmd)) { - case CAM_REQ_INPROG: - case CAM_REQ_CMP: - case CAM_SCSI_STATUS_ERROR: - new_status = DID_OK; - break; - case CAM_REQ_ABORTED: - new_status = DID_ABORT; - break; - case CAM_BUSY: - new_status = DID_BUS_BUSY; - break; - case CAM_REQ_INVALID: - case CAM_PATH_INVALID: - new_status = DID_BAD_TARGET; - break; - case CAM_SEL_TIMEOUT: - new_status = DID_NO_CONNECT; - break; - case CAM_SCSI_BUS_RESET: - case CAM_BDR_SENT: - new_status = DID_RESET; - break; - case CAM_UNCOR_PARITY: - new_status = DID_PARITY; - break; - case CAM_CMD_TIMEOUT: - new_status = DID_TIME_OUT; - break; - case CAM_UA_ABORT: - case CAM_REQ_CMP_ERR: - case CAM_AUTOSENSE_FAIL: - case CAM_NO_HBA: - case CAM_DATA_RUN_ERR: - case CAM_UNEXP_BUSFREE: - case CAM_SEQUENCE_FAIL: - case CAM_CCB_LEN_ERR: - case CAM_PROVIDE_FAIL: - case CAM_REQ_TERMIO: - case CAM_UNREC_HBA_ERROR: - case CAM_REQ_TOO_BIG: - new_status = DID_ERROR; - break; - case CAM_REQUEUE_REQ: - new_status = DID_REQUEUE; + status = ahc_cmd_get_transaction_status(cmd); + switch (status) { + case CAM_REQ_INPROG: + case CAM_REQ_CMP: + new_status = DID_OK; + break; + case CAM_AUTOSENSE_FAIL: + new_status = DID_ERROR; + /* Fallthrough */ + case CAM_SCSI_STATUS_ERROR: + scsi_status = ahc_cmd_get_scsi_status(cmd); + + switch(scsi_status) { + case SCSI_STATUS_CMD_TERMINATED: + case SCSI_STATUS_CHECK_COND: + if ((cmd->result >> 24) != DRIVER_SENSE) { + do_fallback = 1; + } else { + struct scsi_sense_data *sense; + + sense = (struct scsi_sense_data *) + &cmd->sense_buffer; + if (sense->extra_len >=5 && + (sense->add_sense_code == 0x47 + || sense->add_sense_code == 0x48)) + do_fallback = 1; + } break; default: - /* We should never get here */ - new_status = DID_ERROR; break; } + break; + case CAM_REQ_ABORTED: + new_status = DID_ABORT; + break; + case CAM_BUSY: + new_status = DID_BUS_BUSY; + break; + case CAM_REQ_INVALID: + case CAM_PATH_INVALID: + new_status = DID_BAD_TARGET; + break; + case CAM_SEL_TIMEOUT: + new_status = DID_NO_CONNECT; + break; + case CAM_SCSI_BUS_RESET: + case CAM_BDR_SENT: + new_status = DID_RESET; + break; + case CAM_UNCOR_PARITY: + new_status = DID_PARITY; + do_fallback = 1; + break; + case CAM_CMD_TIMEOUT: + new_status = DID_TIME_OUT; + do_fallback = 1; + break; + case CAM_REQ_CMP_ERR: + case CAM_UNEXP_BUSFREE: + case CAM_DATA_RUN_ERR: + new_status = DID_ERROR; + do_fallback = 1; + break; + case CAM_UA_ABORT: + case CAM_NO_HBA: + case CAM_SEQUENCE_FAIL: + case CAM_CCB_LEN_ERR: + case CAM_PROVIDE_FAIL: + case CAM_REQ_TERMIO: + case CAM_UNREC_HBA_ERROR: + case CAM_REQ_TOO_BIG: + new_status = DID_ERROR; + break; + case CAM_REQUEUE_REQ: + new_status = DID_REQUEUE; + break; + default: + /* We should never get here */ + new_status = DID_ERROR; + break; + } - ahc_cmd_set_transaction_status(cmd, new_status); + if (do_fallback) { + printf("%s: device overrun (status %x) on %d:%d:%d\n", + ahc_name(ahc), status, cmd->device->channel, + cmd->device->id, cmd->device->lun); } + ahc_cmd_set_transaction_status(cmd, new_status); + cmd->scsi_done(cmd); } static int -ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) +ahc_linux_queue_abort_cmd(struct scsi_cmnd *cmd) { struct ahc_softc *ahc; struct ahc_linux_device *dev; @@ -2044,8 +2156,7 @@ ahc_linux_queue_recovery_cmd(struct scsi wait = FALSE; ahc = *(struct ahc_softc **)cmd->device->host->hostdata; - scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message\n", - flag == SCB_ABORT ? "n ABORT" : " TARGET RESET"); + scmd_printk(KERN_INFO, cmd, "Attempting to queue an ABORT message:"); printf("CDB:"); for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) @@ -2095,18 +2206,6 @@ ahc_linux_queue_recovery_cmd(struct scsi break; } - if (pending_scb == NULL && flag == SCB_DEVICE_RESET) { - - /* Any SCB for this device will do for a target reset */ - LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) { - if (ahc_match_scb(ahc, pending_scb, scmd_id(cmd), - scmd_channel(cmd) + 'A', - CAM_LUN_WILDCARD, - SCB_LIST_NULL, ROLE_INITIATOR)) - break; - } - } - if (pending_scb == NULL) { scmd_printk(KERN_INFO, cmd, "Command not found\n"); goto no_cmd; @@ -2139,25 +2238,17 @@ ahc_linux_queue_recovery_cmd(struct scsi ahc_dump_card_state(ahc); disconnected = TRUE; - if (flag == SCB_ABORT) { - if (ahc_search_qinfifo(ahc, cmd->device->id, - cmd->device->channel + 'A', - cmd->device->lun, - pending_scb->hscb->tag, - ROLE_INITIATOR, CAM_REQ_ABORTED, - SEARCH_COMPLETE) > 0) { - printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", - ahc_name(ahc), cmd->device->channel, - cmd->device->id, cmd->device->lun); - retval = SUCCESS; - goto done; - } - } else if (ahc_search_qinfifo(ahc, cmd->device->id, - cmd->device->channel + 'A', - cmd->device->lun, pending_scb->hscb->tag, - ROLE_INITIATOR, /*status*/0, - SEARCH_COUNT) > 0) { - disconnected = FALSE; + if (ahc_search_qinfifo(ahc, cmd->device->id, + cmd->device->channel + 'A', + cmd->device->lun, + pending_scb->hscb->tag, + ROLE_INITIATOR, CAM_REQ_ABORTED, + SEARCH_COMPLETE) > 0) { + printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", + ahc_name(ahc), cmd->device->channel, + cmd->device->id, cmd->device->lun); + retval = SUCCESS; + goto done; } if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { @@ -2166,10 +2257,6 @@ ahc_linux_queue_recovery_cmd(struct scsi bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG)); if (bus_scb == pending_scb) disconnected = FALSE; - else if (flag != SCB_ABORT - && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid - && ahc_inb(ahc, SAVED_LUN) == SCB_GET_LUN(pending_scb)) - disconnected = FALSE; } /* @@ -2185,16 +2272,14 @@ ahc_linux_queue_recovery_cmd(struct scsi active_scb_index = ahc_inb(ahc, SCB_TAG); saved_scsiid = ahc_inb(ahc, SAVED_SCSIID); if (last_phase != P_BUSFREE - && (pending_scb->hscb->tag == active_scb_index - || (flag == SCB_DEVICE_RESET - && SCSIID_TARGET(ahc, saved_scsiid) == scmd_id(cmd)))) { + && (pending_scb->hscb->tag) == active_scb_index ) { /* * We're active on the bus, so assert ATN * and hope that the target responds. */ pending_scb = ahc_lookup_scb(ahc, active_scb_index); - pending_scb->flags |= SCB_RECOVERY_SCB|flag; + pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; ahc_outb(ahc, MSG_OUT, HOST_MSG); ahc_outb(ahc, SCSISIGO, last_phase|ATNO); scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n"); @@ -2218,7 +2303,7 @@ ahc_linux_queue_recovery_cmd(struct scsi * an unsolicited reselection occurred. */ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; - pending_scb->flags |= SCB_RECOVERY_SCB|flag; + pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; /* * Remove any cached copy of this SCB in the Common subdirectories: linux-source-2.6.16-orig2/drivers/scsi/aic7xxx/aicasm and linux-source-2.6.16/drivers/scsi/aic7xxx/aicasm