diff options
Diffstat (limited to 'drivers/message/fusion/mptsas.c')
-rw-r--r-- | drivers/message/fusion/mptsas.c | 325 |
1 files changed, 280 insertions, 45 deletions
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index a8df06c422bd..3dbb5659f615 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c | |||
@@ -96,6 +96,12 @@ static int mptsasMgmtCtx = -1; | |||
96 | 96 | ||
97 | static void mptsas_hotplug_work(struct work_struct *work); | 97 | static void mptsas_hotplug_work(struct work_struct *work); |
98 | 98 | ||
99 | struct mptsas_target_reset_event { | ||
100 | struct list_head list; | ||
101 | EVENT_DATA_SAS_DEVICE_STATUS_CHANGE sas_event_data; | ||
102 | u8 target_reset_issued; | ||
103 | }; | ||
104 | |||
99 | enum mptsas_hotplug_action { | 105 | enum mptsas_hotplug_action { |
100 | MPTSAS_ADD_DEVICE, | 106 | MPTSAS_ADD_DEVICE, |
101 | MPTSAS_DEL_DEVICE, | 107 | MPTSAS_DEL_DEVICE, |
@@ -571,20 +577,271 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) | |||
571 | mutex_unlock(&ioc->sas_topology_mutex); | 577 | mutex_unlock(&ioc->sas_topology_mutex); |
572 | } | 578 | } |
573 | 579 | ||
580 | /** | ||
581 | * csmisas_find_vtarget | ||
582 | * | ||
583 | * @ioc | ||
584 | * @volume_id | ||
585 | * @volume_bus | ||
586 | * | ||
587 | **/ | ||
588 | static VirtTarget * | ||
589 | mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id) | ||
590 | { | ||
591 | struct scsi_device *sdev; | ||
592 | VirtDevice *vdev; | ||
593 | VirtTarget *vtarget = NULL; | ||
594 | |||
595 | shost_for_each_device(sdev, ioc->sh) { | ||
596 | if ((vdev = sdev->hostdata) == NULL) | ||
597 | continue; | ||
598 | if (vdev->vtarget->id == id && | ||
599 | vdev->vtarget->channel == channel) | ||
600 | vtarget = vdev->vtarget; | ||
601 | } | ||
602 | return vtarget; | ||
603 | } | ||
604 | |||
605 | /** | ||
606 | * mptsas_target_reset | ||
607 | * | ||
608 | * Issues TARGET_RESET to end device using handshaking method | ||
609 | * | ||
610 | * @ioc | ||
611 | * @channel | ||
612 | * @id | ||
613 | * | ||
614 | * Returns (1) success | ||
615 | * (0) failure | ||
616 | * | ||
617 | **/ | ||
618 | static int | ||
619 | mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id) | ||
620 | { | ||
621 | MPT_FRAME_HDR *mf; | ||
622 | SCSITaskMgmt_t *pScsiTm; | ||
623 | |||
624 | if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) { | ||
625 | dfailprintk((MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n", | ||
626 | ioc->name,__FUNCTION__, __LINE__)); | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | /* Format the Request | ||
631 | */ | ||
632 | pScsiTm = (SCSITaskMgmt_t *) mf; | ||
633 | memset (pScsiTm, 0, sizeof(SCSITaskMgmt_t)); | ||
634 | pScsiTm->TargetID = id; | ||
635 | pScsiTm->Bus = channel; | ||
636 | pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; | ||
637 | pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET; | ||
638 | pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; | ||
639 | |||
640 | DBG_DUMP_TM_REQUEST_FRAME(mf); | ||
641 | |||
642 | if (mpt_send_handshake_request(ioc->TaskCtx, ioc, | ||
643 | sizeof(SCSITaskMgmt_t), (u32 *)mf, NO_SLEEP)) { | ||
644 | mpt_free_msg_frame(ioc, mf); | ||
645 | dfailprintk((MYIOC_s_WARN_FMT "%s, tm handshake failed @%d!!\n", | ||
646 | ioc->name,__FUNCTION__, __LINE__)); | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | return 1; | ||
651 | } | ||
652 | |||
653 | /** | ||
654 | * mptsas_target_reset_queue | ||
655 | * | ||
656 | * Receive request for TARGET_RESET after recieving an firmware | ||
657 | * event NOT_RESPONDING_EVENT, then put command in link list | ||
658 | * and queue if task_queue already in use. | ||
659 | * | ||
660 | * @ioc | ||
661 | * @sas_event_data | ||
662 | * | ||
663 | **/ | ||
574 | static void | 664 | static void |
575 | mptsas_target_reset(MPT_ADAPTER *ioc, VirtTarget * vtarget) | 665 | mptsas_target_reset_queue(MPT_ADAPTER *ioc, |
666 | EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data) | ||
576 | { | 667 | { |
577 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; | 668 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; |
669 | VirtTarget *vtarget = NULL; | ||
670 | struct mptsas_target_reset_event *target_reset_list; | ||
671 | u8 id, channel; | ||
578 | 672 | ||
579 | if (mptscsih_TMHandler(hd, | 673 | id = sas_event_data->TargetID; |
580 | MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, | 674 | channel = sas_event_data->Bus; |
581 | vtarget->channel, vtarget->id, 0, 0, 5) < 0) { | 675 | |
582 | hd->tmPending = 0; | 676 | if (!(vtarget = mptsas_find_vtarget(ioc, channel, id))) |
583 | hd->tmState = TM_STATE_NONE; | 677 | return; |
584 | printk(MYIOC_s_WARN_FMT | 678 | |
585 | "Error processing TaskMgmt id=%d TARGET_RESET\n", | 679 | vtarget->deleted = 1; /* block IO */ |
586 | ioc->name, vtarget->id); | 680 | |
681 | target_reset_list = kzalloc(sizeof(*target_reset_list), | ||
682 | GFP_ATOMIC); | ||
683 | if (!target_reset_list) { | ||
684 | dfailprintk((MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n", | ||
685 | ioc->name,__FUNCTION__, __LINE__)); | ||
686 | return; | ||
687 | } | ||
688 | |||
689 | memcpy(&target_reset_list->sas_event_data, sas_event_data, | ||
690 | sizeof(*sas_event_data)); | ||
691 | list_add_tail(&target_reset_list->list, &hd->target_reset_list); | ||
692 | |||
693 | if (hd->resetPending) | ||
694 | return; | ||
695 | |||
696 | if (mptsas_target_reset(ioc, channel, id)) { | ||
697 | target_reset_list->target_reset_issued = 1; | ||
698 | hd->resetPending = 1; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | /** | ||
703 | * mptsas_dev_reset_complete | ||
704 | * | ||
705 | * Completion for TARGET_RESET after NOT_RESPONDING_EVENT, | ||
706 | * enable work queue to finish off removing device from upper layers. | ||
707 | * then send next TARGET_RESET in the queue. | ||
708 | * | ||
709 | * @ioc | ||
710 | * | ||
711 | **/ | ||
712 | static void | ||
713 | mptsas_dev_reset_complete(MPT_ADAPTER *ioc) | ||
714 | { | ||
715 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; | ||
716 | struct list_head *head = &hd->target_reset_list; | ||
717 | struct mptsas_target_reset_event *target_reset_list; | ||
718 | struct mptsas_hotplug_event *ev; | ||
719 | EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data; | ||
720 | u8 id, channel; | ||
721 | __le64 sas_address; | ||
722 | |||
723 | if (list_empty(head)) | ||
724 | return; | ||
725 | |||
726 | target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, list); | ||
727 | |||
728 | sas_event_data = &target_reset_list->sas_event_data; | ||
729 | id = sas_event_data->TargetID; | ||
730 | channel = sas_event_data->Bus; | ||
731 | hd->resetPending = 0; | ||
732 | |||
733 | /* | ||
734 | * retry target reset | ||
735 | */ | ||
736 | if (!target_reset_list->target_reset_issued) { | ||
737 | if (mptsas_target_reset(ioc, channel, id)) { | ||
738 | target_reset_list->target_reset_issued = 1; | ||
739 | hd->resetPending = 1; | ||
740 | } | ||
741 | return; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * enable work queue to remove device from upper layers | ||
746 | */ | ||
747 | list_del(&target_reset_list->list); | ||
748 | |||
749 | ev = kzalloc(sizeof(*ev), GFP_ATOMIC); | ||
750 | if (!ev) { | ||
751 | dfailprintk((MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n", | ||
752 | ioc->name,__FUNCTION__, __LINE__)); | ||
753 | return; | ||
754 | } | ||
755 | |||
756 | INIT_WORK(&ev->work, mptsas_hotplug_work); | ||
757 | ev->ioc = ioc; | ||
758 | ev->handle = le16_to_cpu(sas_event_data->DevHandle); | ||
759 | ev->parent_handle = | ||
760 | le16_to_cpu(sas_event_data->ParentDevHandle); | ||
761 | ev->channel = channel; | ||
762 | ev->id =id; | ||
763 | ev->phy_id = sas_event_data->PhyNum; | ||
764 | memcpy(&sas_address, &sas_event_data->SASAddress, | ||
765 | sizeof(__le64)); | ||
766 | ev->sas_address = le64_to_cpu(sas_address); | ||
767 | ev->device_info = le32_to_cpu(sas_event_data->DeviceInfo); | ||
768 | ev->event_type = MPTSAS_DEL_DEVICE; | ||
769 | schedule_work(&ev->work); | ||
770 | kfree(target_reset_list); | ||
771 | |||
772 | /* | ||
773 | * issue target reset to next device in the queue | ||
774 | */ | ||
775 | |||
776 | head = &hd->target_reset_list; | ||
777 | if (list_empty(head)) | ||
778 | return; | ||
779 | |||
780 | target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, | ||
781 | list); | ||
782 | |||
783 | sas_event_data = &target_reset_list->sas_event_data; | ||
784 | id = sas_event_data->TargetID; | ||
785 | channel = sas_event_data->Bus; | ||
786 | |||
787 | if (mptsas_target_reset(ioc, channel, id)) { | ||
788 | target_reset_list->target_reset_issued = 1; | ||
789 | hd->resetPending = 1; | ||
790 | } | ||
791 | } | ||
792 | |||
793 | /** | ||
794 | * mptsas_taskmgmt_complete | ||
795 | * | ||
796 | * @ioc | ||
797 | * @mf | ||
798 | * @mr | ||
799 | * | ||
800 | **/ | ||
801 | static int | ||
802 | mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) | ||
803 | { | ||
804 | mptsas_dev_reset_complete(ioc); | ||
805 | return mptscsih_taskmgmt_complete(ioc, mf, mr); | ||
806 | } | ||
807 | |||
808 | /** | ||
809 | * mptscsih_ioc_reset | ||
810 | * | ||
811 | * @ioc | ||
812 | * @reset_phase | ||
813 | * | ||
814 | **/ | ||
815 | static int | ||
816 | mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) | ||
817 | { | ||
818 | MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; | ||
819 | struct mptsas_target_reset_event *target_reset_list, *n; | ||
820 | int rc; | ||
821 | |||
822 | rc = mptscsih_ioc_reset(ioc, reset_phase); | ||
823 | |||
824 | if (ioc->bus_type != SAS) | ||
825 | goto out; | ||
826 | |||
827 | if (reset_phase != MPT_IOC_POST_RESET) | ||
828 | goto out; | ||
829 | |||
830 | if (!hd || !hd->ioc) | ||
831 | goto out; | ||
832 | |||
833 | if (list_empty(&hd->target_reset_list)) | ||
834 | goto out; | ||
835 | |||
836 | /* flush the target_reset_list */ | ||
837 | list_for_each_entry_safe(target_reset_list, n, | ||
838 | &hd->target_reset_list, list) { | ||
839 | list_del(&target_reset_list->list); | ||
840 | kfree(target_reset_list); | ||
587 | } | 841 | } |
842 | |||
843 | out: | ||
844 | return rc; | ||
588 | } | 845 | } |
589 | 846 | ||
590 | static int | 847 | static int |
@@ -1885,8 +2142,6 @@ mptsas_delete_expander_phys(MPT_ADAPTER *ioc) | |||
1885 | struct mptsas_portinfo buffer; | 2142 | struct mptsas_portinfo buffer; |
1886 | struct mptsas_portinfo *port_info, *n, *parent; | 2143 | struct mptsas_portinfo *port_info, *n, *parent; |
1887 | struct mptsas_phyinfo *phy_info; | 2144 | struct mptsas_phyinfo *phy_info; |
1888 | struct scsi_target * starget; | ||
1889 | VirtTarget * vtarget; | ||
1890 | struct sas_port * port; | 2145 | struct sas_port * port; |
1891 | int i; | 2146 | int i; |
1892 | u64 expander_sas_address; | 2147 | u64 expander_sas_address; |
@@ -1904,25 +2159,6 @@ mptsas_delete_expander_phys(MPT_ADAPTER *ioc) | |||
1904 | MPI_SAS_EXPAND_PGAD_FORM_SHIFT), port_info->handle)) { | 2159 | MPI_SAS_EXPAND_PGAD_FORM_SHIFT), port_info->handle)) { |
1905 | 2160 | ||
1906 | /* | 2161 | /* |
1907 | * Issue target reset to all child end devices | ||
1908 | * then mark them deleted to prevent further | ||
1909 | * IO going to them. | ||
1910 | */ | ||
1911 | phy_info = port_info->phy_info; | ||
1912 | for (i = 0; i < port_info->num_phys; i++, phy_info++) { | ||
1913 | starget = mptsas_get_starget(phy_info); | ||
1914 | if (!starget) | ||
1915 | continue; | ||
1916 | vtarget = starget->hostdata; | ||
1917 | if(vtarget->deleted) | ||
1918 | continue; | ||
1919 | vtarget->deleted = 1; | ||
1920 | mptsas_target_reset(ioc, vtarget); | ||
1921 | sas_port_delete(mptsas_get_port(phy_info)); | ||
1922 | mptsas_port_delete(phy_info->port_details); | ||
1923 | } | ||
1924 | |||
1925 | /* | ||
1926 | * Obtain the port_info instance to the parent port | 2162 | * Obtain the port_info instance to the parent port |
1927 | */ | 2163 | */ |
1928 | parent = mptsas_find_portinfo_by_handle(ioc, | 2164 | parent = mptsas_find_portinfo_by_handle(ioc, |
@@ -2319,9 +2555,6 @@ mptsas_hotplug_work(struct work_struct *work) | |||
2319 | ev->phys_disk_num; | 2555 | ev->phys_disk_num; |
2320 | break; | 2556 | break; |
2321 | } | 2557 | } |
2322 | |||
2323 | vtarget->deleted = 1; | ||
2324 | mptsas_target_reset(ioc, vtarget); | ||
2325 | } | 2558 | } |
2326 | 2559 | ||
2327 | if (phy_info->attached.device_info & | 2560 | if (phy_info->attached.device_info & |
@@ -2474,8 +2707,6 @@ mptsas_hotplug_work(struct work_struct *work) | |||
2474 | printk(MYIOC_s_INFO_FMT | 2707 | printk(MYIOC_s_INFO_FMT |
2475 | "removing raid volume, channel %d, id %d\n", | 2708 | "removing raid volume, channel %d, id %d\n", |
2476 | ioc->name, MPTSAS_RAID_CHANNEL, ev->id); | 2709 | ioc->name, MPTSAS_RAID_CHANNEL, ev->id); |
2477 | vdevice->vtarget->deleted = 1; | ||
2478 | mptsas_target_reset(ioc, vdevice->vtarget); | ||
2479 | vdevice = sdev->hostdata; | 2710 | vdevice = sdev->hostdata; |
2480 | scsi_remove_device(sdev); | 2711 | scsi_remove_device(sdev); |
2481 | scsi_device_put(sdev); | 2712 | scsi_device_put(sdev); |
@@ -2509,8 +2740,12 @@ mptsas_send_sas_event(MPT_ADAPTER *ioc, | |||
2509 | return; | 2740 | return; |
2510 | 2741 | ||
2511 | switch (sas_event_data->ReasonCode) { | 2742 | switch (sas_event_data->ReasonCode) { |
2512 | case MPI_EVENT_SAS_DEV_STAT_RC_ADDED: | ||
2513 | case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING: | 2743 | case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING: |
2744 | |||
2745 | mptsas_target_reset_queue(ioc, sas_event_data); | ||
2746 | break; | ||
2747 | |||
2748 | case MPI_EVENT_SAS_DEV_STAT_RC_ADDED: | ||
2514 | ev = kzalloc(sizeof(*ev), GFP_ATOMIC); | 2749 | ev = kzalloc(sizeof(*ev), GFP_ATOMIC); |
2515 | if (!ev) { | 2750 | if (!ev) { |
2516 | printk(KERN_WARNING "mptsas: lost hotplug event\n"); | 2751 | printk(KERN_WARNING "mptsas: lost hotplug event\n"); |
@@ -2871,8 +3106,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
2871 | sh->sg_tablesize = numSGE; | 3106 | sh->sg_tablesize = numSGE; |
2872 | } | 3107 | } |
2873 | 3108 | ||
2874 | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | ||
2875 | |||
2876 | hd = (MPT_SCSI_HOST *) sh->hostdata; | 3109 | hd = (MPT_SCSI_HOST *) sh->hostdata; |
2877 | hd->ioc = ioc; | 3110 | hd->ioc = ioc; |
2878 | 3111 | ||
@@ -2912,15 +3145,17 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
2912 | 3145 | ||
2913 | ioc->sas_data.ptClear = mpt_pt_clear; | 3146 | ioc->sas_data.ptClear = mpt_pt_clear; |
2914 | 3147 | ||
3148 | init_waitqueue_head(&hd->scandv_waitq); | ||
3149 | hd->scandv_wait_done = 0; | ||
3150 | hd->last_queue_full = 0; | ||
3151 | INIT_LIST_HEAD(&hd->target_reset_list); | ||
3152 | spin_unlock_irqrestore(&ioc->FreeQlock, flags); | ||
3153 | |||
2915 | if (ioc->sas_data.ptClear==1) { | 3154 | if (ioc->sas_data.ptClear==1) { |
2916 | mptbase_sas_persist_operation( | 3155 | mptbase_sas_persist_operation( |
2917 | ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT); | 3156 | ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT); |
2918 | } | 3157 | } |
2919 | 3158 | ||
2920 | init_waitqueue_head(&hd->scandv_waitq); | ||
2921 | hd->scandv_wait_done = 0; | ||
2922 | hd->last_queue_full = 0; | ||
2923 | |||
2924 | error = scsi_add_host(sh, &ioc->pcidev->dev); | 3159 | error = scsi_add_host(sh, &ioc->pcidev->dev); |
2925 | if (error) { | 3160 | if (error) { |
2926 | dprintk((KERN_ERR MYNAM | 3161 | dprintk((KERN_ERR MYNAM |
@@ -2999,7 +3234,7 @@ mptsas_init(void) | |||
2999 | return -ENODEV; | 3234 | return -ENODEV; |
3000 | 3235 | ||
3001 | mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER); | 3236 | mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER); |
3002 | mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER); | 3237 | mptsasTaskCtx = mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER); |
3003 | mptsasInternalCtx = | 3238 | mptsasInternalCtx = |
3004 | mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); | 3239 | mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); |
3005 | mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER); | 3240 | mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER); |
@@ -3009,7 +3244,7 @@ mptsas_init(void) | |||
3009 | ": Registered for IOC event notifications\n")); | 3244 | ": Registered for IOC event notifications\n")); |
3010 | } | 3245 | } |
3011 | 3246 | ||
3012 | if (mpt_reset_register(mptsasDoneCtx, mptscsih_ioc_reset) == 0) { | 3247 | if (mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset) == 0) { |
3013 | dprintk((KERN_INFO MYNAM | 3248 | dprintk((KERN_INFO MYNAM |
3014 | ": Registered for IOC reset notifications\n")); | 3249 | ": Registered for IOC reset notifications\n")); |
3015 | } | 3250 | } |