diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-06-20 18:11:22 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 07:04:50 -0400 |
commit | 980d3aeb3828b0fdf2a0b2e893d238130b014575 (patch) | |
tree | 76fcb476d6cb9ab4d8307ec7ff4dcfffbf1d296f /drivers/scsi | |
parent | 77c852f312243192b1f2ce7fc56d678784786692 (diff) |
isci: fix isci_terminate_pending() list management
Walk through the list of pending requests being careful to consider that
multiple requests can be terminated when the lock is dropped (i.e.
invalidating the 'next' reference established by
list_for_each_entry_safe).
Also noticed that all callers to isci_terminate_pending_requests()
specifying terminating, so just drop the parameter.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/request.h | 5 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 131 |
3 files changed, 67 insertions, 71 deletions
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 3b555dcbe569..45592ad33c3b 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c | |||
@@ -1272,7 +1272,7 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remot | |||
1272 | "%s: idev = %p\n", __func__, idev); | 1272 | "%s: idev = %p\n", __func__, idev); |
1273 | 1273 | ||
1274 | /* Cleanup all requests pending for this device. */ | 1274 | /* Cleanup all requests pending for this device. */ |
1275 | isci_terminate_pending_requests(ihost, idev, terminating); | 1275 | isci_terminate_pending_requests(ihost, idev); |
1276 | 1276 | ||
1277 | dev_dbg(&ihost->pdev->dev, | 1277 | dev_dbg(&ihost->pdev->dev, |
1278 | "%s: idev = %p, done\n", __func__, idev); | 1278 | "%s: idev = %p, done\n", __func__, idev); |
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 547c35cbe459..ac9368c5a6b5 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h | |||
@@ -776,9 +776,8 @@ isci_request_io_request_get_next_sge(struct isci_request *request, | |||
776 | } | 776 | } |
777 | 777 | ||
778 | void | 778 | void |
779 | isci_terminate_pending_requests(struct isci_host *isci_host, | 779 | isci_terminate_pending_requests(struct isci_host *ihost, |
780 | struct isci_remote_device *isci_device, | 780 | struct isci_remote_device *idev); |
781 | enum isci_request_status new_request_state); | ||
782 | enum sci_status | 781 | enum sci_status |
783 | scic_task_request_construct(struct scic_sds_controller *scic, | 782 | scic_task_request_construct(struct scic_sds_controller *scic, |
784 | struct scic_sds_remote_device *sci_dev, | 783 | struct scic_sds_remote_device *sci_dev, |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 573cf1c9e81d..01032dc2c116 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -796,81 +796,79 @@ static void isci_terminate_request_core( | |||
796 | * @isci_host: This parameter specifies SCU. | 796 | * @isci_host: This parameter specifies SCU. |
797 | * @isci_device: This parameter specifies the target. | 797 | * @isci_device: This parameter specifies the target. |
798 | * | 798 | * |
799 | * | ||
800 | */ | 799 | */ |
801 | void isci_terminate_pending_requests( | 800 | void isci_terminate_pending_requests(struct isci_host *ihost, |
802 | struct isci_host *isci_host, | 801 | struct isci_remote_device *idev) |
803 | struct isci_remote_device *isci_device, | ||
804 | enum isci_request_status new_request_state) | ||
805 | { | 802 | { |
806 | struct isci_request *request; | 803 | struct completion request_completion; |
807 | struct isci_request *next_request; | ||
808 | unsigned long flags; | ||
809 | enum isci_request_status old_state; | 804 | enum isci_request_status old_state; |
810 | DECLARE_COMPLETION_ONSTACK(request_completion); | 805 | unsigned long flags; |
811 | 806 | LIST_HEAD(list); | |
812 | dev_dbg(&isci_host->pdev->dev, | ||
813 | "%s: isci_device = %p (new request state = %d)\n", | ||
814 | __func__, isci_device, new_request_state); | ||
815 | 807 | ||
816 | spin_lock_irqsave(&isci_host->scic_lock, flags); | 808 | spin_lock_irqsave(&ihost->scic_lock, flags); |
809 | list_splice_init(&idev->reqs_in_process, &list); | ||
817 | 810 | ||
818 | /* Iterate through the list. */ | 811 | /* assumes that isci_terminate_request_core deletes from the list */ |
819 | list_for_each_entry_safe(request, next_request, | 812 | while (!list_empty(&list)) { |
820 | &isci_device->reqs_in_process, dev_node) { | 813 | struct isci_request *ireq = list_entry(list.next, typeof(*ireq), dev_node); |
821 | 814 | ||
822 | init_completion(&request_completion); | 815 | /* Change state to "terminating" if it is currently |
816 | * "started". | ||
817 | */ | ||
818 | old_state = isci_request_change_started_to_newstate(ireq, | ||
819 | &request_completion, | ||
820 | terminating); | ||
821 | switch (old_state) { | ||
822 | case started: | ||
823 | case completed: | ||
824 | case aborting: | ||
825 | break; | ||
826 | default: | ||
827 | /* termination in progress, or otherwise dispositioned. | ||
828 | * We know the request was on 'list' so should be safe | ||
829 | * to move it back to reqs_in_process | ||
830 | */ | ||
831 | list_move(&ireq->dev_node, &idev->reqs_in_process); | ||
832 | ireq = NULL; | ||
833 | break; | ||
834 | } | ||
823 | 835 | ||
824 | /* Change state to "new_request_state" if it is currently | 836 | if (!ireq) |
825 | * "started". | 837 | continue; |
826 | */ | 838 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
827 | old_state = isci_request_change_started_to_newstate( | ||
828 | request, | ||
829 | &request_completion, | ||
830 | new_request_state); | ||
831 | 839 | ||
832 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | 840 | init_completion(&request_completion); |
833 | 841 | ||
834 | if ((old_state == started) || | 842 | dev_dbg(&ihost->pdev->dev, |
835 | (old_state == completed) || | 843 | "%s: idev=%p request=%p; task=%p old_state=%d\n", |
836 | (old_state == aborting)) { | 844 | __func__, idev, ireq, |
837 | 845 | ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL, | |
838 | dev_warn(&isci_host->pdev->dev, | 846 | old_state); |
839 | "%s: isci_device=%p request=%p; task=%p " | 847 | |
840 | "old_state=%d\n", | 848 | /* If the old_state is started: |
841 | __func__, | 849 | * This request was not already being aborted. If it had been, |
842 | isci_device, request, | 850 | * then the aborting I/O (ie. the TMF request) would not be in |
843 | ((request->ttype == io_task) | 851 | * the aborting state, and thus would be terminated here. Note |
844 | ? isci_request_access_task(request) | 852 | * that since the TMF completion's call to the kernel function |
845 | : NULL), | 853 | * "complete()" does not happen until the pending I/O request |
846 | old_state); | 854 | * terminate fully completes, we do not have to implement a |
847 | 855 | * special wait here for already aborting requests - the | |
848 | /* If the old_state is started: | 856 | * termination of the TMF request will force the request |
849 | * This request was not already being aborted. If it had been, | 857 | * to finish it's already started terminate. |
850 | * then the aborting I/O (ie. the TMF request) would not be in | 858 | * |
851 | * the aborting state, and thus would be terminated here. Note | 859 | * If old_state == completed: |
852 | * that since the TMF completion's call to the kernel function | 860 | * This request completed from the SCU hardware perspective |
853 | * "complete()" does not happen until the pending I/O request | 861 | * and now just needs cleaning up in terms of freeing the |
854 | * terminate fully completes, we do not have to implement a | 862 | * request and potentially calling up to libsas. |
855 | * special wait here for already aborting requests - the | 863 | * |
856 | * termination of the TMF request will force the request | 864 | * If old_state == aborting: |
857 | * to finish it's already started terminate. | 865 | * This request has already gone through a TMF timeout, but may |
858 | * | 866 | * not have been terminated; needs cleaning up at least. |
859 | * If old_state == completed: | 867 | */ |
860 | * This request completed from the SCU hardware perspective | 868 | isci_terminate_request_core(ihost, idev, ireq); |
861 | * and now just needs cleaning up in terms of freeing the | 869 | spin_lock_irqsave(&ihost->scic_lock, flags); |
862 | * request and potentially calling up to libsas. | ||
863 | * | ||
864 | * If old_state == aborting: | ||
865 | * This request has already gone through a TMF timeout, but may | ||
866 | * not have been terminated; needs cleaning up at least. | ||
867 | */ | ||
868 | isci_terminate_request_core(isci_host, isci_device, | ||
869 | request); | ||
870 | } | ||
871 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
872 | } | 870 | } |
873 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | 871 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
874 | } | 872 | } |
875 | 873 | ||
876 | /** | 874 | /** |
@@ -965,8 +963,7 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) | |||
965 | if (ret == TMF_RESP_FUNC_COMPLETE) | 963 | if (ret == TMF_RESP_FUNC_COMPLETE) |
966 | /* Terminate all I/O now. */ | 964 | /* Terminate all I/O now. */ |
967 | isci_terminate_pending_requests(isci_host, | 965 | isci_terminate_pending_requests(isci_host, |
968 | isci_device, | 966 | isci_device); |
969 | terminating); | ||
970 | 967 | ||
971 | return ret; | 968 | return ret; |
972 | } | 969 | } |