diff options
Diffstat (limited to 'drivers/scsi/sym53c8xx_2')
-rw-r--r-- | drivers/scsi/sym53c8xx_2/sym_glue.c | 84 |
1 files changed, 22 insertions, 62 deletions
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index a27dd66b6613..e48409e26e5f 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c | |||
@@ -137,24 +137,14 @@ static void sym2_setup_params(void) | |||
137 | static struct scsi_transport_template *sym2_transport_template = NULL; | 137 | static struct scsi_transport_template *sym2_transport_template = NULL; |
138 | 138 | ||
139 | /* | 139 | /* |
140 | * Used by the eh thread to wait for command completion. | ||
141 | * It is allocated on the eh thread stack. | ||
142 | */ | ||
143 | struct sym_eh_wait { | ||
144 | struct completion done; | ||
145 | struct timer_list timer; | ||
146 | void (*old_done)(struct scsi_cmnd *); | ||
147 | int to_do; | ||
148 | int timed_out; | ||
149 | }; | ||
150 | |||
151 | /* | ||
152 | * Driver private area in the SCSI command structure. | 140 | * Driver private area in the SCSI command structure. |
153 | */ | 141 | */ |
154 | struct sym_ucmd { /* Override the SCSI pointer structure */ | 142 | struct sym_ucmd { /* Override the SCSI pointer structure */ |
143 | struct completion done; | ||
144 | void (*old_done)(struct scsi_cmnd *); | ||
155 | dma_addr_t data_mapping; | 145 | dma_addr_t data_mapping; |
156 | u_char data_mapped; | 146 | int to_do; |
157 | struct sym_eh_wait *eh_wait; | 147 | u_char data_mapped; /* corresponds to data_mapping above */ |
158 | }; | 148 | }; |
159 | 149 | ||
160 | #define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp)) | 150 | #define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp)) |
@@ -713,55 +703,35 @@ static void sym53c8xx_timer(unsigned long npref) | |||
713 | #define SYM_EH_DO_WAIT 2 | 703 | #define SYM_EH_DO_WAIT 2 |
714 | 704 | ||
715 | /* | 705 | /* |
716 | * Our general completion handler. | 706 | * scsi_done() alias when error recovery is in progress. |
717 | */ | 707 | */ |
718 | static void __sym_eh_done(struct scsi_cmnd *cmd, int timed_out) | 708 | static void sym_eh_done(struct scsi_cmnd *cmd) |
719 | { | 709 | { |
720 | struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait; | 710 | struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd); |
721 | if (!ep) | 711 | BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd)); |
722 | return; | ||
723 | |||
724 | /* Try to avoid a race here (not 100% safe) */ | ||
725 | if (!timed_out) { | ||
726 | ep->timed_out = 0; | ||
727 | if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer)) | ||
728 | return; | ||
729 | } | ||
730 | 712 | ||
731 | /* Revert everything */ | 713 | cmd->scsi_done = ucmd->old_done; |
732 | SYM_UCMD_PTR(cmd)->eh_wait = NULL; | ||
733 | cmd->scsi_done = ep->old_done; | ||
734 | 714 | ||
735 | /* Wake up the eh thread if it wants to sleep */ | 715 | if (ucmd->to_do == SYM_EH_DO_WAIT) |
736 | if (ep->to_do == SYM_EH_DO_WAIT) | 716 | complete(&ucmd->done); |
737 | complete(&ep->done); | ||
738 | } | 717 | } |
739 | 718 | ||
740 | /* | 719 | /* |
741 | * scsi_done() alias when error recovery is in progress. | ||
742 | */ | ||
743 | static void sym_eh_done(struct scsi_cmnd *cmd) { __sym_eh_done(cmd, 0); } | ||
744 | |||
745 | /* | ||
746 | * Some timeout handler to avoid waiting too long. | ||
747 | */ | ||
748 | static void sym_eh_timeout(u_long p) { __sym_eh_done((struct scsi_cmnd *)p, 1); } | ||
749 | |||
750 | /* | ||
751 | * Generic method for our eh processing. | 720 | * Generic method for our eh processing. |
752 | * The 'op' argument tells what we have to do. | 721 | * The 'op' argument tells what we have to do. |
753 | */ | 722 | */ |
754 | static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd) | 723 | static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd) |
755 | { | 724 | { |
756 | struct sym_hcb *np = SYM_SOFTC_PTR(cmd); | 725 | struct sym_hcb *np = SYM_SOFTC_PTR(cmd); |
726 | struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd); | ||
727 | struct Scsi_Host *host = cmd->device->host; | ||
757 | SYM_QUEHEAD *qp; | 728 | SYM_QUEHEAD *qp; |
758 | int to_do = SYM_EH_DO_IGNORE; | 729 | int to_do = SYM_EH_DO_IGNORE; |
759 | int sts = -1; | 730 | int sts = -1; |
760 | struct sym_eh_wait eh, *ep = &eh; | ||
761 | 731 | ||
762 | dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname); | 732 | dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname); |
763 | 733 | ||
764 | spin_lock_irq(cmd->device->host->host_lock); | 734 | spin_lock_irq(host->host_lock); |
765 | /* This one is queued in some place -> to wait for completion */ | 735 | /* This one is queued in some place -> to wait for completion */ |
766 | FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { | 736 | FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { |
767 | struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); | 737 | struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); |
@@ -772,10 +742,9 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd) | |||
772 | } | 742 | } |
773 | 743 | ||
774 | if (to_do == SYM_EH_DO_WAIT) { | 744 | if (to_do == SYM_EH_DO_WAIT) { |
775 | init_completion(&ep->done); | 745 | init_completion(&ucmd->done); |
776 | ep->old_done = cmd->scsi_done; | 746 | ucmd->old_done = cmd->scsi_done; |
777 | cmd->scsi_done = sym_eh_done; | 747 | cmd->scsi_done = sym_eh_done; |
778 | SYM_UCMD_PTR(cmd)->eh_wait = ep; | ||
779 | } | 748 | } |
780 | 749 | ||
781 | /* Try to proceed the operation we have been asked for */ | 750 | /* Try to proceed the operation we have been asked for */ |
@@ -802,28 +771,19 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd) | |||
802 | 771 | ||
803 | /* On error, restore everything and cross fingers :) */ | 772 | /* On error, restore everything and cross fingers :) */ |
804 | if (sts) { | 773 | if (sts) { |
805 | SYM_UCMD_PTR(cmd)->eh_wait = NULL; | 774 | cmd->scsi_done = ucmd->old_done; |
806 | cmd->scsi_done = ep->old_done; | ||
807 | to_do = SYM_EH_DO_IGNORE; | 775 | to_do = SYM_EH_DO_IGNORE; |
808 | } | 776 | } |
809 | 777 | ||
810 | ep->to_do = to_do; | 778 | ucmd->to_do = to_do; |
779 | spin_unlock_irq(host->host_lock); | ||
811 | 780 | ||
812 | /* Wait for completion with locks released, as required by kernel */ | ||
813 | if (to_do == SYM_EH_DO_WAIT) { | 781 | if (to_do == SYM_EH_DO_WAIT) { |
814 | init_timer(&ep->timer); | 782 | if (!wait_for_completion_timeout(&ucmd->done, 5*HZ)) { |
815 | ep->timer.expires = jiffies + (5*HZ); | 783 | ucmd->to_do = SYM_EH_DO_IGNORE; |
816 | ep->timer.function = sym_eh_timeout; | ||
817 | ep->timer.data = (u_long)cmd; | ||
818 | ep->timed_out = 1; /* Be pessimistic for once :) */ | ||
819 | add_timer(&ep->timer); | ||
820 | spin_unlock_irq(np->s.host->host_lock); | ||
821 | wait_for_completion(&ep->done); | ||
822 | spin_lock_irq(np->s.host->host_lock); | ||
823 | if (ep->timed_out) | ||
824 | sts = -2; | 784 | sts = -2; |
785 | } | ||
825 | } | 786 | } |
826 | spin_unlock_irq(cmd->device->host->host_lock); | ||
827 | dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname, | 787 | dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname, |
828 | sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed"); | 788 | sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed"); |
829 | return sts ? SCSI_FAILED : SCSI_SUCCESS; | 789 | return sts ? SCSI_FAILED : SCSI_SUCCESS; |