X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_error.c;h=b8edcf5b54511be2f10fc9690fd01fbdd4dfb99e;hb=23d046f43a05155e050a68f3ad1f19b672713374;hp=3d355d054612bd0ec6a30cda4af65ff8aacd6001;hpb=0612ec48762bf8712db1925b2e67246d2237ebab;p=linux-2.6-omap-h63xx.git diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 3d355d05461..b8edcf5b545 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -359,6 +359,11 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return SUCCESS; case MEDIUM_ERROR: + if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ + sshdr.asc == 0x13 || /* AMNF DATA FIELD */ + sshdr.asc == 0x14) { /* RECORD NOT FOUND */ + return SUCCESS; + } return NEEDS_RETRY; case HARDWARE_ERROR: @@ -453,9 +458,140 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) } /** - * scsi_send_eh_cmnd - send a cmd to a device as part of error recovery. - * @scmd: SCSI Cmd to send. - * @timeout: Timeout for cmd. + * scsi_try_host_reset - ask host adapter to reset itself + * @scmd: SCSI cmd to send hsot reset. + **/ +static int scsi_try_host_reset(struct scsi_cmnd *scmd) +{ + unsigned long flags; + int rtn; + + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n", + __FUNCTION__)); + + if (!scmd->device->host->hostt->eh_host_reset_handler) + return FAILED; + + rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd); + + if (rtn == SUCCESS) { + if (!scmd->device->host->hostt->skip_settle_delay) + ssleep(HOST_RESET_SETTLE_TIME); + spin_lock_irqsave(scmd->device->host->host_lock, flags); + scsi_report_bus_reset(scmd->device->host, + scmd_channel(scmd)); + spin_unlock_irqrestore(scmd->device->host->host_lock, flags); + } + + return rtn; +} + +/** + * scsi_try_bus_reset - ask host to perform a bus reset + * @scmd: SCSI cmd to send bus reset. + **/ +static int scsi_try_bus_reset(struct scsi_cmnd *scmd) +{ + unsigned long flags; + int rtn; + + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n", + __FUNCTION__)); + + if (!scmd->device->host->hostt->eh_bus_reset_handler) + return FAILED; + + rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd); + + if (rtn == SUCCESS) { + if (!scmd->device->host->hostt->skip_settle_delay) + ssleep(BUS_RESET_SETTLE_TIME); + spin_lock_irqsave(scmd->device->host->host_lock, flags); + scsi_report_bus_reset(scmd->device->host, + scmd_channel(scmd)); + spin_unlock_irqrestore(scmd->device->host->host_lock, flags); + } + + return rtn; +} + +/** + * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev + * @scmd: SCSI cmd used to send BDR + * + * Notes: + * There is no timeout for this operation. if this operation is + * unreliable for a given host, then the host itself needs to put a + * timer on it, and set the host back to a consistent state prior to + * returning. + **/ +static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) +{ + int rtn; + + if (!scmd->device->host->hostt->eh_device_reset_handler) + return FAILED; + + rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd); + if (rtn == SUCCESS) { + scmd->device->was_reset = 1; + scmd->device->expecting_cc_ua = 1; + } + + return rtn; +} + +static int __scsi_try_to_abort_cmd(struct scsi_cmnd *scmd) +{ + if (!scmd->device->host->hostt->eh_abort_handler) + return FAILED; + + return scmd->device->host->hostt->eh_abort_handler(scmd); +} + +/** + * scsi_try_to_abort_cmd - Ask host to abort a running command. + * @scmd: SCSI cmd to abort from Lower Level. + * + * Notes: + * This function will not return until the user's completion function + * has been called. there is no timeout on this operation. if the + * author of the low-level driver wishes this operation to be timed, + * they can provide this facility themselves. helper functions in + * scsi_error.c can be supplied to make this easier to do. + **/ +static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd) +{ + /* + * scsi_done was called just after the command timed out and before + * we had a chance to process it. (db) + */ + if (scmd->serial_number == 0) + return SUCCESS; + return __scsi_try_to_abort_cmd(scmd); +} + +static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd) +{ + if (__scsi_try_to_abort_cmd(scmd) != SUCCESS) + if (scsi_try_bus_device_reset(scmd) != SUCCESS) + if (scsi_try_bus_reset(scmd) != SUCCESS) + scsi_try_host_reset(scmd); +} + +/** + * scsi_send_eh_cmnd - submit a scsi command as part of error recory + * @scmd: SCSI command structure to hijack + * @cmnd: CDB to send + * @cmnd_size: size in bytes of @cmnd + * @timeout: timeout for this request + * @copy_sense: request sense data if set to 1 + * + * This function is used to send a scsi command down to a target device + * as part of the error recovery process. If @copy_sense is 0 the command + * sent must be one that does not transfer any data. If @copy_sense is 1 + * the command must be REQUEST_SENSE and this functions copies out the + * sense buffer it got into @scmd->sense_buffer. * * Return value: * SUCCESS or FAILED or NEEDS_RETRY @@ -469,6 +605,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, DECLARE_COMPLETION_ONSTACK(done); unsigned long timeleft; unsigned long flags; + struct scatterlist sgl; unsigned char old_cmnd[MAX_COMMAND_SIZE]; enum dma_data_direction old_data_direction; unsigned short old_use_sg; @@ -495,24 +632,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, memcpy(scmd->cmnd, cmnd, cmnd_size); if (copy_sense) { - int gfp_mask = GFP_ATOMIC; + gfp_t gfp_mask = GFP_ATOMIC; if (shost->hostt->unchecked_isa_dma) gfp_mask |= __GFP_DMA; - scmd->sc_data_direction = DMA_FROM_DEVICE; - scmd->request_bufflen = 252; - scmd->request_buffer = kzalloc(scmd->request_bufflen, gfp_mask); - if (!scmd->request_buffer) + sgl.page = alloc_page(gfp_mask); + if (!sgl.page) return FAILED; + sgl.offset = 0; + sgl.length = 252; + + scmd->sc_data_direction = DMA_FROM_DEVICE; + scmd->request_bufflen = sgl.length; + scmd->request_buffer = &sgl; + scmd->use_sg = 1; } else { scmd->request_buffer = NULL; scmd->request_bufflen = 0; scmd->sc_data_direction = DMA_NONE; + scmd->use_sg = 0; } scmd->underflow = 0; - scmd->use_sg = 0; scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); if (sdev->scsi_level <= SCSI_2) @@ -564,13 +706,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, break; } } else { - /* - * FIXME(eric) - we are not tracking whether we could - * abort a timed out command or not. not sure how - * we should treat them differently anyways. - */ - if (shost->hostt->eh_abort_handler) - shost->hostt->eh_abort_handler(scmd); + scsi_abort_eh_cmnd(scmd); rtn = FAILED; } @@ -583,7 +719,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, memcpy(scmd->sense_buffer, scmd->request_buffer, sizeof(scmd->sense_buffer)); } - kfree(scmd->request_buffer); + __free_page(sgl.page); } @@ -657,8 +793,8 @@ EXPORT_SYMBOL(scsi_eh_finish_cmd); * XXX: Long term this code should go away, but that needs an audit of * all LLDDs first. **/ -static int scsi_eh_get_sense(struct list_head *work_q, - struct list_head *done_q) +int scsi_eh_get_sense(struct list_head *work_q, + struct list_head *done_q) { struct scsi_cmnd *scmd, *next; int rtn; @@ -700,31 +836,7 @@ static int scsi_eh_get_sense(struct list_head *work_q, return list_empty(work_q); } - -/** - * scsi_try_to_abort_cmd - Ask host to abort a running command. - * @scmd: SCSI cmd to abort from Lower Level. - * - * Notes: - * This function will not return until the user's completion function - * has been called. there is no timeout on this operation. if the - * author of the low-level driver wishes this operation to be timed, - * they can provide this facility themselves. helper functions in - * scsi_error.c can be supplied to make this easier to do. - **/ -static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd) -{ - if (!scmd->device->host->hostt->eh_abort_handler) - return FAILED; - - /* - * scsi_done was called just after the command timed out and before - * we had a chance to process it. (db) - */ - if (scmd->serial_number == 0) - return SUCCESS; - return scmd->device->host->hostt->eh_abort_handler(scmd); -} +EXPORT_SYMBOL_GPL(scsi_eh_get_sense); /** * scsi_eh_tur - Send TUR to device. @@ -799,32 +911,6 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, return list_empty(work_q); } -/** - * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev - * @scmd: SCSI cmd used to send BDR - * - * Notes: - * There is no timeout for this operation. if this operation is - * unreliable for a given host, then the host itself needs to put a - * timer on it, and set the host back to a consistent state prior to - * returning. - **/ -static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) -{ - int rtn; - - if (!scmd->device->host->hostt->eh_device_reset_handler) - return FAILED; - - rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd); - if (rtn == SUCCESS) { - scmd->device->was_reset = 1; - scmd->device->expecting_cc_ua = 1; - } - - return rtn; -} - /** * scsi_eh_try_stu - Send START_UNIT to device. * @scmd: Scsi cmd to send START_UNIT @@ -955,64 +1041,6 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, return list_empty(work_q); } -/** - * scsi_try_bus_reset - ask host to perform a bus reset - * @scmd: SCSI cmd to send bus reset. - **/ -static int scsi_try_bus_reset(struct scsi_cmnd *scmd) -{ - unsigned long flags; - int rtn; - - SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n", - __FUNCTION__)); - - if (!scmd->device->host->hostt->eh_bus_reset_handler) - return FAILED; - - rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd); - - if (rtn == SUCCESS) { - if (!scmd->device->host->hostt->skip_settle_delay) - ssleep(BUS_RESET_SETTLE_TIME); - spin_lock_irqsave(scmd->device->host->host_lock, flags); - scsi_report_bus_reset(scmd->device->host, - scmd_channel(scmd)); - spin_unlock_irqrestore(scmd->device->host->host_lock, flags); - } - - return rtn; -} - -/** - * scsi_try_host_reset - ask host adapter to reset itself - * @scmd: SCSI cmd to send hsot reset. - **/ -static int scsi_try_host_reset(struct scsi_cmnd *scmd) -{ - unsigned long flags; - int rtn; - - SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n", - __FUNCTION__)); - - if (!scmd->device->host->hostt->eh_host_reset_handler) - return FAILED; - - rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd); - - if (rtn == SUCCESS) { - if (!scmd->device->host->hostt->skip_settle_delay) - ssleep(HOST_RESET_SETTLE_TIME); - spin_lock_irqsave(scmd->device->host->host_lock, flags); - scsi_report_bus_reset(scmd->device->host, - scmd_channel(scmd)); - spin_unlock_irqrestore(scmd->device->host->host_lock, flags); - } - - return rtn; -} - /** * scsi_eh_bus_reset - send a bus reset * @shost: scsi host being recovered. @@ -1396,9 +1424,9 @@ static void scsi_restart_operations(struct Scsi_Host *shost) * @eh_done_q: list_head for processed commands. * **/ -static void scsi_eh_ready_devs(struct Scsi_Host *shost, - struct list_head *work_q, - struct list_head *done_q) +void scsi_eh_ready_devs(struct Scsi_Host *shost, + struct list_head *work_q, + struct list_head *done_q) { if (!scsi_eh_stu(shost, work_q, done_q)) if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) @@ -1406,6 +1434,7 @@ static void scsi_eh_ready_devs(struct Scsi_Host *shost, if (!scsi_eh_host_reset(work_q, done_q)) scsi_eh_offline_sdevs(work_q, done_q); } +EXPORT_SYMBOL_GPL(scsi_eh_ready_devs); /** * scsi_eh_flush_done_q - finish processed commands or retry them.