diff options
Diffstat (limited to 'drivers/scsi/mpt2sas')
-rw-r--r-- | drivers/scsi/mpt2sas/Kconfig | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_history.txt | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_init.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.c | 90 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.h | 36 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_config.c | 8 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.c | 46 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_debug.h | 2 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 1055 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_transport.c | 89 |
15 files changed, 852 insertions, 490 deletions
diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig index ba8e128de238..bbb7e4bf30a3 100644 --- a/drivers/scsi/mpt2sas/Kconfig +++ b/drivers/scsi/mpt2sas/Kconfig @@ -2,7 +2,7 @@ # Kernel configuration file for the MPT2SAS # # This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2009 LSI Corporation +# Copyright (C) 2007-2010 LSI Corporation # (mailto:DL-MPTFusionLinux@lsi.com) # This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 9958d847a88d..dada0a13223f 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index cf0ac9f40c97..d4e9d6f8452e 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_cnfg.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt index c4adf76b49d9..bd6c92b5fae5 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt +++ b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt @@ -2,7 +2,7 @@ Fusion-MPT MPI 2.0 Header File Change History ============================== - Copyright (c) 2000-2009 LSI Corporation. + Copyright (c) 2000-2010 LSI Corporation. --------------------------------------- Header Set Release Version: 02.00.14 diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h index 6541945e97c3..220bf65a9216 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_init.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 754938422f6a..f18f114922ba 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_ioc.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 73fcdbf92632..686b09b81219 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_tool.h diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 88e6eebc3159..b830d61684dd 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -58,6 +58,7 @@ #include <linux/sort.h> #include <linux/io.h> #include <linux/time.h> +#include <linux/aer.h> #include "mpt2sas_base.h" @@ -285,6 +286,9 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) return; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + return; + switch (ioc_status) { /**************************************************************************** @@ -517,8 +521,18 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc, desc = "IR Operation Status"; break; case MPI2_EVENT_SAS_DISCOVERY: - desc = "Discovery"; - break; + { + Mpi2EventDataSasDiscovery_t *event_data = + (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; + printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name, + (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? + "start" : "stop"); + if (event_data->DiscoveryStatus) + printk("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); + printk("\n"); + return; + } case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: desc = "SAS Broadcast Primitive"; break; @@ -1243,6 +1257,9 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) goto out_fail; } + /* AER (Advanced Error Reporting) hooks */ + pci_enable_pcie_error_reporting(pdev); + pci_set_master(pdev); if (_base_config_dma_addressing(ioc, pdev) != 0) { @@ -1253,7 +1270,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { - if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) { + if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { if (pio_sz) continue; pio_chip = (u64)pci_resource_start(pdev, i); @@ -1261,15 +1278,18 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } else { if (memap_sz) continue; - ioc->chip_phys = pci_resource_start(pdev, i); - chip_phys = (u64)ioc->chip_phys; - memap_sz = pci_resource_len(pdev, i); - ioc->chip = ioremap(ioc->chip_phys, memap_sz); - if (ioc->chip == NULL) { - printk(MPT2SAS_ERR_FMT "unable to map adapter " - "memory!\n", ioc->name); - r = -EINVAL; - goto out_fail; + /* verify memory resource is valid before using */ + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + ioc->chip_phys = pci_resource_start(pdev, i); + chip_phys = (u64)ioc->chip_phys; + memap_sz = pci_resource_len(pdev, i); + ioc->chip = ioremap(ioc->chip_phys, memap_sz); + if (ioc->chip == NULL) { + printk(MPT2SAS_ERR_FMT "unable to map " + "adapter memory!\n", ioc->name); + r = -EINVAL; + goto out_fail; + } } } } @@ -1295,6 +1315,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) ioc->chip_phys = 0; ioc->pci_irq = -1; pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); return r; } @@ -1898,7 +1919,10 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) ioc->config_page, ioc->config_page_dma); } - kfree(ioc->scsi_lookup); + if (ioc->scsi_lookup) { + free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); + ioc->scsi_lookup = NULL; + } kfree(ioc->hpr_lookup); kfree(ioc->internal_lookup); } @@ -2110,11 +2134,13 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->name, (unsigned long long) ioc->request_dma)); total_sz += sz; - ioc->scsi_lookup = kcalloc(ioc->scsiio_depth, - sizeof(struct request_tracker), GFP_KERNEL); + sz = ioc->scsiio_depth * sizeof(struct request_tracker); + ioc->scsi_lookup_pages = get_order(sz); + ioc->scsi_lookup = (struct request_tracker *)__get_free_pages( + GFP_KERNEL, ioc->scsi_lookup_pages); if (!ioc->scsi_lookup) { - printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", - ioc->name); + printk(MPT2SAS_ERR_FMT "scsi_lookup: get_free_pages failed, " + "sz(%d)\n", ioc->name, (int)sz); goto out; } @@ -3006,8 +3032,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) * since epoch ~ midnight January 1, 1970. */ do_gettimeofday(¤t_time); - mpi_request.TimeStamp = (current_time.tv_sec * 1000) + - (current_time.tv_usec >> 3); + mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 + + (current_time.tv_usec / 1000)); if (ioc->logging_level & MPT_DEBUG_INIT) { u32 *mfp; @@ -3179,7 +3205,7 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request->VP_ID = 0; for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) mpi_request->EventMasks[i] = - le32_to_cpu(ioc->event_masks[i]); + cpu_to_le32(ioc->event_masks[i]); mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); @@ -3516,7 +3542,9 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) __func__)); _base_mask_interrupts(ioc); + ioc->shost_recovery = 1; _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + ioc->shost_recovery = 0; if (ioc->pci_irq) { synchronize_irq(pdev->irq); free_irq(ioc->pci_irq, ioc); @@ -3527,6 +3555,7 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) ioc->pci_irq = -1; ioc->chip_phys = 0; pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); return; } @@ -3560,8 +3589,10 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); - if (!ioc->pfacts) + if (!ioc->pfacts) { + r = -ENOMEM; goto out_free_resources; + } for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { r = _base_get_port_facts(ioc, i, CAN_SLEEP); @@ -3607,6 +3638,15 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->ctl_cmds.mutex); + if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || + !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || + !ioc->config_cmds.reply || !ioc->ctl_cmds.reply) { + r = -ENOMEM; + goto out_free_resources; + } + + init_completion(&ioc->shost_recovery_done); + for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; @@ -3639,6 +3679,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); kfree(ioc->base_cmds.reply); kfree(ioc->ctl_cmds.reply); @@ -3646,6 +3687,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.reply = NULL; ioc->base_cmds.reply = NULL; ioc->tm_cmds.reply = NULL; + ioc->scsih_cmds.reply = NULL; ioc->transport_cmds.reply = NULL; ioc->config_cmds.reply = NULL; ioc->pfacts = NULL; @@ -3675,6 +3717,7 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) kfree(ioc->base_cmds.reply); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); } @@ -3811,9 +3854,8 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ioc->shost_recovery = 0; + complete(&ioc->shost_recovery_done); spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - if (!r) - _base_reset_handler(ioc, MPT2_IOC_RUNNING); return r; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index e18b0544c38f..b4afe431ac1e 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -69,11 +69,11 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "04.100.01.00" -#define MPT2SAS_MAJOR_VERSION 04 +#define MPT2SAS_DRIVER_VERSION "05.100.00.02" +#define MPT2SAS_MAJOR_VERSION 05 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 01 -#define MPT2SAS_RELEASE_VERSION 00 +#define MPT2SAS_BUILD_VERSION 00 +#define MPT2SAS_RELEASE_VERSION 02 /* * Set MPT2SAS_SG_DEPTH value based on user input. @@ -119,7 +119,6 @@ #define MPT2_IOC_PRE_RESET 1 /* prior to host reset */ #define MPT2_IOC_AFTER_RESET 2 /* just after host reset */ #define MPT2_IOC_DONE_RESET 3 /* links re-initialized */ -#define MPT2_IOC_RUNNING 4 /* shost running */ /* * logging format @@ -260,16 +259,6 @@ struct _internal_cmd { u16 smid; }; -/* - * SAS Topology Structures - */ - -#define MPTSAS_STATE_TR_SEND 0x0001 -#define MPTSAS_STATE_TR_COMPLETE 0x0002 -#define MPTSAS_STATE_CNTRL_SEND 0x0004 -#define MPTSAS_STATE_CNTRL_COMPLETE 0x0008 - -#define MPT2SAS_REQ_SAS_CNTRL 0x0010 /** * struct _sas_device - attached device information @@ -307,7 +296,6 @@ struct _sas_device { u16 slot; u8 hidden_raid_component; u8 responding; - u16 state; }; /** @@ -378,6 +366,7 @@ struct _sas_port { * @phy_id: unique phy id * @handle: device handle for this phy * @attached_handle: device handle for attached device + * @phy_belongs_to_port: port has been created for this phy */ struct _sas_phy { struct list_head port_siblings; @@ -387,6 +376,7 @@ struct _sas_phy { u8 phy_id; u16 handle; u16 attached_handle; + u8 phy_belongs_to_port; }; /** @@ -603,7 +593,6 @@ struct MPT2SAS_ADAPTER { /* fw event handler */ char firmware_event_name[20]; struct workqueue_struct *firmware_event_thread; - u8 fw_events_off; spinlock_t fw_event_lock; struct list_head fw_event_list; @@ -611,6 +600,7 @@ struct MPT2SAS_ADAPTER { int aen_event_read_flag; u8 broadcast_aen_busy; u8 shost_recovery; + struct completion shost_recovery_done; spinlock_t ioc_reset_in_progress_lock; u8 ioc_link_reset_in_progress; u8 ignore_loginfos; @@ -688,7 +678,8 @@ struct MPT2SAS_ADAPTER { dma_addr_t request_dma; u32 request_dma_sz; struct request_tracker *scsi_lookup; - spinlock_t scsi_lookup_lock; + ulong scsi_lookup_pages; + spinlock_t scsi_lookup_lock; struct list_head free_list; int pending_io_count; wait_queue_head_t reset_wq; @@ -700,7 +691,7 @@ struct MPT2SAS_ADAPTER { u16 max_sges_in_chain_message; u16 chains_needed_per_io; u16 chain_offset_value_for_main_message; - u16 chain_depth; + u32 chain_depth; /* hi-priority queue */ u16 hi_priority_smid; @@ -814,8 +805,9 @@ void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); /* scsih shared API */ u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); -void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout); +int mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, + ulong timeout, struct scsi_cmnd *scmd); void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index cf44b355bc97..e762dd3e2fcb 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -2,7 +2,7 @@ * This module provides common API for accessing firmware configuration pages * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -1390,12 +1390,12 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, if (ioc_status != MPI2_IOCSTATUS_SUCCESS) goto out; for (i = 0; i < config_page->NumElements; i++) { - if ((config_page->ConfigElement[i].ElementFlags & + if ((le16_to_cpu(config_page->ConfigElement[i].ElementFlags) & MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) != MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT) continue; - if (config_page->ConfigElement[i].PhysDiskDevHandle == - pd_handle) { + if (le16_to_cpu(config_page->ConfigElement[i]. + PhysDiskDevHandle) == pd_handle) { *volume_handle = le16_to_cpu(config_page-> ConfigElement[i].VolDevHandle); r = 0; diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index fa9bf83819d5..d88e9756d8f5 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -533,7 +533,7 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, if (!found) { dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name, - desc, tm_request->DevHandle, lun)); + desc, le16_to_cpu(tm_request->DevHandle), lun)); tm_reply = ioc->ctl_cmds.reply; tm_reply->DevHandle = tm_request->DevHandle; tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -551,7 +551,8 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, - desc, tm_request->DevHandle, lun, tm_request->TaskMID)); + desc, le16_to_cpu(tm_request->DevHandle), lun, + le16_to_cpu(tm_request->TaskMID))); return 0; } @@ -647,9 +648,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - if (!mpi_request->FunctionDependent1 || - mpi_request->FunctionDependent1 > - cpu_to_le16(ioc->facts.MaxDevHandle)) { + if (!le16_to_cpu(mpi_request->FunctionDependent1) || + le16_to_cpu(mpi_request->FunctionDependent1) > + ioc->facts.MaxDevHandle) { ret = -EINVAL; mpt2sas_base_free_smid(ioc, smid); goto out; @@ -743,8 +744,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, mpt2sas_base_get_sense_buffer_dma(ioc, smid); priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); - mpt2sas_base_put_smid_scsi_io(ioc, smid, - le16_to_cpu(mpi_request->FunctionDependent1)); + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + le16_to_cpu(mpi_request->FunctionDependent1)); + else + mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SCSI_TASK_MGMT: @@ -752,6 +756,10 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: " + "handle(0x%04x), task_type(0x%02x)\n", ioc->name, + le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); + if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || tm_request->TaskType == @@ -762,7 +770,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, } } - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); mpt2sas_base_put_smid_hi_priority(ioc, smid); @@ -818,7 +825,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; - mutex_unlock(&ioc->tm_cmds.mutex); mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || @@ -897,14 +903,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { printk(MPT2SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, - mpi_request->FunctionDependent1); + le16_to_cpu(mpi_request->FunctionDependent1)); mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_issue_tm(ioc, - mpi_request->FunctionDependent1, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); + le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10, + NULL); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); } else mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); @@ -1373,7 +1378,8 @@ _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, - (unsigned long long)request_data_dma, mpi_request->BufferLength)); + (unsigned long long)request_data_dma, + le32_to_cpu(mpi_request->BufferLength))); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = @@ -2334,8 +2340,8 @@ _ctl_version_nvdata_persistent_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); } static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, _ctl_version_nvdata_persistent_show, NULL); @@ -2354,8 +2360,8 @@ _ctl_version_nvdata_default_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); } static DEVICE_ATTR(version_nvdata_default, S_IRUGO, _ctl_version_nvdata_default_show, NULL); diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 8a5eeb1a5c84..69916e46e04f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h index 5308a25cb307..3dcddfeb6f4c 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -2,7 +2,7 @@ * Logging Support for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index be171ed682e0..c5ff26a2a51d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@ * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -52,6 +52,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/aer.h> #include <linux/raid_class.h> #include <linux/slab.h> @@ -109,14 +110,16 @@ struct sense_info { }; +#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) + /** * struct fw_event_work - firmware event struct * @list: link list framework * @work: work object (ioc->fault_reset_work_q) + * @cancel_pending_work: flag set during reset handling * @ioc: per adapter object * @VF_ID: virtual function id * @VP_ID: virtual port id - * @host_reset_handling: handling events during host reset * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h * @event_data: reply event data payload follows @@ -125,11 +128,11 @@ struct sense_info { */ struct fw_event_work { struct list_head list; - struct work_struct work; + u8 cancel_pending_work; + struct delayed_work delayed_work; struct MPT2SAS_ADAPTER *ioc; u8 VF_ID; u8 VP_ID; - u8 host_reset_handling; u8 ignore; u16 event; void *event_data; @@ -482,27 +485,17 @@ struct _sas_device * mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - /* check the sas_device_init_list */ - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; - /* then check the sas_device_list */ - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; + + return NULL; } /** @@ -517,28 +510,17 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, static struct _sas_device * _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - if (ioc->wait_for_port_enable_to_complete) { - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } else { - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->handle == handle) + return sas_device; - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->handle == handle) + return sas_device; + + return NULL; } /** @@ -555,10 +537,15 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; + if (!sas_device) + return; + spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_del(&sas_device->list); - memset(sas_device, 0, sizeof(struct _sas_device)); - kfree(sas_device); + if (mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device->sas_address)) { + list_del(&sas_device->list); + kfree(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -988,7 +975,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, u32 chain_offset; u32 chain_length; u32 chain_flags; - u32 sges_left; + int sges_left; u32 sges_in_segment; u32 sgl_flags; u32 sgl_flags_last_element; @@ -1009,7 +996,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, sg_scmd = scsi_sglist(scmd); sges_left = scsi_dma_map(scmd); - if (!sges_left) { + if (sges_left < 0) { sdev_printk(KERN_ERR, scmd->device, "pci_map_sg" " failed: request for %d bytes!\n", scsi_bufflen(scmd)); return -ENOMEM; @@ -1395,7 +1382,7 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, } flags = le16_to_cpu(sas_device_pg0.Flags); - device_info = le16_to_cpu(sas_device_pg0.DeviceInfo); + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); sdev_printk(KERN_INFO, sdev, "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " @@ -1963,65 +1950,78 @@ mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) } } + /** * mpt2sas_scsih_issue_tm - main routine for sending tm requests * @ioc: per adapter struct * @device_handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @timeout: timeout in seconds - * Context: The calling function needs to acquire the tm_cmds.mutex + * Context: user * * A generic API for sending task management requests to firmware. * - * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling - * this API. - * * The callback index is set inside `ioc->tm_cb_idx`. * - * Return nothing. + * Return SUCCESS or FAILED. */ -void -mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout) +int +mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel, + uint id, uint lun, u8 type, u16 smid_task, ulong timeout, + struct scsi_cmnd *scmd) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; u16 smid = 0; u32 ioc_state; unsigned long timeleft; + struct scsi_cmnd *scmd_lookup; + int rc; + mutex_lock(&ioc->tm_cmds.mutex); if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->remove_host) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } ioc_state = mpt2sas_base_get_iocstate(ioc, 0); if (ioc_state & MPI2_DOORBELL_USED) { dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " "active!\n", ioc->name)); - goto issue_host_reset; + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + goto err_out; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt2sas_base_fault_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); - goto issue_host_reset; + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + goto err_out; } smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return; + rc = FAILED; + goto err_out; } dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," @@ -2035,21 +2035,24 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; mpi_request->TaskMID = cpu_to_le16(smid_task); - mpi_request->VP_ID = 0; /* TODO */ - mpi_request->VF_ID = 0; int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt2sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); mpt2sas_base_put_smid_hi_priority(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); - mpt2sas_scsih_clear_tm_flag(ioc, handle); if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2SCSITaskManagementRequest_t)/4); - if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) - goto issue_host_reset; + if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) { + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mpt2sas_scsih_clear_tm_flag(ioc, handle); + goto err_out; + } } if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { @@ -2059,12 +2062,57 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo), le32_to_cpu(mpi_reply->TerminationCount))); - if (ioc->logging_level & MPT_DEBUG_TM) + if (ioc->logging_level & MPT_DEBUG_TM) { _scsih_response_code(ioc, mpi_reply->ResponseCode); + if (mpi_reply->IOCStatus) + _debug_dump_mf(mpi_request, + sizeof(Mpi2SCSITaskManagementRequest_t)/4); + } } - return; - issue_host_reset: - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); + + /* sanity check: + * Check to see the commands were terminated. + * This is only needed for eh callbacks, hence the scmd check. + */ + rc = FAILED; + if (scmd == NULL) + goto bypass_sanity_checks; + switch (type) { + case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: + scmd_lookup = _scsih_scsi_lookup_get(ioc, smid_task); + if (scmd_lookup && (scmd_lookup->serial_number == + scmd->serial_number)) + rc = FAILED; + else + rc = SUCCESS; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + } + + bypass_sanity_checks: + + mpt2sas_scsih_clear_tm_flag(ioc, handle); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + + return rc; + + err_out: + mutex_unlock(&ioc->tm_cmds.mutex); + return rc; } /** @@ -2081,7 +2129,6 @@ _scsih_abort(struct scsi_cmnd *scmd) u16 smid; u16 handle; int r; - struct scsi_cmnd *scmd_lookup; printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n", ioc->name, scmd); @@ -2116,19 +2163,10 @@ _scsih_abort(struct scsi_cmnd *scmd) mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); handle = sas_device_priv_data->sas_target->handle; - mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); - - /* sanity check - see whether command actually completed */ - scmd_lookup = _scsih_scsi_lookup_get(ioc, smid); - if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, scmd); out: printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n", @@ -2185,22 +2223,9 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun, - 30); - - /* - * sanity check see whether all commands to this device been - * completed - */ - if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id, - scmd->device->lun, scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, scmd); out: printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n", @@ -2257,21 +2282,9 @@ _scsih_target_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); - - /* - * sanity check see whether all commands to this target been - * completed - */ - if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id, - scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 30, scmd); out: printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n", @@ -2325,8 +2338,9 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) spin_lock_irqsave(&ioc->fw_event_lock, flags); list_add_tail(&fw_event->list, &ioc->fw_event_list); - INIT_WORK(&fw_event->work, _firmware_event_work); - queue_work(ioc->firmware_event_thread, &fw_event->work); + INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); + queue_delayed_work(ioc->firmware_event_thread, + &fw_event->delayed_work, 0); spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } @@ -2353,61 +2367,53 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } + /** - * _scsih_fw_event_add - requeue an event + * _scsih_queue_rescan - queue a topology rescan from user context * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock. * * Return nothing. */ static void -_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work - *fw_event, unsigned long delay) +_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; - if (ioc->firmware_event_thread == NULL) - return; + struct fw_event_work *fw_event; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - queue_work(ioc->firmware_event_thread, &fw_event->work); - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->wait_for_port_enable_to_complete) + return; + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); } /** - * _scsih_fw_event_off - turn flag off preventing event handling + * _scsih_fw_event_cleanup_queue - cleanup event queue * @ioc: per adapter object * - * Used to prevent handling of firmware events during adapter reset - * driver unload. + * Walk the firmware event queue, either killing timers, or waiting + * for outstanding events to complete * * Return nothing. */ static void -_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc) +_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; + struct fw_event_work *fw_event, *next; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 1; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - -} - -/** - * _scsih_fw_event_on - turn flag on allowing firmware event handling - * @ioc: per adapter object - * - * Returns nothing. - */ -static void -_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; + if (list_empty(&ioc->fw_event_list) || + !ioc->firmware_event_thread || in_interrupt()) + return; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 0; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { + if (cancel_delayed_work(&fw_event->delayed_work)) { + _scsih_fw_event_free(ioc, fw_event); + continue; + } + fw_event->cancel_pending_work = 1; + } } /** @@ -2571,25 +2577,24 @@ static void _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SCSITaskManagementRequest_t *mpi_request; - struct MPT2SAS_TARGET *sas_target_priv_data; u16 smid; struct _sas_device *sas_device; unsigned long flags; struct _tr_list *delayed_tr; - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); + if (ioc->shost_recovery || ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); return; } spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - /* skip is hidden raid component */ - if (sas_device && sas_device->hidden_raid_component) + if (sas_device && sas_device->hidden_raid_component) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); if (!smid) { @@ -2598,36 +2603,16 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; INIT_LIST_HEAD(&delayed_tr->list); delayed_tr->handle = handle; - delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; - list_add_tail(&delayed_tr->list, - &ioc->delayed_tr_list); - if (sas_device && sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "DELAYED:tr:handle(0x%04x), " - "(open)\n", handle)); - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", - ioc->name, handle)); - } - return; - } - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_SEND; - sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 1; - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr:handle(0x%04x), (open)\n", - handle)); - } - } else { + list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr:handle(0x%04x), (open)\n", ioc->name, handle)); + "DELAYED:tr:handle(0x%04x), (open)\n", + ioc->name, handle)); + return; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, + ioc->tm_tr_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -2657,35 +2642,15 @@ static u8 _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; - u16 handle; - struct _sas_device *sas_device; Mpi2SasIoUnitControlReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - handle = le16_to_cpu(mpi_reply->DevHandle); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; - if (sas_device->starget) - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } - + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "sc_complete:handle(0x%04x), (open) " + "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); return 1; } @@ -2709,87 +2674,63 @@ static u8 _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; u16 handle; - struct _sas_device *sas_device; + Mpi2SCSITaskManagementRequest_t *mpi_request_tm; Mpi2SCSITaskManagementReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); Mpi2SasIoUnitControlRequest_t *mpi_request; u16 smid_sas_ctrl; - struct MPT2SAS_TARGET *sas_target_priv_data; struct _tr_list *delayed_tr; - u8 rc; - handle = le16_to_cpu(mpi_reply->DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_COMPLETE; - if (sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr_complete:handle(0x%04x), " - "(%s) ioc_status(0x%04x), loginfo(0x%08x), " - "completed(%d)\n", sas_device->handle, - (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ? - "open" : "active", - le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - if (sas_device->starget->hostdata) { - sas_target_priv_data = - sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 0; - } - } - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", ioc->name, - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); + if (ioc->shost_recovery || ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); + return 1; } - if (!list_empty(&ioc->delayed_tr_list)) { - delayed_tr = list_entry(ioc->delayed_tr_list.next, - struct _tr_list, list); - mpt2sas_base_free_smid(ioc, smid); - if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) - _scsih_tm_tr_send(ioc, delayed_tr->handle); - list_del(&delayed_tr->list); - kfree(delayed_tr); - rc = 0; /* tells base_interrupt not to free mf */ - } else - rc = 1; - - if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) - return rc; - - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return rc; + mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); + handle = le16_to_cpu(mpi_request_tm->DevHandle); + if (handle != le16_to_cpu(mpi_reply->DevHandle)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: " + "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle, + le16_to_cpu(mpi_reply->DevHandle), smid)); + return 0; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", ioc->name, + handle, smid, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); if (!smid_sas_ctrl) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return rc; + return 1; } - if (sas_device) - sas_device->state |= MPTSAS_STATE_CNTRL_SEND; - + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl, + ioc->tm_sas_control_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request->DevHandle = mpi_reply->DevHandle; + mpi_request->DevHandle = mpi_request_tm->DevHandle; mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); - return rc; + + if (!list_empty(&ioc->delayed_tr_list)) { + delayed_tr = list_entry(ioc->delayed_tr_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + _scsih_tm_tr_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + return 0; /* tells base_interrupt not to free mf */ + } + return 1; } /** @@ -3021,25 +2962,32 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) scmd->scsi_done = done; sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data) { + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } sas_target_priv_data = sas_device_priv_data->sas_target; - if (!sas_target_priv_data || sas_target_priv_data->handle == - MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) { + /* invalid device handle */ + if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } - /* see if we are busy with task managment stuff */ - if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) - return SCSI_MLQUEUE_DEVICE_BUSY; - else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) + /* host recovery or link resets sent via IOCTLs */ + if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) return SCSI_MLQUEUE_HOST_BUSY; + /* device busy with task managment */ + else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) + return SCSI_MLQUEUE_DEVICE_BUSY; + /* device has been deleted */ + else if (sas_target_priv_data->deleted) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } if (scmd->sc_data_direction == DMA_FROM_DEVICE) mpi_control = MPI2_SCSIIO_CONTROL_READ; @@ -3110,8 +3058,11 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } } - mpt2sas_base_put_smid_scsi_io(ioc, smid, - sas_device_priv_data->sas_target->handle); + if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + sas_device_priv_data->sas_target->handle); + else + mpt2sas_base_put_smid_default(ioc, smid); return 0; out: @@ -3301,8 +3252,8 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, struct sense_info data; _scsih_normalize_sense(scmd->sense_buffer, &data); printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " - "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey, - data.asc, data.ascq); + "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey, + data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); } if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { @@ -3356,7 +3307,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; mpi_request.SlotStatus = - MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; + cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); mpi_request.DevHandle = cpu_to_le16(handle); mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, @@ -4008,6 +3959,134 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) } /** + * _scsih_check_access_status - check access flags + * @ioc: per adapter object + * @sas_address: sas address + * @handle: sas device handle + * @access_flags: errors returned during discovery of the device + * + * Return 0 for success, else failure + */ +static u8 +_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 handle, u8 access_status) +{ + u8 rc = 1; + char *desc = NULL; + + switch (access_status) { + case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: + case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: + rc = 0; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: + desc = "sata capability failed"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: + desc = "sata affiliation conflict"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: + desc = "route not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: + desc = "smp error not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: + desc = "device blocked"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: + desc = "sata initialization failed"; + break; + default: + desc = "unknown"; + break; + } + + if (!rc) + return 0; + + printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), " + "handle(0x%04x)\n", ioc->name, desc, + (unsigned long long)sas_address, handle); + return rc; +} + +static void +_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + struct _sas_device *sas_device; + u32 ioc_status; + unsigned long flags; + u64 sas_address; + struct scsi_target *starget; + struct MPT2SAS_TARGET *sas_target_priv_data; + u32 device_info; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) + return; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + return; + + /* check if this is end device */ + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + if (!(_scsih_is_end_device(device_info))) + return; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + + if (!sas_device) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + + if (unlikely(sas_device->handle != handle)) { + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)" + " to (0x%04x)!!!\n", sas_device->handle, handle); + sas_target_priv_data->handle = handle; + sas_device->handle = handle; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* check if device is present */ + if (!(le16_to_cpu(sas_device_pg0.Flags) & + MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), flags!!!\n", ioc->name, handle); + return; + } + + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) + return; + _scsih_ublock_io_device(ioc, handle); + +} + +/** * _scsih_add_device - creating sas device object * @ioc: per adapter object * @handle: sas device handle @@ -4045,6 +4124,8 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + /* check if device is present */ if (!(le16_to_cpu(sas_device_pg0.Flags) & MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { @@ -4055,15 +4136,10 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - /* check if there were any issus with discovery */ - if (sas_device_pg0.AccessStatus == - MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n", - ioc->name, sas_device_pg0.AccessStatus); + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) return -1; - } /* check if this is end device */ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); @@ -4073,17 +4149,14 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) { - _scsih_ublock_io_device(ioc, handle); + if (sas_device) return 0; - } sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); @@ -4126,67 +4199,38 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) } /** - * _scsih_remove_device - removing sas device object + * _scsih_remove_pd_device - removing sas device pd object * @ioc: per adapter object - * @sas_device: the sas_device object + * @sas_device_delete: the sas_device object * + * For hidden raid components, we do driver-fw handshake from + * hotplug work threads. * Return nothing. */ static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device - *sas_device) +_scsih_remove_pd_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device + sas_device) { - struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; - u16 device_handle, handle; - - if (!sas_device) - return; + u16 vol_handle, handle; - handle = sas_device->handle; + handle = sas_device.handle; dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x)," " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, - (unsigned long long) sas_device->sas_address)); - - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - } - - if (ioc->remove_host || ioc->shost_recovery || !handle) - goto out; + (unsigned long long) sas_device.sas_address)); - if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "target_reset handle(0x%04x)\n", ioc->name, - handle)); - goto skip_tr; - } - - /* Target Reset to flush out all the outstanding IO */ - device_handle = (sas_device->hidden_raid_component) ? - sas_device->volume_handle : handle; - if (device_handle) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " - "handle(0x%04x)\n", ioc->name, device_handle)); - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, device_handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " - "done: handle(0x%04x)\n", ioc->name, device_handle)); - if (ioc->shost_recovery) - goto out; - } - skip_tr: - - if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); - goto out; - } + vol_handle = sas_device.volume_handle; + if (!vol_handle) + return; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " + "handle(0x%04x)\n", ioc->name, vol_handle)); + mpt2sas_scsih_issue_tm(ioc, vol_handle, 0, 0, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, NULL); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " + "done: handle(0x%04x)\n", ioc->name, vol_handle)); + if (ioc->shost_recovery) + return; /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" @@ -4194,34 +4238,68 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request.DevHandle = handle; - mpi_request.VF_ID = 0; /* TODO */ - mpi_request.VP_ID = 0; + mpi_request.DevHandle = cpu_to_le16(handle); if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, - &mpi_request)) != 0) { + &mpi_request)) != 0) printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - } dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status" "(0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo))); - out: + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle(0x%04x)," + " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, + (unsigned long long) sas_device.sas_address)); +} - _scsih_ublock_io_device(ioc, handle); +/** + * _scsih_remove_device - removing sas device object + * @ioc: per adapter object + * @sas_device_delete: the sas_device object + * + * Return nothing. + */ +static void +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + struct _sas_device sas_device_backup; + struct MPT2SAS_TARGET *sas_target_priv_data; - mpt2sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); + if (!sas_device) + return; - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" - "(0x%016llx)\n", ioc->name, handle, - (unsigned long long) sas_device->sas_address); + memcpy(&sas_device_backup, sas_device, sizeof(struct _sas_device)); _scsih_sas_device_remove(ioc, sas_device); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle" - "(0x%04x)\n", ioc->name, __func__, handle)); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device_backup.handle, (unsigned long long) + sas_device_backup.sas_address)); + + if (sas_device_backup.starget && sas_device_backup.starget->hostdata) { + sas_target_priv_data = sas_device_backup.starget->hostdata; + sas_target_priv_data->deleted = 1; + } + + if (sas_device_backup.hidden_raid_component) + _scsih_remove_pd_device(ioc, sas_device_backup); + + _scsih_ublock_io_device(ioc, sas_device_backup.handle); + + mpt2sas_transport_port_remove(ioc, sas_device_backup.sas_address, + sas_device_backup.sas_address_parent); + + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" + "(0x%016llx)\n", ioc->name, sas_device_backup.handle, + (unsigned long long) sas_device_backup.sas_address); + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device_backup.handle, (unsigned long long) + sas_device_backup.sas_address)); } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4331,7 +4409,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_topology_change_event_debug(ioc, event_data); #endif - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->remove_host) return; if (!ioc->sas_hba.num_phys) @@ -4370,7 +4448,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, "expander event\n", ioc->name)); return; } - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->remove_host) return; phy_number = event_data->StartPhyNum + i; reason_code = event_data->PHY[i].PhyStatus & @@ -4393,8 +4471,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, mpt2sas_transport_update_links(ioc, sas_address, handle, phy_number, link_rate); - if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) - _scsih_ublock_io_device(ioc, handle); + if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) + break; + + _scsih_check_device(ioc, handle); break; case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: @@ -4520,10 +4600,10 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, event_data); #endif - if (!(event_data->ReasonCode == + if (event_data->ReasonCode != MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && - event_data->ReasonCode == - MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)) + event_data->ReasonCode != + MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); @@ -4630,7 +4710,6 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, __func__)); - mutex_lock(&ioc->tm_cmds.mutex); termination_count = 0; query_count = 0; mpi_reply = ioc->tm_cmds.reply; @@ -4654,8 +4733,8 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, lun = sas_device_priv_data->lun; query_count++; - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); + mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, NULL); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; @@ -4666,13 +4745,11 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) continue; - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30, NULL); termination_count += le32_to_cpu(mpi_reply->TerminationCount); } ioc->broadcast_aen_busy = 0; - mutex_unlock(&ioc->tm_cmds.mutex); dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - exit, query_count = %d termination_count = %d\n", @@ -5442,6 +5519,26 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work } /** + * _scsih_prep_device_scan - initialize parameters prior to device scan + * @ioc: per adapter object + * + * Set the deleted flag prior to device scan. If the device is found during + * the scan, then we clear the deleted flag. + */ +static void +_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (sas_device_priv_data && sas_device_priv_data->sas_target) + sas_device_priv_data->sas_target->deleted = 1; + } +} + +/** * _scsih_mark_responding_sas_device - mark a sas_devices as responding * @ioc: per adapter object * @sas_address: sas address @@ -5467,10 +5564,13 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, if (sas_device->sas_address == sas_address && sas_device->slot == slot && sas_device->starget) { sas_device->responding = 1; - sas_device->state = 0; starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->tm_busy = 0; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; starget_printk(KERN_INFO, sas_device->starget, "handle(0x%04x), sas_addr(0x%016llx), enclosure " "logical id(0x%016llx), slot(%d)\n", handle, @@ -5483,7 +5583,8 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", sas_device->handle); sas_device->handle = handle; - sas_target_priv_data->handle = handle; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; goto out; } } @@ -5558,6 +5659,12 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, spin_lock_irqsave(&ioc->raid_device_lock, flags); list_for_each_entry(raid_device, &ioc->raid_device_list, list) { if (raid_device->wwid == wwid && raid_device->starget) { + starget = raid_device->starget; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; raid_device->responding = 1; starget_printk(KERN_INFO, raid_device->starget, "handle(0x%04x), wwid(0x%016llx)\n", handle, @@ -5567,9 +5674,8 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", raid_device->handle); raid_device->handle = handle; - starget = raid_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->handle = handle; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; goto out; } } @@ -5694,13 +5800,13 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) } /** - * _scsih_remove_unresponding_devices - removing unresponding devices + * _scsih_remove_unresponding_sas_devices - removing unresponding devices * @ioc: per adapter object * * Return nothing. */ static void -_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) { struct _sas_device *sas_device, *sas_device_next; struct _sas_node *sas_expander; @@ -5722,8 +5828,6 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) (unsigned long long) sas_device->enclosure_logical_id, sas_device->slot); - /* invalidate the device handle */ - sas_device->handle = 0; _scsih_remove_device(ioc, sas_device); } @@ -5774,32 +5878,33 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) case MPT2_IOC_PRE_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - _scsih_fw_event_off(ioc); break; case MPT2_IOC_AFTER_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) { + ioc->scsih_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); + complete(&ioc->scsih_cmds.done); + } if (ioc->tm_cmds.status & MPT2_CMD_PENDING) { ioc->tm_cmds.status |= MPT2_CMD_RESET; mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); complete(&ioc->tm_cmds.done); } - _scsih_fw_event_on(ioc); + _scsih_fw_event_cleanup_queue(ioc); _scsih_flush_running_cmds(ioc); + _scsih_queue_rescan(ioc); break; case MPT2_IOC_DONE_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); _scsih_sas_host_refresh(ioc); + _scsih_prep_device_scan(ioc); _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); break; - case MPT2_IOC_RUNNING: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "MPT2_IOC_RUNNING\n", ioc->name, __func__)); - _scsih_remove_unresponding_devices(ioc); - break; } } @@ -5815,21 +5920,28 @@ static void _firmware_event_work(struct work_struct *work) { struct fw_event_work *fw_event = container_of(work, - struct fw_event_work, work); + struct fw_event_work, delayed_work.work); unsigned long flags; struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; /* the queue is being flushed so ignore this event */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host || fw_event->cancel_pending_work) { _scsih_fw_event_free(ioc, fw_event); return; } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - if (ioc->shost_recovery) { - _scsih_fw_event_requeue(ioc, fw_event, 1000); + if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) { + _scsih_fw_event_free(ioc, fw_event); + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->shost_recovery) { + init_completion(&ioc->shost_recovery_done); + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + wait_for_completion(&ioc->shost_recovery_done); + } else + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + _scsih_remove_unresponding_sas_devices(ioc); return; } @@ -5891,16 +6003,12 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, { struct fw_event_work *fw_event; Mpi2EventNotificationReply_t *mpi_reply; - unsigned long flags; u16 event; + u16 sz; /* events turned off due to host reset or driver unloading */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host) return 1; - } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); event = le16_to_cpu(mpi_reply->Event); @@ -5947,8 +6055,8 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, ioc->name, __FILE__, __LINE__, __func__); return 1; } - fw_event->event_data = - kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); + sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; + fw_event->event_data = kzalloc(sz, GFP_ATOMIC); if (!fw_event->event_data) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -5957,7 +6065,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, } memcpy(fw_event->event_data, mpi_reply->EventData, - mpi_reply->EventDataLength*4); + sz); fw_event->ioc = ioc; fw_event->VF_ID = mpi_reply->VF_ID; fw_event->VP_ID = mpi_reply->VP_ID; @@ -6158,6 +6266,18 @@ _scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct workqueue_struct *wq; + unsigned long flags; + + ioc->remove_host = 1; + _scsih_fw_event_cleanup_queue(ioc); + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + wq = ioc->firmware_event_thread; + ioc->firmware_event_thread = NULL; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (wq) + destroy_workqueue(wq); _scsih_ir_shutdown(ioc); mpt2sas_base_detach(ioc); @@ -6184,7 +6304,7 @@ _scsih_remove(struct pci_dev *pdev) unsigned long flags; ioc->remove_host = 1; - _scsih_fw_event_off(ioc); + _scsih_fw_event_cleanup_queue(ioc); spin_lock_irqsave(&ioc->fw_event_lock, flags); wq = ioc->firmware_event_thread; @@ -6557,6 +6677,122 @@ _scsih_resume(struct pci_dev *pdev) } #endif /* CONFIG_PM */ +/** + * _scsih_pci_error_detected - Called when a PCI error is detected. + * @pdev: PCI device struct + * @state: PCI channel state + * + * Description: Called when a PCI error is detected. + * + * Return value: + * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t +_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n", + ioc->name, state); + + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + scsi_block_requests(ioc->shost); + mpt2sas_base_stop_watchdog(ioc); + mpt2sas_base_free_resources(ioc); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + _scsih_remove(pdev); + return PCI_ERS_RESULT_DISCONNECT; + } + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * _scsih_pci_slot_reset - Called when PCI slot has been reset. + * @pdev: PCI device struct + * + * Description: This routine is called by the pci error recovery + * code after the PCI slot has been reset, just before we + * should resume normal operations. + */ +static pci_ers_result_t +_scsih_pci_slot_reset(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int rc; + + printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n", + ioc->name); + + ioc->pdev = pdev; + rc = mpt2sas_base_map_resources(ioc); + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name, + (rc == 0) ? "success" : "failed"); + + if (!rc) + return PCI_ERS_RESULT_RECOVERED; + else + return PCI_ERS_RESULT_DISCONNECT; +} + +/** + * _scsih_pci_resume() - resume normal ops after PCI reset + * @pdev: pointer to PCI device + * + * Called when the error recovery driver tells us that its + * OK to resume normal operation. Use completion to allow + * halted scsi ops to resume. + */ +static void +_scsih_pci_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name); + + pci_cleanup_aer_uncorrect_error_status(pdev); + mpt2sas_base_start_watchdog(ioc); + scsi_unblock_requests(ioc->shost); +} + +/** + * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers + * @pdev: pointer to PCI device + */ +static pci_ers_result_t +_scsih_pci_mmio_enabled(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n", + ioc->name); + + /* TODO - dump whatever for debugging purposes */ + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +static struct pci_error_handlers _scsih_err_handler = { + .error_detected = _scsih_pci_error_detected, + .mmio_enabled = _scsih_pci_mmio_enabled, + .slot_reset = _scsih_pci_slot_reset, + .resume = _scsih_pci_resume, +}; static struct pci_driver scsih_driver = { .name = MPT2SAS_DRIVER_NAME, @@ -6564,6 +6800,7 @@ static struct pci_driver scsih_driver = { .probe = _scsih_probe, .remove = __devexit_p(_scsih_remove), .shutdown = _scsih_shutdown, + .err_handler = &_scsih_err_handler, #ifdef CONFIG_PM .suspend = _scsih_suspend, .resume = _scsih_resume, diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index bd7ca2b49f81..2727c3b65104 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -2,7 +2,7 @@ * SAS Transport Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -465,6 +465,85 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, return rc; } + +/** + * _transport_delete_duplicate_port - (see below description) + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @sas_address: sas address of device being added + * @phy_num: phy number + * + * This function is called when attempting to add a new port that is claiming + * the same phy resources already in use by another port. If we don't release + * the claimed phy resources, the sas transport layer will hang from the BUG + * in sas_port_add_phy. + * + * The reason we would hit this issue is becuase someone is changing the + * sas address of a device on the fly, meanwhile controller firmware sends + * EVENTs out of order when removing the previous instance of the device. + */ +static void +_transport_delete_duplicate_port(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_node, u64 sas_address, int phy_num) +{ + struct _sas_port *mpt2sas_port, *mpt2sas_port_duplicate; + struct _sas_phy *mpt2sas_phy; + + printk(MPT2SAS_ERR_FMT "new device located at sas_addr(0x%016llx), " + "phy_id(%d)\n", ioc->name, (unsigned long long)sas_address, + phy_num); + + mpt2sas_port_duplicate = NULL; + list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, port_list) { + dev_printk(KERN_ERR, &mpt2sas_port->port->dev, + "existing device at sas_addr(0x%016llx), num_phys(%d)\n", + (unsigned long long) + mpt2sas_port->remote_identify.sas_address, + mpt2sas_port->num_phys); + list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, + port_siblings) { + dev_printk(KERN_ERR, &mpt2sas_phy->phy->dev, + "phy_number(%d)\n", mpt2sas_phy->phy_id); + if (mpt2sas_phy->phy_id == phy_num) + mpt2sas_port_duplicate = mpt2sas_port; + } + } + + if (!mpt2sas_port_duplicate) + return; + + dev_printk(KERN_ERR, &mpt2sas_port_duplicate->port->dev, + "deleting duplicate device at sas_addr(0x%016llx), phy(%d)!!!!\n", + (unsigned long long) + mpt2sas_port_duplicate->remote_identify.sas_address, phy_num); + ioc->logging_level |= MPT_DEBUG_TRANSPORT; + mpt2sas_transport_port_remove(ioc, + mpt2sas_port_duplicate->remote_identify.sas_address, + sas_node->sas_address); + ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; +} + +/** + * _transport_sanity_check - sanity check when adding a new port + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @sas_address: sas address of device being added + * + * See the explanation above from _transport_delete_duplicate_port + */ +static void +_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node, + u64 sas_address) +{ + int i; + + for (i = 0; i < sas_node->num_phys; i++) + if (sas_node->phy[i].remote_identify.sas_address == sas_address) + if (sas_node->phy[i].phy_belongs_to_port) + _transport_delete_duplicate_port(ioc, sas_node, + sas_address, i); +} + /** * mpt2sas_transport_port_add - insert port to the list * @ioc: per adapter object @@ -522,6 +601,9 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, goto out_fail; } + _transport_sanity_check(ioc, sas_node, + mpt2sas_port->remote_identify.sas_address); + for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address != mpt2sas_port->remote_identify.sas_address) @@ -553,6 +635,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); sas_port_add_phy(port, mpt2sas_phy->phy); + mpt2sas_phy->phy_belongs_to_port = 1; } mpt2sas_port->port = port; @@ -651,6 +734,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, (unsigned long long) mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); + mpt2sas_phy->phy_belongs_to_port = 0; sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); list_del(&mpt2sas_phy->port_siblings); } @@ -1341,7 +1425,8 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); req->sense_len = sizeof(*mpi_reply); req->resid_len = 0; - rsp->resid_len -= mpi_reply->ResponseDataLength; + rsp->resid_len -= + le16_to_cpu(mpi_reply->ResponseDataLength); } else { dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - no reply\n", ioc->name, __func__)); |