diff options
Diffstat (limited to 'drivers/scsi/scsi_error.c')
-rw-r--r-- | drivers/scsi/scsi_error.c | 162 |
1 files changed, 92 insertions, 70 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 8a525abda30..d29f8464b74 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | #include "scsi_priv.h" | 38 | #include "scsi_priv.h" |
39 | #include "scsi_logging.h" | 39 | #include "scsi_logging.h" |
40 | #include "scsi_transport_api.h" | ||
40 | 41 | ||
41 | #define SENSE_TIMEOUT (10*HZ) | 42 | #define SENSE_TIMEOUT (10*HZ) |
42 | 43 | ||
@@ -589,39 +590,23 @@ static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd) | |||
589 | } | 590 | } |
590 | 591 | ||
591 | /** | 592 | /** |
592 | * scsi_send_eh_cmnd - submit a scsi command as part of error recory | 593 | * scsi_eh_prep_cmnd - Save a scsi command info as part of error recory |
593 | * @scmd: SCSI command structure to hijack | 594 | * @scmd: SCSI command structure to hijack |
594 | * @cmnd: CDB to send | 595 | * @ses: structure to save restore information |
596 | * @cmnd: CDB to send. Can be NULL if no new cmnd is needed | ||
595 | * @cmnd_size: size in bytes of @cmnd | 597 | * @cmnd_size: size in bytes of @cmnd |
596 | * @timeout: timeout for this request | 598 | * @sense_bytes: size of sense data to copy. or 0 (if != 0 @cmnd is ignored) |
597 | * @copy_sense: request sense data if set to 1 | ||
598 | * | ||
599 | * This function is used to send a scsi command down to a target device | ||
600 | * as part of the error recovery process. If @copy_sense is 0 the command | ||
601 | * sent must be one that does not transfer any data. If @copy_sense is 1 | ||
602 | * the command must be REQUEST_SENSE and this functions copies out the | ||
603 | * sense buffer it got into @scmd->sense_buffer. | ||
604 | * | 599 | * |
605 | * Return value: | 600 | * This function is used to save a scsi command information before re-execution |
606 | * SUCCESS or FAILED or NEEDS_RETRY | 601 | * as part of the error recovery process. If @sense_bytes is 0 the command |
602 | * sent must be one that does not transfer any data. If @sense_bytes != 0 | ||
603 | * @cmnd is ignored and this functions sets up a REQUEST_SENSE command | ||
604 | * and cmnd buffers to read @sense_bytes into @scmd->sense_buffer. | ||
607 | **/ | 605 | **/ |
608 | static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | 606 | void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, |
609 | int cmnd_size, int timeout, int copy_sense) | 607 | unsigned char *cmnd, int cmnd_size, unsigned sense_bytes) |
610 | { | 608 | { |
611 | struct scsi_device *sdev = scmd->device; | 609 | struct scsi_device *sdev = scmd->device; |
612 | struct Scsi_Host *shost = sdev->host; | ||
613 | int old_result = scmd->result; | ||
614 | DECLARE_COMPLETION_ONSTACK(done); | ||
615 | unsigned long timeleft; | ||
616 | unsigned long flags; | ||
617 | struct scatterlist sgl; | ||
618 | unsigned char old_cmnd[MAX_COMMAND_SIZE]; | ||
619 | enum dma_data_direction old_data_direction; | ||
620 | unsigned short old_use_sg; | ||
621 | unsigned char old_cmd_len; | ||
622 | unsigned old_bufflen; | ||
623 | void *old_buffer; | ||
624 | int rtn; | ||
625 | 610 | ||
626 | /* | 611 | /* |
627 | * We need saved copies of a number of fields - this is because | 612 | * We need saved copies of a number of fields - this is because |
@@ -630,35 +615,42 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
630 | * we will need to restore these values prior to running the actual | 615 | * we will need to restore these values prior to running the actual |
631 | * command. | 616 | * command. |
632 | */ | 617 | */ |
633 | old_buffer = scmd->request_buffer; | 618 | ses->cmd_len = scmd->cmd_len; |
634 | old_bufflen = scmd->request_bufflen; | 619 | memcpy(ses->cmnd, scmd->cmnd, sizeof(scmd->cmnd)); |
635 | memcpy(old_cmnd, scmd->cmnd, sizeof(scmd->cmnd)); | 620 | ses->data_direction = scmd->sc_data_direction; |
636 | old_data_direction = scmd->sc_data_direction; | 621 | ses->bufflen = scmd->request_bufflen; |
637 | old_cmd_len = scmd->cmd_len; | 622 | ses->buffer = scmd->request_buffer; |
638 | old_use_sg = scmd->use_sg; | 623 | ses->use_sg = scmd->use_sg; |
639 | 624 | ses->resid = scmd->resid; | |
640 | memset(scmd->cmnd, 0, sizeof(scmd->cmnd)); | 625 | ses->result = scmd->result; |
641 | memcpy(scmd->cmnd, cmnd, cmnd_size); | 626 | |
642 | 627 | if (sense_bytes) { | |
643 | if (copy_sense) { | 628 | scmd->request_bufflen = min_t(unsigned, |
644 | sg_init_one(&sgl, scmd->sense_buffer, | 629 | sizeof(scmd->sense_buffer), sense_bytes); |
645 | sizeof(scmd->sense_buffer)); | 630 | sg_init_one(&ses->sense_sgl, scmd->sense_buffer, |
646 | 631 | scmd->request_bufflen); | |
632 | scmd->request_buffer = &ses->sense_sgl; | ||
647 | scmd->sc_data_direction = DMA_FROM_DEVICE; | 633 | scmd->sc_data_direction = DMA_FROM_DEVICE; |
648 | scmd->request_bufflen = sgl.length; | ||
649 | scmd->request_buffer = &sgl; | ||
650 | scmd->use_sg = 1; | 634 | scmd->use_sg = 1; |
635 | memset(scmd->cmnd, 0, sizeof(scmd->cmnd)); | ||
636 | scmd->cmnd[0] = REQUEST_SENSE; | ||
637 | scmd->cmnd[4] = scmd->request_bufflen; | ||
638 | scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); | ||
651 | } else { | 639 | } else { |
652 | scmd->request_buffer = NULL; | 640 | scmd->request_buffer = NULL; |
653 | scmd->request_bufflen = 0; | 641 | scmd->request_bufflen = 0; |
654 | scmd->sc_data_direction = DMA_NONE; | 642 | scmd->sc_data_direction = DMA_NONE; |
655 | scmd->use_sg = 0; | 643 | scmd->use_sg = 0; |
644 | if (cmnd) { | ||
645 | memset(scmd->cmnd, 0, sizeof(scmd->cmnd)); | ||
646 | memcpy(scmd->cmnd, cmnd, cmnd_size); | ||
647 | scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); | ||
648 | } | ||
656 | } | 649 | } |
657 | 650 | ||
658 | scmd->underflow = 0; | 651 | scmd->underflow = 0; |
659 | scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); | ||
660 | 652 | ||
661 | if (sdev->scsi_level <= SCSI_2) | 653 | if (sdev->scsi_level <= SCSI_2 && sdev->scsi_level != SCSI_UNKNOWN) |
662 | scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | | 654 | scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | |
663 | (sdev->lun << 5 & 0xe0); | 655 | (sdev->lun << 5 & 0xe0); |
664 | 656 | ||
@@ -667,7 +659,58 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
667 | * untransferred sense data should be interpreted as being zero. | 659 | * untransferred sense data should be interpreted as being zero. |
668 | */ | 660 | */ |
669 | memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); | 661 | memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); |
662 | } | ||
663 | EXPORT_SYMBOL(scsi_eh_prep_cmnd); | ||
664 | |||
665 | /** | ||
666 | * scsi_eh_restore_cmnd - Restore a scsi command info as part of error recory | ||
667 | * @scmd: SCSI command structure to restore | ||
668 | * @ses: saved information from a coresponding call to scsi_prep_eh_cmnd | ||
669 | * | ||
670 | * Undo any damage done by above scsi_prep_eh_cmnd(). | ||
671 | **/ | ||
672 | void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses) | ||
673 | { | ||
674 | /* | ||
675 | * Restore original data | ||
676 | */ | ||
677 | scmd->cmd_len = ses->cmd_len; | ||
678 | memcpy(scmd->cmnd, ses->cmnd, sizeof(scmd->cmnd)); | ||
679 | scmd->sc_data_direction = ses->data_direction; | ||
680 | scmd->request_bufflen = ses->bufflen; | ||
681 | scmd->request_buffer = ses->buffer; | ||
682 | scmd->use_sg = ses->use_sg; | ||
683 | scmd->resid = ses->resid; | ||
684 | scmd->result = ses->result; | ||
685 | } | ||
686 | EXPORT_SYMBOL(scsi_eh_restore_cmnd); | ||
670 | 687 | ||
688 | /** | ||
689 | * scsi_send_eh_cmnd - submit a scsi command as part of error recory | ||
690 | * @scmd: SCSI command structure to hijack | ||
691 | * @cmnd: CDB to send | ||
692 | * @cmnd_size: size in bytes of @cmnd | ||
693 | * @timeout: timeout for this request | ||
694 | * @sense_bytes: size of sense data to copy or 0 | ||
695 | * | ||
696 | * This function is used to send a scsi command down to a target device | ||
697 | * as part of the error recovery process. See also scsi_eh_prep_cmnd() above. | ||
698 | * | ||
699 | * Return value: | ||
700 | * SUCCESS or FAILED or NEEDS_RETRY | ||
701 | **/ | ||
702 | static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | ||
703 | int cmnd_size, int timeout, unsigned sense_bytes) | ||
704 | { | ||
705 | struct scsi_device *sdev = scmd->device; | ||
706 | struct Scsi_Host *shost = sdev->host; | ||
707 | DECLARE_COMPLETION_ONSTACK(done); | ||
708 | unsigned long timeleft; | ||
709 | unsigned long flags; | ||
710 | struct scsi_eh_save ses; | ||
711 | int rtn; | ||
712 | |||
713 | scsi_eh_prep_cmnd(scmd, &ses, cmnd, cmnd_size, sense_bytes); | ||
671 | shost->eh_action = &done; | 714 | shost->eh_action = &done; |
672 | 715 | ||
673 | spin_lock_irqsave(shost->host_lock, flags); | 716 | spin_lock_irqsave(shost->host_lock, flags); |
@@ -711,17 +754,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
711 | rtn = FAILED; | 754 | rtn = FAILED; |
712 | } | 755 | } |
713 | 756 | ||
714 | 757 | scsi_eh_restore_cmnd(scmd, &ses); | |
715 | /* | ||
716 | * Restore original data | ||
717 | */ | ||
718 | scmd->request_buffer = old_buffer; | ||
719 | scmd->request_bufflen = old_bufflen; | ||
720 | memcpy(scmd->cmnd, old_cmnd, sizeof(scmd->cmnd)); | ||
721 | scmd->sc_data_direction = old_data_direction; | ||
722 | scmd->cmd_len = old_cmd_len; | ||
723 | scmd->use_sg = old_use_sg; | ||
724 | scmd->result = old_result; | ||
725 | return rtn; | 758 | return rtn; |
726 | } | 759 | } |
727 | 760 | ||
@@ -736,10 +769,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
736 | **/ | 769 | **/ |
737 | static int scsi_request_sense(struct scsi_cmnd *scmd) | 770 | static int scsi_request_sense(struct scsi_cmnd *scmd) |
738 | { | 771 | { |
739 | static unsigned char generic_sense[6] = | 772 | return scsi_send_eh_cmnd(scmd, NULL, 0, SENSE_TIMEOUT, ~0); |
740 | {REQUEST_SENSE, 0, 0, 0, 252, 0}; | ||
741 | |||
742 | return scsi_send_eh_cmnd(scmd, generic_sense, 6, SENSE_TIMEOUT, 1); | ||
743 | } | 773 | } |
744 | 774 | ||
745 | /** | 775 | /** |
@@ -1136,9 +1166,8 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q, | |||
1136 | struct scsi_cmnd *scmd, *next; | 1166 | struct scsi_cmnd *scmd, *next; |
1137 | 1167 | ||
1138 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { | 1168 | list_for_each_entry_safe(scmd, next, work_q, eh_entry) { |
1139 | sdev_printk(KERN_INFO, scmd->device, | 1169 | sdev_printk(KERN_INFO, scmd->device, "Device offlined - " |
1140 | "scsi: Device offlined - not" | 1170 | "not ready after error recovery\n"); |
1141 | " ready after error recovery\n"); | ||
1142 | scsi_device_set_state(scmd->device, SDEV_OFFLINE); | 1171 | scsi_device_set_state(scmd->device, SDEV_OFFLINE); |
1143 | if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD) { | 1172 | if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD) { |
1144 | /* | 1173 | /* |
@@ -1671,7 +1700,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) | |||
1671 | memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd)); | 1700 | memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd)); |
1672 | 1701 | ||
1673 | scmd->scsi_done = scsi_reset_provider_done_command; | 1702 | scmd->scsi_done = scsi_reset_provider_done_command; |
1674 | scmd->done = NULL; | ||
1675 | scmd->request_buffer = NULL; | 1703 | scmd->request_buffer = NULL; |
1676 | scmd->request_bufflen = 0; | 1704 | scmd->request_bufflen = 0; |
1677 | 1705 | ||
@@ -1681,12 +1709,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) | |||
1681 | 1709 | ||
1682 | init_timer(&scmd->eh_timeout); | 1710 | init_timer(&scmd->eh_timeout); |
1683 | 1711 | ||
1684 | /* | ||
1685 | * Sometimes the command can get back into the timer chain, | ||
1686 | * so use the pid as an identifier. | ||
1687 | */ | ||
1688 | scmd->pid = 0; | ||
1689 | |||
1690 | spin_lock_irqsave(shost->host_lock, flags); | 1712 | spin_lock_irqsave(shost->host_lock, flags); |
1691 | shost->tmf_in_progress = 1; | 1713 | shost->tmf_in_progress = 1; |
1692 | spin_unlock_irqrestore(shost->host_lock, flags); | 1714 | spin_unlock_irqrestore(shost->host_lock, flags); |