Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SCSI] fusion - serialize target resets in mptsas.c

Fusion firmware requires target reset following hotplug removal event,
with purpose to flush target outstanding request in fw. Current implementation
does the target resets from delayed work tasks, that in heavy load
conditions, take too long to be invoked, resulting in command time outs
This patch will issue target reset immediately from ISR context, and will
queue remaining target resets to be issued after the previous one completes.
The delayed work tasks are spawned during the target reset completion.

Signed-off-by: Eric Moore <Eric.Moore@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>

authored by

Eric Moore and committed by
James Bottomley
df9e062a b506ade9

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