diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2011-03-04 17:06:52 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 06:55:30 -0400 |
commit | 4dc043c41037fc6e369270daaa626465a8766565 (patch) | |
tree | 9021ca328112a5a171da0392df43c05a2d63fa52 | |
parent | cbb65c665b341e560b7a3b37cc616376031b3ee5 (diff) |
isci: Termination handling cleanup, added termination timeouts.
Added a request "dead" state for use when a termination wait times-out.
isci_terminate_pending_requests now detaches the device's pending list
and terminates each entry on the detached list.
Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r-- | drivers/scsi/isci/request.h | 3 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 233 |
2 files changed, 143 insertions, 93 deletions
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 166295ee8cfd..b45c0f1f057f 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h | |||
@@ -71,7 +71,8 @@ enum isci_request_status { | |||
71 | completed = 0x03, | 71 | completed = 0x03, |
72 | aborting = 0x04, | 72 | aborting = 0x04, |
73 | aborted = 0x05, | 73 | aborted = 0x05, |
74 | terminating = 0x06 | 74 | terminating = 0x06, |
75 | dead = 0x07 | ||
75 | }; | 76 | }; |
76 | 77 | ||
77 | enum task_type { | 78 | enum task_type { |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 5e80e8449223..c781a4ab4a50 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -663,6 +663,59 @@ static void isci_request_cleanup_completed_loiterer( | |||
663 | } | 663 | } |
664 | isci_request_free(isci_host, isci_request); | 664 | isci_request_free(isci_host, isci_request); |
665 | } | 665 | } |
666 | |||
667 | /** | ||
668 | * @isci_termination_timed_out(): this function will deal with a request for | ||
669 | * which the wait for termination has timed-out. | ||
670 | * | ||
671 | * @isci_host This SCU. | ||
672 | * @isci_request The I/O request being terminated. | ||
673 | */ | ||
674 | static void | ||
675 | isci_termination_timed_out( | ||
676 | struct isci_host * host, | ||
677 | struct isci_request * request | ||
678 | ) | ||
679 | { | ||
680 | unsigned long state_flags; | ||
681 | |||
682 | dev_warn(&host->pdev->dev, | ||
683 | "%s: host = %p; request = %p\n", | ||
684 | __func__, host, request); | ||
685 | |||
686 | /* At this point, the request to terminate | ||
687 | * has timed out. The best we can do is to | ||
688 | * have the request die a silent death | ||
689 | * if it ever completes. | ||
690 | */ | ||
691 | spin_lock_irqsave(&request->state_lock, state_flags); | ||
692 | |||
693 | if (request->status == started) { | ||
694 | |||
695 | /* Set the request state to "dead", | ||
696 | * and clear the task pointer so that an actual | ||
697 | * completion event callback doesn't do | ||
698 | * anything. | ||
699 | */ | ||
700 | request->status = dead; | ||
701 | |||
702 | /* Clear the timeout completion event pointer.*/ | ||
703 | request->io_request_completion = NULL; | ||
704 | |||
705 | if (request->ttype == io_task) { | ||
706 | |||
707 | /* Break links with the sas_task. */ | ||
708 | if (request->ttype_ptr.io_task_ptr != NULL) { | ||
709 | |||
710 | request->ttype_ptr.io_task_ptr->lldd_task = NULL; | ||
711 | request->ttype_ptr.io_task_ptr = NULL; | ||
712 | } | ||
713 | } | ||
714 | } | ||
715 | spin_unlock_irqrestore(&request->state_lock, state_flags); | ||
716 | } | ||
717 | |||
718 | |||
666 | /** | 719 | /** |
667 | * isci_terminate_request_core() - This function will terminate the given | 720 | * isci_terminate_request_core() - This function will terminate the given |
668 | * request, and wait for it to complete. This function must only be called | 721 | * request, and wait for it to complete. This function must only be called |
@@ -684,35 +737,20 @@ static void isci_terminate_request_core( | |||
684 | bool needs_cleanup_handling = false; | 737 | bool needs_cleanup_handling = false; |
685 | enum isci_request_status request_status; | 738 | enum isci_request_status request_status; |
686 | unsigned long flags; | 739 | unsigned long flags; |
740 | unsigned long timeout_remaining; | ||
741 | |||
687 | 742 | ||
688 | dev_dbg(&isci_host->pdev->dev, | 743 | dev_dbg(&isci_host->pdev->dev, |
689 | "%s: device = %p; request = %p\n", | 744 | "%s: device = %p; request = %p\n", |
690 | __func__, isci_device, isci_request); | 745 | __func__, isci_device, isci_request); |
691 | 746 | ||
692 | /* Peek at the current status of the request. This will tell | 747 | spin_lock_irqsave(&isci_host->scic_lock, flags); |
693 | * us if there was special handling on the request such that it | ||
694 | * needs to be detached and freed here. | ||
695 | */ | ||
696 | spin_lock_irqsave(&isci_request->state_lock, flags); | ||
697 | request_status = isci_request_get_state(isci_request); | ||
698 | |||
699 | if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ | ||
700 | && ((request_status == aborted) | ||
701 | || (request_status == aborting) | ||
702 | || (request_status == terminating) | ||
703 | || (request_status == completed) | ||
704 | ) | ||
705 | ) { | ||
706 | 748 | ||
707 | /* The completion routine won't free a request in | 749 | /* Note that we are not going to control |
708 | * the aborted/aborting/terminating state, so we do | 750 | * the target to abort the request. |
709 | * it here. | 751 | */ |
710 | */ | 752 | isci_request->complete_in_target = true; |
711 | needs_cleanup_handling = true; | ||
712 | } | ||
713 | spin_unlock_irqrestore(&isci_request->state_lock, flags); | ||
714 | 753 | ||
715 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
716 | /* Make sure the request wasn't just sitting around signalling | 754 | /* Make sure the request wasn't just sitting around signalling |
717 | * device condition (if the request handle is NULL, then the | 755 | * device condition (if the request handle is NULL, then the |
718 | * request completed but needed additional handling here). | 756 | * request completed but needed additional handling here). |
@@ -733,13 +771,16 @@ static void isci_terminate_request_core( | |||
733 | * fail is when the io request is completed and | 771 | * fail is when the io request is completed and |
734 | * being aborted. | 772 | * being aborted. |
735 | */ | 773 | */ |
736 | if (status != SCI_SUCCESS) | 774 | if (status != SCI_SUCCESS) { |
737 | dev_err(&isci_host->pdev->dev, | 775 | dev_err(&isci_host->pdev->dev, |
738 | "%s: scic_controller_terminate_request" | 776 | "%s: scic_controller_terminate_request" |
739 | " returned = 0x%x\n", | 777 | " returned = 0x%x\n", |
740 | __func__, | 778 | __func__, |
741 | status); | 779 | status); |
742 | else { | 780 | /* Clear the completion pointer from the request. */ |
781 | isci_request->io_request_completion = NULL; | ||
782 | |||
783 | } else { | ||
743 | if (was_terminated) { | 784 | if (was_terminated) { |
744 | dev_dbg(&isci_host->pdev->dev, | 785 | dev_dbg(&isci_host->pdev->dev, |
745 | "%s: before completion wait (%p)\n", | 786 | "%s: before completion wait (%p)\n", |
@@ -747,21 +788,62 @@ static void isci_terminate_request_core( | |||
747 | isci_request->io_request_completion); | 788 | isci_request->io_request_completion); |
748 | 789 | ||
749 | /* Wait here for the request to complete. */ | 790 | /* Wait here for the request to complete. */ |
750 | wait_for_completion(isci_request->io_request_completion); | 791 | #define TERMINATION_TIMEOUT_MSEC 50 |
792 | timeout_remaining | ||
793 | = wait_for_completion_timeout( | ||
794 | isci_request->io_request_completion, | ||
795 | msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC)); | ||
796 | |||
797 | if (!timeout_remaining) { | ||
798 | |||
799 | isci_termination_timed_out(isci_host, | ||
800 | isci_request); | ||
801 | |||
802 | dev_err(&isci_host->pdev->dev, | ||
803 | "%s: *** Timeout waiting for " | ||
804 | "termination(%p/%p)\n", | ||
805 | __func__, | ||
806 | isci_request->io_request_completion, | ||
807 | isci_request); | ||
808 | |||
809 | } else | ||
810 | dev_dbg(&isci_host->pdev->dev, | ||
811 | "%s: after completion wait (%p)\n", | ||
812 | __func__, | ||
813 | isci_request->io_request_completion); | ||
814 | } | ||
815 | /* Clear the completion pointer from the request. */ | ||
816 | isci_request->io_request_completion = NULL; | ||
751 | 817 | ||
752 | dev_dbg(&isci_host->pdev->dev, | 818 | /* Peek at the status of the request. This will tell |
753 | "%s: after completion wait (%p)\n", | 819 | * us if there was special handling on the request such that it |
754 | __func__, | 820 | * needs to be detached and freed here. |
755 | isci_request->io_request_completion); | 821 | */ |
822 | spin_lock_irqsave(&isci_request->state_lock, flags); | ||
823 | request_status = isci_request_get_state(isci_request); | ||
824 | |||
825 | if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ | ||
826 | && ((request_status == aborted) | ||
827 | || (request_status == aborting) | ||
828 | || (request_status == terminating) | ||
829 | || (request_status == completed) | ||
830 | || (request_status == dead) | ||
831 | ) | ||
832 | ) { | ||
833 | |||
834 | /* The completion routine won't free a request in | ||
835 | * the aborted/aborting/etc. states, so we do | ||
836 | * it here. | ||
837 | */ | ||
838 | needs_cleanup_handling = true; | ||
756 | } | 839 | } |
840 | spin_unlock_irqrestore(&isci_request->state_lock, flags); | ||
757 | 841 | ||
758 | if (needs_cleanup_handling) | 842 | if (needs_cleanup_handling) |
759 | isci_request_cleanup_completed_loiterer( | 843 | isci_request_cleanup_completed_loiterer( |
760 | isci_host, isci_device, isci_request | 844 | isci_host, isci_device, isci_request |
761 | ); | 845 | ); |
762 | } | 846 | } |
763 | /* Clear the completion pointer from the request. */ | ||
764 | isci_request->io_request_completion = NULL; | ||
765 | } | 847 | } |
766 | 848 | ||
767 | static void isci_terminate_request( | 849 | static void isci_terminate_request( |
@@ -771,11 +853,7 @@ static void isci_terminate_request( | |||
771 | enum isci_request_status new_request_state) | 853 | enum isci_request_status new_request_state) |
772 | { | 854 | { |
773 | enum isci_request_status old_state; | 855 | enum isci_request_status old_state; |
774 | |||
775 | DECLARE_COMPLETION_ONSTACK(request_completion); | 856 | DECLARE_COMPLETION_ONSTACK(request_completion); |
776 | unsigned long flags; | ||
777 | |||
778 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
779 | 857 | ||
780 | /* Change state to "new_request_state" if it is currently "started" */ | 858 | /* Change state to "new_request_state" if it is currently "started" */ |
781 | old_state = isci_request_change_started_to_newstate( | 859 | old_state = isci_request_change_started_to_newstate( |
@@ -823,73 +901,44 @@ void isci_terminate_pending_requests( | |||
823 | struct isci_remote_device *isci_device, | 901 | struct isci_remote_device *isci_device, |
824 | enum isci_request_status new_request_state) | 902 | enum isci_request_status new_request_state) |
825 | { | 903 | { |
826 | struct isci_request *isci_request; | 904 | struct isci_request *request; |
827 | struct sas_task *task; | 905 | struct isci_request *next_request; |
828 | bool done = false; | 906 | unsigned long flags; |
829 | unsigned long flags; | 907 | struct list_head aborted_request_list; |
908 | |||
909 | INIT_LIST_HEAD(&aborted_request_list); | ||
830 | 910 | ||
831 | dev_dbg(&isci_host->pdev->dev, | 911 | dev_dbg(&isci_host->pdev->dev, |
832 | "%s: isci_device = %p (new request state = %d)\n", | 912 | "%s: isci_device = %p (new request state = %d)\n", |
833 | __func__, isci_device, new_request_state); | 913 | __func__, isci_device, new_request_state); |
834 | 914 | ||
835 | #define ISCI_TERMINATE_SHOW_PENDING_REQUESTS | 915 | spin_lock_irqsave(&isci_host->scic_lock, flags); |
836 | #ifdef ISCI_TERMINATE_SHOW_PENDING_REQUESTS | ||
837 | { | ||
838 | struct isci_request *request; | ||
839 | |||
840 | /* Only abort the task if it's in the | ||
841 | * device's request_in_process list | ||
842 | */ | ||
843 | list_for_each_entry(request, | ||
844 | &isci_device->reqs_in_process, | ||
845 | dev_node) | ||
846 | dev_dbg(&isci_host->pdev->dev, | ||
847 | "%s: isci_device = %p; request is on " | ||
848 | "reqs_in_process list: %p\n", | ||
849 | __func__, isci_device, request); | ||
850 | } | ||
851 | #endif /* ISCI_TERMINATE_SHOW_PENDING_REQUESTS */ | ||
852 | |||
853 | /* Clean up all pending requests. */ | ||
854 | do { | ||
855 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
856 | |||
857 | if (list_empty(&isci_device->reqs_in_process)) { | ||
858 | |||
859 | done = true; | ||
860 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
861 | 916 | ||
862 | dev_dbg(&isci_host->pdev->dev, | 917 | /* Move all of the pending requests off of the device list. */ |
863 | "%s: isci_device = %p; done.\n", | 918 | list_splice_init(&isci_device->reqs_in_process, |
864 | __func__, isci_device); | 919 | &aborted_request_list); |
865 | } else { | ||
866 | /* The list was not empty - grab the first request. */ | ||
867 | isci_request = list_first_entry( | ||
868 | &isci_device->reqs_in_process, | ||
869 | struct isci_request, dev_node | ||
870 | ); | ||
871 | /* Note that we are not expecting to have to control | ||
872 | * the target to abort the request. | ||
873 | */ | ||
874 | isci_request->complete_in_target = true; | ||
875 | 920 | ||
876 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | 921 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); |
877 | 922 | ||
878 | /* Get the libsas task reference. */ | 923 | /* Iterate through the now-local list. */ |
879 | task = isci_request_access_task(isci_request); | 924 | list_for_each_entry_safe(request, next_request, |
925 | &aborted_request_list, dev_node) { | ||
880 | 926 | ||
881 | dev_dbg(&isci_host->pdev->dev, | 927 | dev_warn(&isci_host->pdev->dev, |
882 | "%s: isci_device=%p request=%p; task=%p\n", | 928 | "%s: isci_device=%p request=%p; task=%p\n", |
883 | __func__, isci_device, isci_request, task); | 929 | __func__, |
930 | isci_device, request, | ||
931 | ((request->ttype == io_task) | ||
932 | ? isci_request_access_task(request) | ||
933 | : NULL)); | ||
884 | 934 | ||
885 | /* Mark all still pending I/O with the selected next | 935 | /* Mark all still pending I/O with the selected next |
886 | * state. | 936 | * state, terminate and free it. |
887 | */ | 937 | */ |
888 | isci_terminate_request(isci_host, isci_device, | 938 | isci_terminate_request(isci_host, isci_device, |
889 | isci_request, new_request_state | 939 | request, new_request_state |
890 | ); | 940 | ); |
891 | } | 941 | } |
892 | } while (!done); | ||
893 | } | 942 | } |
894 | 943 | ||
895 | /** | 944 | /** |