diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2011-06-20 17:09:16 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 07:04:50 -0400 |
commit | 77c852f312243192b1f2ce7fc56d678784786692 (patch) | |
tree | bb0f8a9694f1f2cf4d7985ef3dc40af79963c4f7 | |
parent | f53a3a32c1e799e27f63bff7b42b4c36749e5e6f (diff) |
isci: Handle timed-out request terminations correctly
In the situation where a termination of an I/O times-out,
make sure that the linkage from the request to the task
is severed completely. Also make sure that the selection
of tasks to terminate occurs under scic_lock.
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.c | 9 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 358 |
2 files changed, 181 insertions, 186 deletions
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 5879e5f308e6..433565c2b343 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c | |||
@@ -2741,6 +2741,15 @@ static void isci_request_io_request_complete(struct isci_host *isci_host, | |||
2741 | spin_unlock(&request->state_lock); | 2741 | spin_unlock(&request->state_lock); |
2742 | break; | 2742 | break; |
2743 | 2743 | ||
2744 | case dead: | ||
2745 | /* This was a terminated request that timed-out during the | ||
2746 | * termination process. There is no task to complete to | ||
2747 | * libsas. | ||
2748 | */ | ||
2749 | complete_to_host = isci_perform_normal_io_completion; | ||
2750 | spin_unlock(&request->state_lock); | ||
2751 | break; | ||
2752 | |||
2744 | default: | 2753 | default: |
2745 | 2754 | ||
2746 | /* The request is done from an SCU HW perspective. */ | 2755 | /* The request is done from an SCU HW perspective. */ |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 709c08171743..573cf1c9e81d 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -567,31 +567,33 @@ static enum isci_request_status isci_task_validate_request_to_abort( | |||
567 | return old_state; | 567 | return old_state; |
568 | } | 568 | } |
569 | 569 | ||
570 | /** | ||
571 | * isci_request_cleanup_completed_loiterer() - This function will take care of | ||
572 | * the final cleanup on any request which has been explicitly terminated. | ||
573 | * @isci_host: This parameter specifies the ISCI host object | ||
574 | * @isci_device: This is the device to which the request is pending. | ||
575 | * @isci_request: This parameter specifies the terminated request object. | ||
576 | * @task: This parameter is the libsas I/O request. | ||
577 | */ | ||
570 | static void isci_request_cleanup_completed_loiterer( | 578 | static void isci_request_cleanup_completed_loiterer( |
571 | struct isci_host *isci_host, | 579 | struct isci_host *isci_host, |
572 | struct isci_remote_device *isci_device, | 580 | struct isci_remote_device *isci_device, |
573 | struct isci_request *isci_request) | 581 | struct isci_request *isci_request, |
582 | struct sas_task *task) | ||
574 | { | 583 | { |
575 | struct sas_task *task; | 584 | unsigned long flags; |
576 | unsigned long flags; | ||
577 | |||
578 | task = (isci_request->ttype == io_task) | ||
579 | ? isci_request_access_task(isci_request) | ||
580 | : NULL; | ||
581 | 585 | ||
582 | dev_dbg(&isci_host->pdev->dev, | 586 | dev_dbg(&isci_host->pdev->dev, |
583 | "%s: isci_device=%p, request=%p, task=%p\n", | 587 | "%s: isci_device=%p, request=%p, task=%p\n", |
584 | __func__, isci_device, isci_request, task); | 588 | __func__, isci_device, isci_request, task); |
585 | 589 | ||
586 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
587 | list_del_init(&isci_request->dev_node); | ||
588 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
589 | |||
590 | if (task != NULL) { | 590 | if (task != NULL) { |
591 | 591 | ||
592 | spin_lock_irqsave(&task->task_state_lock, flags); | 592 | spin_lock_irqsave(&task->task_state_lock, flags); |
593 | task->lldd_task = NULL; | 593 | task->lldd_task = NULL; |
594 | 594 | ||
595 | task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; | ||
596 | |||
595 | isci_set_task_doneflags(task); | 597 | isci_set_task_doneflags(task); |
596 | 598 | ||
597 | /* If this task is not in the abort path, call task_done. */ | 599 | /* If this task is not in the abort path, call task_done. */ |
@@ -602,61 +604,16 @@ static void isci_request_cleanup_completed_loiterer( | |||
602 | } else | 604 | } else |
603 | spin_unlock_irqrestore(&task->task_state_lock, flags); | 605 | spin_unlock_irqrestore(&task->task_state_lock, flags); |
604 | } | 606 | } |
605 | isci_request_free(isci_host, isci_request); | ||
606 | } | ||
607 | |||
608 | /** | ||
609 | * @isci_termination_timed_out(): this function will deal with a request for | ||
610 | * which the wait for termination has timed-out. | ||
611 | * | ||
612 | * @isci_host This SCU. | ||
613 | * @isci_request The I/O request being terminated. | ||
614 | */ | ||
615 | static void | ||
616 | isci_termination_timed_out( | ||
617 | struct isci_host * host, | ||
618 | struct isci_request * request | ||
619 | ) | ||
620 | { | ||
621 | unsigned long state_flags; | ||
622 | |||
623 | dev_warn(&host->pdev->dev, | ||
624 | "%s: host = %p; request = %p\n", | ||
625 | __func__, host, request); | ||
626 | 607 | ||
627 | /* At this point, the request to terminate | 608 | if (isci_request != NULL) { |
628 | * has timed out. The best we can do is to | 609 | spin_lock_irqsave(&isci_host->scic_lock, flags); |
629 | * have the request die a silent death | 610 | list_del_init(&isci_request->dev_node); |
630 | * if it ever completes. | 611 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); |
631 | */ | ||
632 | spin_lock_irqsave(&request->state_lock, state_flags); | ||
633 | |||
634 | if (request->status == started) { | ||
635 | |||
636 | /* Set the request state to "dead", | ||
637 | * and clear the task pointer so that an actual | ||
638 | * completion event callback doesn't do | ||
639 | * anything. | ||
640 | */ | ||
641 | request->status = dead; | ||
642 | |||
643 | /* Clear the timeout completion event pointer.*/ | ||
644 | request->io_request_completion = NULL; | ||
645 | |||
646 | if (request->ttype == io_task) { | ||
647 | |||
648 | /* Break links with the sas_task. */ | ||
649 | if (request->ttype_ptr.io_task_ptr != NULL) { | ||
650 | 612 | ||
651 | request->ttype_ptr.io_task_ptr->lldd_task = NULL; | 613 | isci_request_free(isci_host, isci_request); |
652 | request->ttype_ptr.io_task_ptr = NULL; | ||
653 | } | ||
654 | } | ||
655 | } | 614 | } |
656 | spin_unlock_irqrestore(&request->state_lock, state_flags); | ||
657 | } | 615 | } |
658 | 616 | ||
659 | |||
660 | /** | 617 | /** |
661 | * isci_terminate_request_core() - This function will terminate the given | 618 | * isci_terminate_request_core() - This function will terminate the given |
662 | * request, and wait for it to complete. This function must only be called | 619 | * request, and wait for it to complete. This function must only be called |
@@ -666,7 +623,6 @@ isci_termination_timed_out( | |||
666 | * @isci_device: The target. | 623 | * @isci_device: The target. |
667 | * @isci_request: The I/O request to be terminated. | 624 | * @isci_request: The I/O request to be terminated. |
668 | * | 625 | * |
669 | * | ||
670 | */ | 626 | */ |
671 | static void isci_terminate_request_core( | 627 | static void isci_terminate_request_core( |
672 | struct isci_host *isci_host, | 628 | struct isci_host *isci_host, |
@@ -677,9 +633,10 @@ static void isci_terminate_request_core( | |||
677 | bool was_terminated = false; | 633 | bool was_terminated = false; |
678 | bool needs_cleanup_handling = false; | 634 | bool needs_cleanup_handling = false; |
679 | enum isci_request_status request_status; | 635 | enum isci_request_status request_status; |
680 | unsigned long flags; | 636 | unsigned long flags; |
681 | unsigned long timeout_remaining; | 637 | unsigned long termination_completed = 1; |
682 | 638 | struct completion *io_request_completion; | |
639 | struct sas_task *task; | ||
683 | 640 | ||
684 | dev_dbg(&isci_host->pdev->dev, | 641 | dev_dbg(&isci_host->pdev->dev, |
685 | "%s: device = %p; request = %p\n", | 642 | "%s: device = %p; request = %p\n", |
@@ -687,6 +644,12 @@ static void isci_terminate_request_core( | |||
687 | 644 | ||
688 | spin_lock_irqsave(&isci_host->scic_lock, flags); | 645 | spin_lock_irqsave(&isci_host->scic_lock, flags); |
689 | 646 | ||
647 | io_request_completion = isci_request->io_request_completion; | ||
648 | |||
649 | task = (isci_request->ttype == io_task) | ||
650 | ? isci_request_access_task(isci_request) | ||
651 | : NULL; | ||
652 | |||
690 | /* Note that we are not going to control | 653 | /* Note that we are not going to control |
691 | * the target to abort the request. | 654 | * the target to abort the request. |
692 | */ | 655 | */ |
@@ -715,119 +678,112 @@ static void isci_terminate_request_core( | |||
715 | dev_err(&isci_host->pdev->dev, | 678 | dev_err(&isci_host->pdev->dev, |
716 | "%s: scic_controller_terminate_request" | 679 | "%s: scic_controller_terminate_request" |
717 | " returned = 0x%x\n", | 680 | " returned = 0x%x\n", |
718 | __func__, | 681 | __func__, status); |
719 | status); | 682 | |
720 | /* Clear the completion pointer from the request. */ | ||
721 | isci_request->io_request_completion = NULL; | 683 | isci_request->io_request_completion = NULL; |
722 | 684 | ||
723 | } else { | 685 | } else { |
724 | if (was_terminated) { | 686 | if (was_terminated) { |
725 | dev_dbg(&isci_host->pdev->dev, | 687 | dev_dbg(&isci_host->pdev->dev, |
726 | "%s: before completion wait (%p)\n", | 688 | "%s: before completion wait (%p/%p)\n", |
727 | __func__, | 689 | __func__, isci_request, io_request_completion); |
728 | isci_request->io_request_completion); | ||
729 | 690 | ||
730 | /* Wait here for the request to complete. */ | 691 | /* Wait here for the request to complete. */ |
731 | #define TERMINATION_TIMEOUT_MSEC 50 | 692 | #define TERMINATION_TIMEOUT_MSEC 500 |
732 | timeout_remaining | 693 | termination_completed |
733 | = wait_for_completion_timeout( | 694 | = wait_for_completion_timeout( |
734 | isci_request->io_request_completion, | 695 | io_request_completion, |
735 | msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC)); | 696 | msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC)); |
736 | 697 | ||
737 | if (!timeout_remaining) { | 698 | if (!termination_completed) { |
699 | |||
700 | /* The request to terminate has timed out. */ | ||
701 | spin_lock_irqsave(&isci_host->scic_lock, | ||
702 | flags); | ||
703 | |||
704 | /* Check for state changes. */ | ||
705 | if (!isci_request->terminated) { | ||
706 | |||
707 | /* The best we can do is to have the | ||
708 | * request die a silent death if it | ||
709 | * ever really completes. | ||
710 | * | ||
711 | * Set the request state to "dead", | ||
712 | * and clear the task pointer so that | ||
713 | * an actual completion event callback | ||
714 | * doesn't do anything. | ||
715 | */ | ||
716 | isci_request->status = dead; | ||
717 | isci_request->io_request_completion | ||
718 | = NULL; | ||
719 | |||
720 | if (isci_request->ttype == io_task) { | ||
721 | |||
722 | /* Break links with the | ||
723 | * sas_task. | ||
724 | */ | ||
725 | isci_request->ttype_ptr.io_task_ptr | ||
726 | = NULL; | ||
727 | } | ||
728 | } else | ||
729 | termination_completed = 1; | ||
730 | |||
731 | spin_unlock_irqrestore(&isci_host->scic_lock, | ||
732 | flags); | ||
738 | 733 | ||
739 | isci_termination_timed_out(isci_host, | 734 | if (!termination_completed) { |
740 | isci_request); | ||
741 | 735 | ||
742 | dev_err(&isci_host->pdev->dev, | 736 | dev_err(&isci_host->pdev->dev, |
743 | "%s: *** Timeout waiting for " | 737 | "%s: *** Timeout waiting for " |
744 | "termination(%p/%p)\n", | 738 | "termination(%p/%p)\n", |
745 | __func__, | 739 | __func__, io_request_completion, |
746 | isci_request->io_request_completion, | 740 | isci_request); |
747 | isci_request); | ||
748 | 741 | ||
749 | } else | 742 | /* The request can no longer be referenced |
743 | * safely since it may go away if the | ||
744 | * termination every really does complete. | ||
745 | */ | ||
746 | isci_request = NULL; | ||
747 | } | ||
748 | } | ||
749 | if (termination_completed) | ||
750 | dev_dbg(&isci_host->pdev->dev, | 750 | dev_dbg(&isci_host->pdev->dev, |
751 | "%s: after completion wait (%p)\n", | 751 | "%s: after completion wait (%p/%p)\n", |
752 | __func__, | 752 | __func__, isci_request, io_request_completion); |
753 | isci_request->io_request_completion); | ||
754 | } | 753 | } |
755 | /* Clear the completion pointer from the request. */ | ||
756 | isci_request->io_request_completion = NULL; | ||
757 | 754 | ||
758 | /* Peek at the status of the request. This will tell | 755 | if (termination_completed) { |
759 | * us if there was special handling on the request such that it | ||
760 | * needs to be detached and freed here. | ||
761 | */ | ||
762 | spin_lock_irqsave(&isci_request->state_lock, flags); | ||
763 | request_status = isci_request_get_state(isci_request); | ||
764 | |||
765 | if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ | ||
766 | && ((request_status == aborted) | ||
767 | || (request_status == aborting) | ||
768 | || (request_status == terminating) | ||
769 | || (request_status == completed) | ||
770 | || (request_status == dead) | ||
771 | ) | ||
772 | ) { | ||
773 | |||
774 | /* The completion routine won't free a request in | ||
775 | * the aborted/aborting/etc. states, so we do | ||
776 | * it here. | ||
777 | */ | ||
778 | needs_cleanup_handling = true; | ||
779 | } | ||
780 | spin_unlock_irqrestore(&isci_request->state_lock, flags); | ||
781 | |||
782 | if (needs_cleanup_handling) | ||
783 | isci_request_cleanup_completed_loiterer( | ||
784 | isci_host, isci_device, isci_request | ||
785 | ); | ||
786 | } | ||
787 | } | ||
788 | 756 | ||
789 | static void isci_terminate_request( | 757 | isci_request->io_request_completion = NULL; |
790 | struct isci_host *isci_host, | ||
791 | struct isci_remote_device *isci_device, | ||
792 | struct isci_request *isci_request, | ||
793 | enum isci_request_status new_request_state) | ||
794 | { | ||
795 | enum isci_request_status old_state; | ||
796 | DECLARE_COMPLETION_ONSTACK(request_completion); | ||
797 | 758 | ||
798 | /* Change state to "new_request_state" if it is currently "started" */ | 759 | /* Peek at the status of the request. This will tell |
799 | old_state = isci_request_change_started_to_newstate( | 760 | * us if there was special handling on the request such that it |
800 | isci_request, | 761 | * needs to be detached and freed here. |
801 | &request_completion, | 762 | */ |
802 | new_request_state | 763 | spin_lock_irqsave(&isci_request->state_lock, flags); |
803 | ); | 764 | request_status = isci_request_get_state(isci_request); |
765 | |||
766 | if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ | ||
767 | && ((request_status == aborted) | ||
768 | || (request_status == aborting) | ||
769 | || (request_status == terminating) | ||
770 | || (request_status == completed) | ||
771 | || (request_status == dead) | ||
772 | ) | ||
773 | ) { | ||
774 | |||
775 | /* The completion routine won't free a request in | ||
776 | * the aborted/aborting/etc. states, so we do | ||
777 | * it here. | ||
778 | */ | ||
779 | needs_cleanup_handling = true; | ||
780 | } | ||
781 | spin_unlock_irqrestore(&isci_request->state_lock, flags); | ||
804 | 782 | ||
805 | if ((old_state == started) || | 783 | } |
806 | (old_state == completed) || | 784 | if (needs_cleanup_handling) |
807 | (old_state == aborting)) { | 785 | isci_request_cleanup_completed_loiterer( |
808 | 786 | isci_host, isci_device, isci_request, task); | |
809 | /* If the old_state is started: | ||
810 | * This request was not already being aborted. If it had been, | ||
811 | * then the aborting I/O (ie. the TMF request) would not be in | ||
812 | * the aborting state, and thus would be terminated here. Note | ||
813 | * that since the TMF completion's call to the kernel function | ||
814 | * "complete()" does not happen until the pending I/O request | ||
815 | * terminate fully completes, we do not have to implement a | ||
816 | * special wait here for already aborting requests - the | ||
817 | * termination of the TMF request will force the request | ||
818 | * to finish it's already started terminate. | ||
819 | * | ||
820 | * If old_state == completed: | ||
821 | * This request completed from the SCU hardware perspective | ||
822 | * and now just needs cleaning up in terms of freeing the | ||
823 | * request and potentially calling up to libsas. | ||
824 | * | ||
825 | * If old_state == aborting: | ||
826 | * This request has already gone through a TMF timeout, but may | ||
827 | * not have been terminated; needs cleaning up at least. | ||
828 | */ | ||
829 | isci_terminate_request_core(isci_host, isci_device, | ||
830 | isci_request); | ||
831 | } | 787 | } |
832 | } | 788 | } |
833 | 789 | ||
@@ -850,9 +806,8 @@ void isci_terminate_pending_requests( | |||
850 | struct isci_request *request; | 806 | struct isci_request *request; |
851 | struct isci_request *next_request; | 807 | struct isci_request *next_request; |
852 | unsigned long flags; | 808 | unsigned long flags; |
853 | struct list_head aborted_request_list; | 809 | enum isci_request_status old_state; |
854 | 810 | DECLARE_COMPLETION_ONSTACK(request_completion); | |
855 | INIT_LIST_HEAD(&aborted_request_list); | ||
856 | 811 | ||
857 | dev_dbg(&isci_host->pdev->dev, | 812 | dev_dbg(&isci_host->pdev->dev, |
858 | "%s: isci_device = %p (new request state = %d)\n", | 813 | "%s: isci_device = %p (new request state = %d)\n", |
@@ -860,31 +815,62 @@ void isci_terminate_pending_requests( | |||
860 | 815 | ||
861 | spin_lock_irqsave(&isci_host->scic_lock, flags); | 816 | spin_lock_irqsave(&isci_host->scic_lock, flags); |
862 | 817 | ||
863 | /* Move all of the pending requests off of the device list. */ | 818 | /* Iterate through the list. */ |
864 | list_splice_init(&isci_device->reqs_in_process, | ||
865 | &aborted_request_list); | ||
866 | |||
867 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
868 | |||
869 | /* Iterate through the now-local list. */ | ||
870 | list_for_each_entry_safe(request, next_request, | 819 | list_for_each_entry_safe(request, next_request, |
871 | &aborted_request_list, dev_node) { | 820 | &isci_device->reqs_in_process, dev_node) { |
872 | 821 | ||
873 | dev_warn(&isci_host->pdev->dev, | 822 | init_completion(&request_completion); |
874 | "%s: isci_device=%p request=%p; task=%p\n", | ||
875 | __func__, | ||
876 | isci_device, request, | ||
877 | ((request->ttype == io_task) | ||
878 | ? isci_request_access_task(request) | ||
879 | : NULL)); | ||
880 | 823 | ||
881 | /* Mark all still pending I/O with the selected next | 824 | /* Change state to "new_request_state" if it is currently |
882 | * state, terminate and free it. | 825 | * "started". |
883 | */ | 826 | */ |
884 | isci_terminate_request(isci_host, isci_device, | 827 | old_state = isci_request_change_started_to_newstate( |
885 | request, new_request_state | 828 | request, |
886 | ); | 829 | &request_completion, |
830 | new_request_state); | ||
831 | |||
832 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
833 | |||
834 | if ((old_state == started) || | ||
835 | (old_state == completed) || | ||
836 | (old_state == aborting)) { | ||
837 | |||
838 | dev_warn(&isci_host->pdev->dev, | ||
839 | "%s: isci_device=%p request=%p; task=%p " | ||
840 | "old_state=%d\n", | ||
841 | __func__, | ||
842 | isci_device, request, | ||
843 | ((request->ttype == io_task) | ||
844 | ? isci_request_access_task(request) | ||
845 | : NULL), | ||
846 | old_state); | ||
847 | |||
848 | /* If the old_state is started: | ||
849 | * This request was not already being aborted. If it had been, | ||
850 | * then the aborting I/O (ie. the TMF request) would not be in | ||
851 | * the aborting state, and thus would be terminated here. Note | ||
852 | * that since the TMF completion's call to the kernel function | ||
853 | * "complete()" does not happen until the pending I/O request | ||
854 | * terminate fully completes, we do not have to implement a | ||
855 | * special wait here for already aborting requests - the | ||
856 | * termination of the TMF request will force the request | ||
857 | * to finish it's already started terminate. | ||
858 | * | ||
859 | * If old_state == completed: | ||
860 | * This request completed from the SCU hardware perspective | ||
861 | * and now just needs cleaning up in terms of freeing the | ||
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); | ||
887 | } | 872 | } |
873 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
888 | } | 874 | } |
889 | 875 | ||
890 | /** | 876 | /** |
@@ -1257,9 +1243,9 @@ int isci_task_abort_task(struct sas_task *task) | |||
1257 | if (ret == TMF_RESP_FUNC_COMPLETE) { | 1243 | if (ret == TMF_RESP_FUNC_COMPLETE) { |
1258 | old_request->complete_in_target = true; | 1244 | old_request->complete_in_target = true; |
1259 | 1245 | ||
1260 | /* Clean up the request on our side, and wait for the aborted I/O to | 1246 | /* Clean up the request on our side, and wait for the aborted |
1261 | * complete. | 1247 | * I/O to complete. |
1262 | */ | 1248 | */ |
1263 | isci_terminate_request_core(isci_host, isci_device, old_request); | 1249 | isci_terminate_request_core(isci_host, isci_device, old_request); |
1264 | } | 1250 | } |
1265 | 1251 | ||