X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmessage%2Ffusion%2Fmptsas.c;h=09e9a9d96410942dcfe21bb2d0484ee8f0956693;hb=refs%2Fheads%2Fvserver;hp=2512d0e6155ede3b55f1996b13fe69b6b78eda0a;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 2512d0e61..09e9a9d96 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -3,9 +3,9 @@ * For use with LSI Logic PCI chip/adapter(s) * running LSI Logic Fusion MPT (Message Passing Technology) firmware. * - * Copyright (c) 1999-2005 LSI Logic Corporation + * Copyright (c) 1999-2007 LSI Logic Corporation * (mailto:mpt_linux_developer@lsil.com) - * Copyright (c) 2005-2006 Dell + * Copyright (c) 2005-2007 Dell */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -50,11 +50,14 @@ #include #include #include +#include /* for mdelay */ +#include #include #include #include #include +#include #include "mptbase.h" #include "mptscsih.h" @@ -64,20 +67,20 @@ #define my_VERSION MPT_LINUX_VERSION_COMMON #define MYNAM "mptsas" +/* + * Reserved channel for integrated raid + */ +#define MPTSAS_RAID_CHANNEL 1 + MODULE_AUTHOR(MODULEAUTHOR); MODULE_DESCRIPTION(my_NAME); MODULE_LICENSE("GPL"); - -static int mpt_pq_filter; -module_param(mpt_pq_filter, int, 0); -MODULE_PARM_DESC(mpt_pq_filter, - "Enable peripheral qualifier filter: enable=1 " - "(default=0)"); +MODULE_VERSION(my_VERSION); static int mpt_pt_clear; module_param(mpt_pt_clear, int, 0); MODULE_PARM_DESC(mpt_pt_clear, - "Clear persistency table: enable=1 " + " Clear persistency table: enable=1 " "(default=MPTSCSIH_PT_CLEAR=0)"); static int mptsasDoneCtx = -1; @@ -91,6 +94,7 @@ enum mptsas_hotplug_action { MPTSAS_DEL_DEVICE, MPTSAS_ADD_RAID, MPTSAS_DEL_RAID, + MPTSAS_IGNORE_EVENT, }; struct mptsas_hotplug_event { @@ -104,6 +108,13 @@ struct mptsas_hotplug_event { u16 handle; u16 parent_handle; u8 phy_id; + u8 phys_disk_num; + u8 phys_disk_num_valid; +}; + +struct mptsas_discovery_event { + struct work_struct work; + MPT_ADAPTER *ioc; }; /* @@ -117,6 +128,8 @@ struct mptsas_hotplug_event { struct mptsas_devinfo { u16 handle; /* unique id to address this device */ u16 handle_parent; /* unique id to address parent device */ + u16 handle_enclosure; /* enclosure identifier of the enclosure */ + u16 slot; /* physical slot in enclosure */ u8 phy_id; /* phy number of parent device */ u8 port_id; /* sas physical port this device is assoc'd with */ @@ -127,27 +140,52 @@ struct mptsas_devinfo { u32 device_info; /* bitfield detailed info about this device */ }; +/* + * Specific details on ports, wide/narrow + */ +struct mptsas_portinfo_details{ + u16 num_phys; /* number of phys belong to this port */ + u64 phy_bitmask; /* TODO, extend support for 255 phys */ + struct sas_rphy *rphy; /* transport layer rphy object */ + struct sas_port *port; /* transport layer port object */ + struct scsi_target *starget; + struct mptsas_portinfo *port_info; +}; + struct mptsas_phyinfo { u8 phy_id; /* phy index */ - u8 port_id; /* port number this phy is part of */ + u8 port_id; /* firmware port identifier */ u8 negotiated_link_rate; /* nego'd link rate for this phy */ u8 hw_link_rate; /* hardware max/min phys link rate */ u8 programmed_link_rate; /* programmed max/min phy link rate */ + u8 sas_port_add_phy; /* flag to request sas_port_add_phy*/ struct mptsas_devinfo identify; /* point to phy device info */ struct mptsas_devinfo attached; /* point to attached device info */ - struct sas_phy *phy; - struct sas_rphy *rphy; + struct sas_phy *phy; /* transport layer phy object */ + struct mptsas_portinfo *portinfo; + struct mptsas_portinfo_details * port_details; }; struct mptsas_portinfo { struct list_head list; u16 handle; /* unique id to address this */ - u8 num_phys; /* number of phys */ + u16 num_phys; /* number of phys */ struct mptsas_phyinfo *phy_info; }; +struct mptsas_enclosure { + u64 enclosure_logical_id; /* The WWN for the enclosure */ + u16 enclosure_handle; /* unique id to address this */ + u16 flags; /* details enclosure management */ + u16 num_slot; /* num slots */ + u16 start_slot; /* first slot */ + u8 start_id; /* starting logical target id */ + u8 start_channel; /* starting logical channel id */ + u8 sep_id; /* SEP device logical target id */ + u8 sep_channel; /* SEP channel logical channel id */ +}; -#ifdef SASDEBUG +#ifdef MPT_DEBUG_SAS static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) { printk("---- IO UNIT PAGE 0 ------------\n"); @@ -205,9 +243,11 @@ static void mptsas_print_device_pg0(SasDevicePage0_t *pg0) printk("---- SAS DEVICE PAGE 0 ---------\n"); printk("Handle=0x%X\n" ,le16_to_cpu(pg0->DevHandle)); + printk("Parent Handle=0x%X\n" ,le16_to_cpu(pg0->ParentDevHandle)); printk("Enclosure Handle=0x%X\n", le16_to_cpu(pg0->EnclosureHandle)); printk("Slot=0x%X\n", le16_to_cpu(pg0->Slot)); - printk("SAS Address=0x%llX\n", le64_to_cpu(sas_address)); + printk("SAS Address=0x%llX\n", (unsigned long long) + le64_to_cpu(sas_address)); printk("Target ID=0x%X\n", pg0->TargetID); printk("Bus=0x%X\n", pg0->Bus); /* The PhyNum field specifies the PHY number of the parent @@ -243,12 +283,474 @@ static void mptsas_print_expander_pg1(SasExpanderPage1_t *pg1) #define mptsas_print_expander_pg1(pg1) do { } while (0) #endif +static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + return ((MPT_SCSI_HOST *)shost->hostdata)->ioc; +} + +static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy) +{ + struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); + return ((MPT_SCSI_HOST *)shost->hostdata)->ioc; +} + +/* + * mptsas_find_portinfo_by_handle + * + * This function should be called with the sas_topology_mutex already held + */ +static struct mptsas_portinfo * +mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) +{ + struct mptsas_portinfo *port_info, *rc=NULL; + int i; + + list_for_each_entry(port_info, &ioc->sas_topology, list) + for (i = 0; i < port_info->num_phys; i++) + if (port_info->phy_info[i].identify.handle == handle) { + rc = port_info; + goto out; + } + out: + return rc; +} /* - * This is pretty ugly. We will be able to seriously clean it up - * once the DV code in mptscsih goes away and we can properly - * implement ->target_alloc. + * Returns true if there is a scsi end device */ +static inline int +mptsas_is_end_device(struct mptsas_devinfo * attached) +{ + if ((attached->sas_address) && + (attached->device_info & + MPI_SAS_DEVICE_INFO_END_DEVICE) && + ((attached->device_info & + MPI_SAS_DEVICE_INFO_SSP_TARGET) | + (attached->device_info & + MPI_SAS_DEVICE_INFO_STP_TARGET) | + (attached->device_info & + MPI_SAS_DEVICE_INFO_SATA_DEVICE))) + return 1; + else + return 0; +} + +/* no mutex */ +static void +mptsas_port_delete(struct mptsas_portinfo_details * port_details) +{ + struct mptsas_portinfo *port_info; + struct mptsas_phyinfo *phy_info; + u8 i; + + if (!port_details) + return; + + port_info = port_details->port_info; + phy_info = port_info->phy_info; + + dsaswideprintk((KERN_DEBUG "%s: [%p]: num_phys=%02d " + "bitmask=0x%016llX\n", __FUNCTION__, port_details, + port_details->num_phys, (unsigned long long) + port_details->phy_bitmask)); + + for (i = 0; i < port_info->num_phys; i++, phy_info++) { + if(phy_info->port_details != port_details) + continue; + memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); + phy_info->port_details = NULL; + } + kfree(port_details); +} + +static inline struct sas_rphy * +mptsas_get_rphy(struct mptsas_phyinfo *phy_info) +{ + if (phy_info->port_details) + return phy_info->port_details->rphy; + else + return NULL; +} + +static inline void +mptsas_set_rphy(struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy) +{ + if (phy_info->port_details) { + phy_info->port_details->rphy = rphy; + dsaswideprintk((KERN_DEBUG "sas_rphy_add: rphy=%p\n", rphy)); + } + +#ifdef MPT_DEBUG_SAS_WIDE + if (rphy) { + dev_printk(KERN_DEBUG, &rphy->dev, "add:"); + printk("rphy=%p release=%p\n", + rphy, rphy->dev.release); + } +#endif +} + +static inline struct sas_port * +mptsas_get_port(struct mptsas_phyinfo *phy_info) +{ + if (phy_info->port_details) + return phy_info->port_details->port; + else + return NULL; +} + +static inline void +mptsas_set_port(struct mptsas_phyinfo *phy_info, struct sas_port *port) +{ + if (phy_info->port_details) + phy_info->port_details->port = port; + +#ifdef MPT_DEBUG_SAS_WIDE + if (port) { + dev_printk(KERN_DEBUG, &port->dev, "add: "); + printk("port=%p release=%p\n", + port, port->dev.release); + } +#endif +} + +static inline struct scsi_target * +mptsas_get_starget(struct mptsas_phyinfo *phy_info) +{ + if (phy_info->port_details) + return phy_info->port_details->starget; + else + return NULL; +} + +static inline void +mptsas_set_starget(struct mptsas_phyinfo *phy_info, struct scsi_target * +starget) +{ + if (phy_info->port_details) + phy_info->port_details->starget = starget; +} + + +/* + * mptsas_setup_wide_ports + * + * Updates for new and existing narrow/wide port configuration + * in the sas_topology + */ +static void +mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) +{ + struct mptsas_portinfo_details * port_details; + struct mptsas_phyinfo *phy_info, *phy_info_cmp; + u64 sas_address; + int i, j; + + mutex_lock(&ioc->sas_topology_mutex); + + phy_info = port_info->phy_info; + for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) { + if (phy_info->attached.handle) + continue; + port_details = phy_info->port_details; + if (!port_details) + continue; + if (port_details->num_phys < 2) + continue; + /* + * Removing a phy from a port, letting the last + * phy be removed by firmware events. + */ + dsaswideprintk((KERN_DEBUG + "%s: [%p]: deleting phy = %d\n", + __FUNCTION__, port_details, i)); + port_details->num_phys--; + port_details->phy_bitmask &= ~ (1 << phy_info->phy_id); + memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); + sas_port_delete_phy(port_details->port, phy_info->phy); + phy_info->port_details = NULL; + } + + /* + * Populate and refresh the tree + */ + phy_info = port_info->phy_info; + for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) { + sas_address = phy_info->attached.sas_address; + dsaswideprintk((KERN_DEBUG "phy_id=%d sas_address=0x%018llX\n", + i, (unsigned long long)sas_address)); + if (!sas_address) + continue; + port_details = phy_info->port_details; + /* + * Forming a port + */ + if (!port_details) { + port_details = kzalloc(sizeof(*port_details), + GFP_KERNEL); + if (!port_details) + goto out; + port_details->num_phys = 1; + port_details->port_info = port_info; + if (phy_info->phy_id < 64 ) + port_details->phy_bitmask |= + (1 << phy_info->phy_id); + phy_info->sas_port_add_phy=1; + dsaswideprintk((KERN_DEBUG "\t\tForming port\n\t\t" + "phy_id=%d sas_address=0x%018llX\n", + i, (unsigned long long)sas_address)); + phy_info->port_details = port_details; + } + + if (i == port_info->num_phys - 1) + continue; + phy_info_cmp = &port_info->phy_info[i + 1]; + for (j = i + 1 ; j < port_info->num_phys ; j++, + phy_info_cmp++) { + if (!phy_info_cmp->attached.sas_address) + continue; + if (sas_address != phy_info_cmp->attached.sas_address) + continue; + if (phy_info_cmp->port_details == port_details ) + continue; + dsaswideprintk((KERN_DEBUG + "\t\tphy_id=%d sas_address=0x%018llX\n", + j, (unsigned long long) + phy_info_cmp->attached.sas_address)); + if (phy_info_cmp->port_details) { + port_details->rphy = + mptsas_get_rphy(phy_info_cmp); + port_details->port = + mptsas_get_port(phy_info_cmp); + port_details->starget = + mptsas_get_starget(phy_info_cmp); + port_details->num_phys = + phy_info_cmp->port_details->num_phys; + if (!phy_info_cmp->port_details->num_phys) + kfree(phy_info_cmp->port_details); + } else + phy_info_cmp->sas_port_add_phy=1; + /* + * Adding a phy to a port + */ + phy_info_cmp->port_details = port_details; + if (phy_info_cmp->phy_id < 64 ) + port_details->phy_bitmask |= + (1 << phy_info_cmp->phy_id); + port_details->num_phys++; + } + } + + out: + +#ifdef MPT_DEBUG_SAS_WIDE + for (i = 0; i < port_info->num_phys; i++) { + port_details = port_info->phy_info[i].port_details; + if (!port_details) + continue; + dsaswideprintk((KERN_DEBUG + "%s: [%p]: phy_id=%02d num_phys=%02d " + "bitmask=0x%016llX\n", __FUNCTION__, + port_details, i, port_details->num_phys, + (unsigned long long)port_details->phy_bitmask)); + dsaswideprintk((KERN_DEBUG"\t\tport = %p rphy=%p\n", + port_details->port, port_details->rphy)); + } + dsaswideprintk((KERN_DEBUG"\n")); +#endif + mutex_unlock(&ioc->sas_topology_mutex); +} + +static void +mptsas_target_reset(MPT_ADAPTER *ioc, VirtTarget * vtarget) +{ + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + + if (mptscsih_TMHandler(hd, + MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, + vtarget->bus_id, vtarget->target_id, 0, 0, 5) < 0) { + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + printk(MYIOC_s_WARN_FMT + "Error processing TaskMgmt id=%d TARGET_RESET\n", + ioc->name, vtarget->target_id); + } +} + +static int +mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasEnclosurePage0_t *buffer; + dma_addr_t dma_handle; + int error; + __le64 le_identifier; + + memset(&hdr, 0, sizeof(hdr)); + hdr.PageVersion = MPI_SASENCLOSURE0_PAGEVERSION; + hdr.PageNumber = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_ENCLOSURE; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + /* save config data */ + memcpy(&le_identifier, &buffer->EnclosureLogicalID, sizeof(__le64)); + enclosure->enclosure_logical_id = le64_to_cpu(le_identifier); + enclosure->enclosure_handle = le16_to_cpu(buffer->EnclosureHandle); + enclosure->flags = le16_to_cpu(buffer->Flags); + enclosure->num_slot = le16_to_cpu(buffer->NumSlots); + enclosure->start_slot = le16_to_cpu(buffer->StartSlot); + enclosure->start_id = buffer->StartTargetID; + enclosure->start_channel = buffer->StartBus; + enclosure->sep_id = buffer->SEPTargetID; + enclosure->sep_channel = buffer->SEPBus; + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int +mptsas_slave_configure(struct scsi_device *sdev) +{ + + if (sdev->channel == MPTSAS_RAID_CHANNEL) + goto out; + + sas_read_port_mode_page(sdev); + + out: + return mptscsih_slave_configure(sdev); +} + +static int +mptsas_target_alloc(struct scsi_target *starget) +{ + struct Scsi_Host *host = dev_to_shost(&starget->dev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + VirtTarget *vtarget; + u32 target_id; + u32 channel; + struct sas_rphy *rphy; + struct mptsas_portinfo *p; + int i; + + vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL); + if (!vtarget) + return -ENOMEM; + + vtarget->starget = starget; + vtarget->ioc_id = hd->ioc->id; + vtarget->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY; + + target_id = starget->id; + channel = 0; + + hd->Targets[target_id] = vtarget; + + if (starget->channel == MPTSAS_RAID_CHANNEL) + goto out; + + rphy = dev_to_rphy(starget->dev.parent); + mutex_lock(&hd->ioc->sas_topology_mutex); + list_for_each_entry(p, &hd->ioc->sas_topology, list) { + for (i = 0; i < p->num_phys; i++) { + if (p->phy_info[i].attached.sas_address != + rphy->identify.sas_address) + continue; + target_id = p->phy_info[i].attached.id; + channel = p->phy_info[i].attached.channel; + mptsas_set_starget(&p->phy_info[i], starget); + + /* + * Exposing hidden raid components + */ + if (mptscsih_is_phys_disk(hd->ioc, target_id)) { + target_id = mptscsih_raid_id_to_num(hd, + target_id); + vtarget->tflags |= + MPT_TARGET_FLAGS_RAID_COMPONENT; + } + mutex_unlock(&hd->ioc->sas_topology_mutex); + goto out; + } + } + mutex_unlock(&hd->ioc->sas_topology_mutex); + + kfree(vtarget); + return -ENXIO; + + out: + vtarget->target_id = target_id; + vtarget->bus_id = channel; + starget->hostdata = vtarget; + return 0; +} + +static void +mptsas_target_destroy(struct scsi_target *starget) +{ + struct Scsi_Host *host = dev_to_shost(&starget->dev); + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + struct sas_rphy *rphy; + struct mptsas_portinfo *p; + int i; + + if (!starget->hostdata) + return; + + if (starget->channel == MPTSAS_RAID_CHANNEL) + goto out; + + rphy = dev_to_rphy(starget->dev.parent); + list_for_each_entry(p, &hd->ioc->sas_topology, list) { + for (i = 0; i < p->num_phys; i++) { + if (p->phy_info[i].attached.sas_address != + rphy->identify.sas_address) + continue; + mptsas_set_starget(&p->phy_info[i], NULL); + goto out; + } + } + + out: + kfree(starget->hostdata); + starget->hostdata = NULL; +} + + static int mptsas_slave_alloc(struct scsi_device *sdev) { @@ -256,50 +758,38 @@ mptsas_slave_alloc(struct scsi_device *sdev) MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; struct sas_rphy *rphy; struct mptsas_portinfo *p; - VirtTarget *vtarget; VirtDevice *vdev; struct scsi_target *starget; - int i; + int i; vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL); if (!vdev) { - printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", + printk(MYIOC_s_ERR_FMT "slave_alloc kzalloc(%zd) FAILED!\n", hd->ioc->name, sizeof(VirtDevice)); return -ENOMEM; } - vdev->ioc_id = hd->ioc->id; - sdev->hostdata = vdev; starget = scsi_target(sdev); - vtarget = starget->hostdata; - vdev->vtarget = vtarget; - if (vtarget->num_luns == 0) { - vtarget->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY; - hd->Targets[sdev->id] = vtarget; - } + vdev->vtarget = starget->hostdata; - /* - RAID volumes placed beyond the last expected port. - */ - if (sdev->channel == hd->ioc->num_ports) { - vdev->target_id = sdev->id; - vdev->bus_id = 0; - vdev->lun = 0; + if (sdev->channel == MPTSAS_RAID_CHANNEL) goto out; - } rphy = dev_to_rphy(sdev->sdev_target->dev.parent); mutex_lock(&hd->ioc->sas_topology_mutex); list_for_each_entry(p, &hd->ioc->sas_topology, list) { for (i = 0; i < p->num_phys; i++) { - if (p->phy_info[i].attached.sas_address == - rphy->identify.sas_address) { - vdev->target_id = - p->phy_info[i].attached.id; - vdev->bus_id = p->phy_info[i].attached.channel; - vdev->lun = sdev->lun; - mutex_unlock(&hd->ioc->sas_topology_mutex); - goto out; - } + if (p->phy_info[i].attached.sas_address != + rphy->identify.sas_address) + continue; + vdev->lun = sdev->lun; + /* + * Exposing hidden raid components + */ + if (mptscsih_is_phys_disk(hd->ioc, + p->phy_info[i].attached.id)) + sdev->no_uld_attach = 1; + mutex_unlock(&hd->ioc->sas_topology_mutex); + goto out; } } mutex_unlock(&hd->ioc->sas_topology_mutex); @@ -308,83 +798,39 @@ mptsas_slave_alloc(struct scsi_device *sdev) return -ENXIO; out: - vtarget->ioc_id = vdev->ioc_id; - vtarget->target_id = vdev->target_id; - vtarget->bus_id = vdev->bus_id; - vtarget->num_luns++; + vdev->vtarget->num_luns++; + sdev->hostdata = vdev; return 0; } -static void -mptsas_slave_destroy(struct scsi_device *sdev) +static int +mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { - struct Scsi_Host *host = sdev->host; - MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; - struct sas_rphy *rphy; - struct mptsas_portinfo *p; - int i; - VirtDevice *vdev; - - /* - * Handle hotplug removal case. - * We need to clear out attached data structure. - */ - rphy = dev_to_rphy(sdev->sdev_target->dev.parent); + VirtDevice *vdev = SCpnt->device->hostdata; - mutex_lock(&hd->ioc->sas_topology_mutex); - list_for_each_entry(p, &hd->ioc->sas_topology, list) { - for (i = 0; i < p->num_phys; i++) { - if (p->phy_info[i].attached.sas_address == - rphy->identify.sas_address) { - memset(&p->phy_info[i].attached, 0, - sizeof(struct mptsas_devinfo)); - p->phy_info[i].rphy = NULL; - goto out; - } - } +// scsi_print_command(SCpnt); + if (vdev->vtarget->deleted) { + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return 0; } - out: - mutex_unlock(&hd->ioc->sas_topology_mutex); - /* - * Issue target reset to flush firmware outstanding commands. - */ - vdev = sdev->hostdata; - if (vdev->configured_lun){ - if (mptscsih_TMHandler(hd, - MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, - vdev->bus_id, - vdev->target_id, - 0, 0, 5 /* 5 second timeout */) - < 0){ - - /* The TM request failed! - * Fatal error case. - */ - printk(MYIOC_s_WARN_FMT - "Error processing TaskMgmt id=%d TARGET_RESET\n", - hd->ioc->name, - vdev->target_id); - - hd->tmPending = 0; - hd->tmState = TM_STATE_NONE; - } - } - mptscsih_slave_destroy(sdev); + return mptscsih_qcmd(SCpnt,done); } + static struct scsi_host_template mptsas_driver_template = { .module = THIS_MODULE, .proc_name = "mptsas", .proc_info = mptscsih_proc_info, .name = "MPT SPI Host", .info = mptscsih_info, - .queuecommand = mptscsih_qcmd, - .target_alloc = mptscsih_target_alloc, + .queuecommand = mptsas_qcmd, + .target_alloc = mptsas_target_alloc, .slave_alloc = mptsas_slave_alloc, - .slave_configure = mptscsih_slave_configure, - .target_destroy = mptscsih_target_destroy, - .slave_destroy = mptsas_slave_destroy, + .slave_configure = mptsas_slave_configure, + .target_destroy = mptsas_target_destroy, + .slave_destroy = mptscsih_slave_destroy, .change_queue_depth = mptscsih_change_queue_depth, .eh_abort_handler = mptscsih_abort, .eh_device_reset_handler = mptscsih_dev_reset, @@ -399,12 +845,6 @@ static struct scsi_host_template mptsas_driver_template = { .use_clustering = ENABLE_CLUSTERING, }; -static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy) -{ - struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); - return ((MPT_SCSI_HOST *)shost->hostdata)->ioc; -} - static int mptsas_get_linkerrors(struct sas_phy *phy) { MPT_ADAPTER *ioc = phy_to_ioc(phy); @@ -414,6 +854,10 @@ static int mptsas_get_linkerrors(struct sas_phy *phy) dma_addr_t dma_handle; int error; + /* FIXME: only have link errors on local phys */ + if (!scsi_is_sas_phy_local(phy)) + return -EINVAL; + hdr.PageVersion = MPI_SASPHY1_PAGEVERSION; hdr.ExtPageLength = 0; hdr.PageNumber = 1 /* page number 1*/; @@ -486,6 +930,10 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset) unsigned long timeleft; int error = -ERESTARTSYS; + /* FIXME: fusion doesn't allow non-local phy reset */ + if (!scsi_is_sas_phy_local(phy)) + return -EINVAL; + /* not implemented for expanders */ if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP) return -ENXIO; @@ -546,8 +994,67 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset) return error; } +static int +mptsas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) +{ + MPT_ADAPTER *ioc = rphy_to_ioc(rphy); + int i, error; + struct mptsas_portinfo *p; + struct mptsas_enclosure enclosure_info; + u64 enclosure_handle; + + mutex_lock(&ioc->sas_topology_mutex); + list_for_each_entry(p, &ioc->sas_topology, list) { + for (i = 0; i < p->num_phys; i++) { + if (p->phy_info[i].attached.sas_address == + rphy->identify.sas_address) { + enclosure_handle = p->phy_info[i]. + attached.handle_enclosure; + goto found_info; + } + } + } + mutex_unlock(&ioc->sas_topology_mutex); + return -ENXIO; + + found_info: + mutex_unlock(&ioc->sas_topology_mutex); + memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure)); + error = mptsas_sas_enclosure_pg0(ioc, &enclosure_info, + (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE << + MPI_SAS_ENCLOS_PGAD_FORM_SHIFT), enclosure_handle); + if (!error) + *identifier = enclosure_info.enclosure_logical_id; + return error; +} + +static int +mptsas_get_bay_identifier(struct sas_rphy *rphy) +{ + MPT_ADAPTER *ioc = rphy_to_ioc(rphy); + struct mptsas_portinfo *p; + int i, rc; + + mutex_lock(&ioc->sas_topology_mutex); + list_for_each_entry(p, &ioc->sas_topology, list) { + for (i = 0; i < p->num_phys; i++) { + if (p->phy_info[i].attached.sas_address == + rphy->identify.sas_address) { + rc = p->phy_info[i].attached.slot; + goto out; + } + } + } + rc = -ENXIO; + out: + mutex_unlock(&ioc->sas_topology_mutex); + return rc; +} + static struct sas_function_template mptsas_transport_functions = { .get_linkerrors = mptsas_get_linkerrors, + .get_enclosure_identifier = mptsas_get_enclosure_identifier, + .get_bay_identifier = mptsas_get_bay_identifier, .phy_reset = mptsas_phy_reset, }; @@ -601,12 +1108,15 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) port_info->num_phys = buffer->NumPhys; port_info->phy_info = kcalloc(port_info->num_phys, - sizeof(struct mptsas_phyinfo),GFP_KERNEL); + sizeof(*port_info->phy_info),GFP_KERNEL); if (!port_info->phy_info) { error = -ENOMEM; goto out_free_consistent; } + if (port_info->num_phys) + port_info->handle = + le16_to_cpu(buffer->PhyData[0].ControllerDevHandle); for (i = 0; i < port_info->num_phys; i++) { mptsas_print_phy_data(&buffer->PhyData[i]); port_info->phy_info[i].phy_id = i; @@ -614,6 +1124,7 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) buffer->PhyData[i].Port; port_info->phy_info[i].negotiated_link_rate = buffer->PhyData[i].NegotiatedLinkRate; + port_info->phy_info[i].portinfo = port_info; } out_free_consistent: @@ -696,7 +1207,11 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, SasDevicePage0_t *buffer; dma_addr_t dma_handle; __le64 sas_address; - int error; + int error=0; + + if (ioc->sas_discovery_runtime && + mptsas_is_end_device(device_info)) + goto out; hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; hdr.ExtPageLength = 0; @@ -713,6 +1228,7 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, cfg.dir = 0; /* read */ cfg.timeout = 10; + memset(device_info, 0, sizeof(struct mptsas_devinfo)); error = mpt_config(ioc, &cfg); if (error) goto out; @@ -739,6 +1255,9 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, device_info->handle = le16_to_cpu(buffer->DevHandle); device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle); + device_info->handle_enclosure = + le16_to_cpu(buffer->EnclosureHandle); + device_info->slot = le16_to_cpu(buffer->Slot); device_info->phy_id = buffer->PhyNum; device_info->port_id = buffer->PhysicalPort; device_info->id = buffer->TargetID; @@ -763,7 +1282,7 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, CONFIGPARMS cfg; SasExpanderPage0_t *buffer; dma_addr_t dma_handle; - int error; + int i, error; hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; hdr.ExtPageLength = 0; @@ -780,6 +1299,7 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, cfg.dir = 0; /* read */ cfg.timeout = 10; + memset(port_info, 0, sizeof(struct mptsas_portinfo)); error = mpt_config(ioc, &cfg); if (error) goto out; @@ -807,12 +1327,15 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, port_info->num_phys = buffer->NumPhys; port_info->handle = le16_to_cpu(buffer->DevHandle); port_info->phy_info = kcalloc(port_info->num_phys, - sizeof(struct mptsas_phyinfo),GFP_KERNEL); + sizeof(*port_info->phy_info),GFP_KERNEL); if (!port_info->phy_info) { error = -ENOMEM; goto out_free_consistent; } + for (i = 0; i < port_info->num_phys; i++) + port_info->phy_info[i].portinfo = port_info; + out_free_consistent: pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, buffer, dma_handle); @@ -828,7 +1351,11 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, CONFIGPARMS cfg; SasExpanderPage1_t *buffer; dma_addr_t dma_handle; - int error; + int error=0; + + if (ioc->sas_discovery_runtime && + mptsas_is_end_device(&phy_info->attached)) + goto out; hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; hdr.ExtPageLength = 0; @@ -880,7 +1407,6 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle); phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle); - out_free_consistent: pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, buffer, dma_handle); @@ -888,26 +1414,6 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, return error; } -/* - * Returns true if there is a scsi end device - */ -static inline int -mptsas_is_end_device(struct mptsas_devinfo * attached) -{ - if ((attached->handle) && - (attached->device_info & - MPI_SAS_DEVICE_INFO_END_DEVICE) && - ((attached->device_info & - MPI_SAS_DEVICE_INFO_SSP_TARGET) | - (attached->device_info & - MPI_SAS_DEVICE_INFO_STP_TARGET) | - (attached->device_info & - MPI_SAS_DEVICE_INFO_SATA_DEVICE))) - return 1; - else - return 0; -} - static void mptsas_parse_device_info(struct sas_identify *identify, struct mptsas_devinfo *device_info) @@ -970,14 +1476,25 @@ mptsas_parse_device_info(struct sas_identify *identify, static int mptsas_probe_one_phy(struct device *dev, struct mptsas_phyinfo *phy_info, int index, int local) { + MPT_ADAPTER *ioc; struct sas_phy *phy; - int error; + struct sas_port *port; + int error = 0; - phy = sas_phy_alloc(dev, index); - if (!phy) - return -ENOMEM; + if (!dev) { + error = -ENODEV; + goto out; + } + + if (!phy_info->phy) { + phy = sas_phy_alloc(dev, index); + if (!phy) { + error = -ENOMEM; + goto out; + } + } else + phy = phy_info->phy; - phy->port_identifier = phy_info->port_id; mptsas_parse_device_info(&phy->identify, &phy_info->identify); /* @@ -1061,54 +1578,156 @@ static int mptsas_probe_one_phy(struct device *dev, break; } - if (local) - phy->local_attached = 1; + if (!phy_info->phy) { + + error = sas_phy_add(phy); + if (error) { + sas_phy_free(phy); + goto out; + } + phy_info->phy = phy; + } + + if (!phy_info->attached.handle || + !phy_info->port_details) + goto out; + + port = mptsas_get_port(phy_info); + ioc = phy_to_ioc(phy_info->phy); - error = sas_phy_add(phy); - if (error) { - sas_phy_free(phy); - return error; + if (phy_info->sas_port_add_phy) { + + if (!port) { + port = sas_port_alloc_num(dev); + if (!port) { + error = -ENOMEM; + goto out; + } + error = sas_port_add(port); + if (error) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + goto out; + } + mptsas_set_port(phy_info, port); + dsaswideprintk((KERN_DEBUG + "sas_port_alloc: port=%p dev=%p port_id=%d\n", + port, dev, port->port_identifier)); + } + dsaswideprintk((KERN_DEBUG "sas_port_add_phy: phy_id=%d\n", + phy_info->phy_id)); + sas_port_add_phy(port, phy_info->phy); + phy_info->sas_port_add_phy = 0; } - phy_info->phy = phy; - if (phy_info->attached.handle) { + if (!mptsas_get_rphy(phy_info) && port && !port->rphy) { + struct sas_rphy *rphy; + struct device *parent; + struct sas_identify identify; + + parent = dev->parent->parent; + /* + * Let the hotplug_work thread handle processing + * the adding/removing of devices that occur + * after start of day. + */ + if (ioc->sas_discovery_runtime && + mptsas_is_end_device(&phy_info->attached)) + goto out; + + mptsas_parse_device_info(&identify, &phy_info->attached); + if (scsi_is_host_device(parent)) { + struct mptsas_portinfo *port_info; + int i; + + mutex_lock(&ioc->sas_topology_mutex); + port_info = mptsas_find_portinfo_by_handle(ioc, + ioc->handle); + mutex_unlock(&ioc->sas_topology_mutex); + + for (i = 0; i < port_info->num_phys; i++) + if (port_info->phy_info[i].identify.sas_address == + identify.sas_address) { + sas_port_mark_backlink(port); + goto out; + } + + } else if (scsi_is_sas_rphy(parent)) { + struct sas_rphy *parent_rphy = dev_to_rphy(parent); + if (identify.sas_address == + parent_rphy->identify.sas_address) { + sas_port_mark_backlink(port); + goto out; + } + } - rphy = sas_rphy_alloc(phy); - if (!rphy) - return 0; /* non-fatal: an rphy can be added later */ + switch (identify.device_type) { + case SAS_END_DEVICE: + rphy = sas_end_device_alloc(port); + break; + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: + rphy = sas_expander_alloc(port, identify.device_type); + break; + default: + rphy = NULL; + break; + } + if (!rphy) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + goto out; + } - mptsas_parse_device_info(&rphy->identify, &phy_info->attached); + rphy->identify = identify; error = sas_rphy_add(rphy); if (error) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); sas_rphy_free(rphy); - return error; + goto out; } - - phy_info->rphy = rphy; + mptsas_set_rphy(phy_info, rphy); } - return 0; + out: + return error; } static int -mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) +mptsas_probe_hba_phys(MPT_ADAPTER *ioc) { - struct mptsas_portinfo *port_info; + struct mptsas_portinfo *port_info, *hba; u32 handle = 0xFFFF; int error = -ENOMEM, i; - port_info = kzalloc(sizeof(*port_info), GFP_KERNEL); - if (!port_info) + hba = kzalloc(sizeof(*port_info), GFP_KERNEL); + if (! hba) goto out; - error = mptsas_sas_io_unit_pg0(ioc, port_info); + error = mptsas_sas_io_unit_pg0(ioc, hba); if (error) goto out_free_port_info; - ioc->num_ports = port_info->num_phys; mutex_lock(&ioc->sas_topology_mutex); - list_add_tail(&port_info->list, &ioc->sas_topology); + ioc->handle = hba->handle; + port_info = mptsas_find_portinfo_by_handle(ioc, hba->handle); + if (!port_info) { + port_info = hba; + list_add_tail(&port_info->list, &ioc->sas_topology); + } else { + port_info->handle = hba->handle; + for (i = 0; i < hba->num_phys; i++) + port_info->phy_info[i].negotiated_link_rate = + hba->phy_info[i].negotiated_link_rate; + kfree(hba->phy_info); + kfree(hba); + hba = NULL; + } mutex_unlock(&ioc->sas_topology_mutex); for (i = 0; i < port_info->num_phys; i++) { @@ -1123,52 +1742,62 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) port_info->phy_info[i].phy_id; handle = port_info->phy_info[i].identify.handle; - if (port_info->phy_info[i].attached.handle) { + if (port_info->phy_info[i].attached.handle) mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].attached, (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << MPI_SAS_DEVICE_PGAD_FORM_SHIFT), port_info->phy_info[i].attached.handle); - } + } + mptsas_setup_wide_ports(ioc, port_info); + + for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++) mptsas_probe_one_phy(&ioc->sh->shost_gendev, - &port_info->phy_info[i], *index, 1); - (*index)++; - } + &port_info->phy_info[i], ioc->sas_index, 1); return 0; out_free_port_info: - kfree(port_info); + kfree(hba); out: return error; } static int -mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) +mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle) { - struct mptsas_portinfo *port_info, *p; + struct mptsas_portinfo *port_info, *p, *ex; + struct device *parent; + struct sas_rphy *rphy; int error = -ENOMEM, i, j; - port_info = kzalloc(sizeof(*port_info), GFP_KERNEL); - if (!port_info) + ex = kzalloc(sizeof(*port_info), GFP_KERNEL); + if (!ex) goto out; - error = mptsas_sas_expander_pg0(ioc, port_info, + error = mptsas_sas_expander_pg0(ioc, ex, (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE << MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle); if (error) goto out_free_port_info; - *handle = port_info->handle; + *handle = ex->handle; mutex_lock(&ioc->sas_topology_mutex); - list_add_tail(&port_info->list, &ioc->sas_topology); + port_info = mptsas_find_portinfo_by_handle(ioc, *handle); + if (!port_info) { + port_info = ex; + list_add_tail(&port_info->list, &ioc->sas_topology); + } else { + port_info->handle = ex->handle; + kfree(ex->phy_info); + kfree(ex); + ex = NULL; + } mutex_unlock(&ioc->sas_topology_mutex); for (i = 0; i < port_info->num_phys; i++) { - struct device *parent; - mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i], (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM << MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + *handle); @@ -1189,85 +1818,229 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << MPI_SAS_DEVICE_PGAD_FORM_SHIFT), port_info->phy_info[i].attached.handle); + port_info->phy_info[i].attached.phy_id = + port_info->phy_info[i].phy_id; } + } - /* - * If we find a parent port handle this expander is - * attached to another expander, else it hangs of the - * HBA phys. - */ - parent = &ioc->sh->shost_gendev; + parent = &ioc->sh->shost_gendev; + for (i = 0; i < port_info->num_phys; i++) { mutex_lock(&ioc->sas_topology_mutex); list_for_each_entry(p, &ioc->sas_topology, list) { for (j = 0; j < p->num_phys; j++) { - if (port_info->phy_info[i].identify.handle == + if (port_info->phy_info[i].identify.handle != p->phy_info[j].attached.handle) - parent = &p->phy_info[j].rphy->dev; + continue; + rphy = mptsas_get_rphy(&p->phy_info[j]); + parent = &rphy->dev; } } mutex_unlock(&ioc->sas_topology_mutex); + } + + mptsas_setup_wide_ports(ioc, port_info); + for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++) mptsas_probe_one_phy(parent, &port_info->phy_info[i], - *index, 0); - (*index)++; - } + ioc->sas_index, 0); return 0; out_free_port_info: - kfree(port_info); + if (ex) { + kfree(ex->phy_info); + kfree(ex); + } out: return error; } +/* + * mptsas_delete_expander_phys + * + * + * This will traverse topology, and remove expanders + * that are no longer present + */ +static void +mptsas_delete_expander_phys(MPT_ADAPTER *ioc) +{ + struct mptsas_portinfo buffer; + struct mptsas_portinfo *port_info, *n, *parent; + struct mptsas_phyinfo *phy_info; + struct scsi_target * starget; + VirtTarget * vtarget; + struct sas_port * port; + int i; + u64 expander_sas_address; + + mutex_lock(&ioc->sas_topology_mutex); + list_for_each_entry_safe(port_info, n, &ioc->sas_topology, list) { + + if (port_info->phy_info && + (!(port_info->phy_info[0].identify.device_info & + MPI_SAS_DEVICE_INFO_SMP_TARGET))) + continue; + + if (mptsas_sas_expander_pg0(ioc, &buffer, + (MPI_SAS_EXPAND_PGAD_FORM_HANDLE << + MPI_SAS_EXPAND_PGAD_FORM_SHIFT), port_info->handle)) { + + /* + * Issue target reset to all child end devices + * then mark them deleted to prevent further + * IO going to them. + */ + phy_info = port_info->phy_info; + for (i = 0; i < port_info->num_phys; i++, phy_info++) { + starget = mptsas_get_starget(phy_info); + if (!starget) + continue; + vtarget = starget->hostdata; + if(vtarget->deleted) + continue; + vtarget->deleted = 1; + mptsas_target_reset(ioc, vtarget); + sas_port_delete(mptsas_get_port(phy_info)); + mptsas_port_delete(phy_info->port_details); + } + + /* + * Obtain the port_info instance to the parent port + */ + parent = mptsas_find_portinfo_by_handle(ioc, + port_info->phy_info[0].identify.handle_parent); + + if (!parent) + goto next_port; + + expander_sas_address = + port_info->phy_info[0].identify.sas_address; + + /* + * Delete rphys in the parent that point + * to this expander. The transport layer will + * cleanup all the children. + */ + phy_info = parent->phy_info; + for (i = 0; i < parent->num_phys; i++, phy_info++) { + port = mptsas_get_port(phy_info); + if (!port) + continue; + if (phy_info->attached.sas_address != + expander_sas_address) + continue; +#ifdef MPT_DEBUG_SAS_WIDE + dev_printk(KERN_DEBUG, &port->dev, + "delete port (%d)\n", port->port_identifier); +#endif + sas_port_delete(port); + mptsas_port_delete(phy_info->port_details); + } + next_port: + + phy_info = port_info->phy_info; + for (i = 0; i < port_info->num_phys; i++, phy_info++) + mptsas_port_delete(phy_info->port_details); + + list_del(&port_info->list); + kfree(port_info->phy_info); + kfree(port_info); + } + /* + * Free this memory allocated from inside + * mptsas_sas_expander_pg0 + */ + kfree(buffer.phy_info); + } + mutex_unlock(&ioc->sas_topology_mutex); +} + +/* + * Start of day discovery + */ static void mptsas_scan_sas_topology(MPT_ADAPTER *ioc) { u32 handle = 0xFFFF; - int index = 0; + int i; + + mutex_lock(&ioc->sas_discovery_mutex); + mptsas_probe_hba_phys(ioc); + while (!mptsas_probe_expander_phys(ioc, &handle)) + ; + /* + Reporting RAID volumes. + */ + if (!ioc->raid_data.pIocPg2) + goto out; + if (!ioc->raid_data.pIocPg2->NumActiveVolumes) + goto out; + for (i=0; iraid_data.pIocPg2->NumActiveVolumes; i++) { + scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL, + ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0); + } + out: + mutex_unlock(&ioc->sas_discovery_mutex); +} + +/* + * Work queue thread to handle Runtime discovery + * Mere purpose is the hot add/delete of expanders + *(Mutex UNLOCKED) + */ +static void +__mptsas_discovery_work(MPT_ADAPTER *ioc) +{ + u32 handle = 0xFFFF; - mptsas_probe_hba_phys(ioc, &index); - while (!mptsas_probe_expander_phys(ioc, &handle, &index)) + ioc->sas_discovery_runtime=1; + mptsas_delete_expander_phys(ioc); + mptsas_probe_hba_phys(ioc); + while (!mptsas_probe_expander_phys(ioc, &handle)) ; + ioc->sas_discovery_runtime=0; +} + +/* + * Work queue thread to handle Runtime discovery + * Mere purpose is the hot add/delete of expanders + *(Mutex LOCKED) + */ +static void +mptsas_discovery_work(struct work_struct *work) +{ + struct mptsas_discovery_event *ev = + container_of(work, struct mptsas_discovery_event, work); + MPT_ADAPTER *ioc = ev->ioc; + + mutex_lock(&ioc->sas_discovery_mutex); + __mptsas_discovery_work(ioc); + mutex_unlock(&ioc->sas_discovery_mutex); + kfree(ev); } static struct mptsas_phyinfo * -mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id) +mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address) { struct mptsas_portinfo *port_info; - struct mptsas_devinfo device_info; struct mptsas_phyinfo *phy_info = NULL; - int i, error; - - /* - * Retrieve the parent sas_address - */ - error = mptsas_sas_device_pg0(ioc, &device_info, - (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << - MPI_SAS_DEVICE_PGAD_FORM_SHIFT), - parent_handle); - if (error) { - printk("mptsas: failed to retrieve device page\n"); - return NULL; - } + int i; - /* - * The phy_info structures are never deallocated during lifetime of - * a host, so the code below is safe without additional refcounting. - */ mutex_lock(&ioc->sas_topology_mutex); list_for_each_entry(port_info, &ioc->sas_topology, list) { for (i = 0; i < port_info->num_phys; i++) { - if (port_info->phy_info[i].identify.sas_address == - device_info.sas_address && - port_info->phy_info[i].phy_id == phy_id) { - phy_info = &port_info->phy_info[i]; - break; - } + if (port_info->phy_info[i].attached.sas_address + != sas_address) + continue; + if (!mptsas_is_end_device( + &port_info->phy_info[i].attached)) + continue; + phy_info = &port_info->phy_info[i]; + break; } } mutex_unlock(&ioc->sas_topology_mutex); - return phy_info; } @@ -1278,43 +2051,124 @@ mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u32 id) struct mptsas_phyinfo *phy_info = NULL; int i; - /* - * The phy_info structures are never deallocated during lifetime of - * a host, so the code below is safe without additional refcounting. - */ mutex_lock(&ioc->sas_topology_mutex); list_for_each_entry(port_info, &ioc->sas_topology, list) { - for (i = 0; i < port_info->num_phys; i++) - if (mptsas_is_end_device(&port_info->phy_info[i].attached)) - if (port_info->phy_info[i].attached.id == id) { - phy_info = &port_info->phy_info[i]; - break; - } + for (i = 0; i < port_info->num_phys; i++) { + if (port_info->phy_info[i].attached.id != id) + continue; + if (!mptsas_is_end_device( + &port_info->phy_info[i].attached)) + continue; + phy_info = &port_info->phy_info[i]; + break; + } } mutex_unlock(&ioc->sas_topology_mutex); - return phy_info; } +/* + * Work queue thread to clear the persitency table + */ +static void +mptsas_persist_clear_table(struct work_struct *work) +{ + MPT_ADAPTER *ioc = container_of(work, MPT_ADAPTER, sas_persist_task); + + mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT); +} + +static void +mptsas_reprobe_lun(struct scsi_device *sdev, void *data) +{ + int rc; + + sdev->no_uld_attach = data ? 1 : 0; + rc = scsi_device_reprobe(sdev); +} + +static void +mptsas_reprobe_target(struct scsi_target *starget, int uld_attach) +{ + starget_for_each_device(starget, uld_attach ? (void *)1 : NULL, + mptsas_reprobe_lun); +} + +/* + * Work queue thread to handle SAS hotplug events + */ static void -mptsas_hotplug_work(void *arg) +mptsas_hotplug_work(struct work_struct *work) { - struct mptsas_hotplug_event *ev = arg; + struct mptsas_hotplug_event *ev = + container_of(work, struct mptsas_hotplug_event, work); MPT_ADAPTER *ioc = ev->ioc; struct mptsas_phyinfo *phy_info; struct sas_rphy *rphy; + struct sas_port *port; struct scsi_device *sdev; + struct scsi_target * starget; + struct sas_identify identify; char *ds = NULL; struct mptsas_devinfo sas_device; + VirtTarget *vtarget; + VirtDevice *vdevice; + + mutex_lock(&ioc->sas_discovery_mutex); switch (ev->event_type) { case MPTSAS_DEL_DEVICE: phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id); - if (!phy_info) { - printk("mptsas: remove event for non-existant PHY.\n"); + + /* + * Sanity checks, for non-existing phys and remote rphys. + */ + if (!phy_info || !phy_info->port_details) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + rphy = mptsas_get_rphy(phy_info); + if (!rphy) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); break; } + port = mptsas_get_port(phy_info); + if (!port) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + + starget = mptsas_get_starget(phy_info); + if (starget) { + vtarget = starget->hostdata; + + if (!vtarget) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + + /* + * Handling RAID components + */ + if (ev->phys_disk_num_valid) { + vtarget->target_id = ev->phys_disk_num; + vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT; + mptsas_reprobe_target(starget, 1); + break; + } + + vtarget->deleted = 1; + mptsas_target_reset(ioc, vtarget); + } if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) ds = "ssp"; @@ -1327,55 +2181,80 @@ mptsas_hotplug_work(void *arg) "removing %s device, channel %d, id %d, phy %d\n", ioc->name, ds, ev->channel, ev->id, phy_info->phy_id); - if (phy_info->rphy) { - sas_rphy_delete(phy_info->rphy); - phy_info->rphy = NULL; - } +#ifdef MPT_DEBUG_SAS_WIDE + dev_printk(KERN_DEBUG, &port->dev, + "delete port (%d)\n", port->port_identifier); +#endif + sas_port_delete(port); + mptsas_port_delete(phy_info->port_details); break; case MPTSAS_ADD_DEVICE: + if (ev->phys_disk_num_valid) + mpt_findImVolumes(ioc); + /* - * When there is no sas address, - * RAID volumes are being deleted, - * and hidden phy disk are being added. - * We don't know the SAS data yet, - * so lookup sas device page to get - * pertaining info + * Refresh sas device pg0 data */ - if (!ev->sas_address) { - if (mptsas_sas_device_pg0(ioc, - &sas_device, ev->id, - (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID << - MPI_SAS_DEVICE_PGAD_FORM_SHIFT))) - break; - ev->handle = sas_device.handle; - ev->parent_handle = sas_device.handle_parent; - ev->channel = sas_device.channel; - ev->phy_id = sas_device.phy_id; - ev->sas_address = sas_device.sas_address; - ev->device_info = sas_device.device_info; + if (mptsas_sas_device_pg0(ioc, &sas_device, + (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), ev->id)) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + + ssleep(2); + __mptsas_discovery_work(ioc); + + phy_info = mptsas_find_phyinfo_by_sas_address(ioc, + sas_device.sas_address); + + if (!phy_info || !phy_info->port_details) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; } - phy_info = mptsas_find_phyinfo_by_parent(ioc, - ev->parent_handle, ev->phy_id); - if (!phy_info) { - printk("mptsas: add event for non-existant PHY.\n"); + starget = mptsas_get_starget(phy_info); + if (starget) { + vtarget = starget->hostdata; + + if (!vtarget) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + /* + * Handling RAID components + */ + if (vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) { + vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT; + vtarget->target_id = ev->id; + mptsas_reprobe_target(starget, 0); + } break; } - if (phy_info->rphy) { - printk("mptsas: trying to add existing device.\n"); + if (mptsas_get_rphy(phy_info)) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); + break; + } + port = mptsas_get_port(phy_info); + if (!port) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); break; } - /* fill attached info */ - phy_info->attached.handle = ev->handle; - phy_info->attached.phy_id = ev->phy_id; - phy_info->attached.port_id = phy_info->identify.port_id; - phy_info->attached.id = ev->id; - phy_info->attached.channel = ev->channel; - phy_info->attached.sas_address = ev->sas_address; - phy_info->attached.device_info = ev->device_info; + memcpy(&phy_info->attached, &sas_device, + sizeof(struct mptsas_devinfo)); if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET) ds = "ssp"; @@ -1388,61 +2267,65 @@ mptsas_hotplug_work(void *arg) "attaching %s device, channel %d, id %d, phy %d\n", ioc->name, ds, ev->channel, ev->id, ev->phy_id); - - rphy = sas_rphy_alloc(phy_info->phy); - if (!rphy) + mptsas_parse_device_info(&identify, &phy_info->attached); + rphy = sas_end_device_alloc(port); + if (!rphy) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); break; /* non-fatal: an rphy can be added later */ + } - rphy->scsi_target_id = phy_info->attached.id; - mptsas_parse_device_info(&rphy->identify, &phy_info->attached); + rphy->identify = identify; if (sas_rphy_add(rphy)) { + dfailprintk((MYIOC_s_ERR_FMT + "%s: exit at line=%d\n", ioc->name, + __FUNCTION__, __LINE__)); sas_rphy_free(rphy); break; } - - phy_info->rphy = rphy; + mptsas_set_rphy(phy_info, rphy); break; case MPTSAS_ADD_RAID: - sdev = scsi_device_lookup( - ioc->sh, - ioc->num_ports, - ev->id, - 0); + sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, + ev->id, 0); if (sdev) { scsi_device_put(sdev); break; } printk(MYIOC_s_INFO_FMT - "attaching device, channel %d, id %d\n", - ioc->name, ioc->num_ports, ev->id); - scsi_add_device(ioc->sh, - ioc->num_ports, - ev->id, - 0); + "attaching raid volume, channel %d, id %d\n", + ioc->name, MPTSAS_RAID_CHANNEL, ev->id); + scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL, ev->id, 0); mpt_findImVolumes(ioc); break; case MPTSAS_DEL_RAID: - sdev = scsi_device_lookup( - ioc->sh, - ioc->num_ports, - ev->id, - 0); + sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, + ev->id, 0); if (!sdev) break; printk(MYIOC_s_INFO_FMT - "removing device, channel %d, id %d\n", - ioc->name, ioc->num_ports, ev->id); + "removing raid volume, channel %d, id %d\n", + ioc->name, MPTSAS_RAID_CHANNEL, ev->id); + vdevice = sdev->hostdata; + vdevice->vtarget->deleted = 1; + mptsas_target_reset(ioc, vdevice->vtarget); scsi_remove_device(sdev); scsi_device_put(sdev); mpt_findImVolumes(ioc); break; + case MPTSAS_IGNORE_EVENT: + default: + break; } + mutex_unlock(&ioc->sas_discovery_mutex); kfree(ev); + } static void -mptscsih_send_sas_event(MPT_ADAPTER *ioc, +mptsas_send_sas_event(MPT_ADAPTER *ioc, EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data) { struct mptsas_hotplug_event *ev; @@ -1455,65 +2338,103 @@ mptscsih_send_sas_event(MPT_ADAPTER *ioc, MPI_SAS_DEVICE_INFO_SATA_DEVICE )) == 0) return; - if ((sas_event_data->ReasonCode & - (MPI_EVENT_SAS_DEV_STAT_RC_ADDED | - MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING)) == 0) - return; + switch (sas_event_data->ReasonCode) { + case MPI_EVENT_SAS_DEV_STAT_RC_ADDED: + case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING: + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) { + printk(KERN_WARNING "mptsas: lost hotplug event\n"); + break; + } - ev = kmalloc(sizeof(*ev), GFP_ATOMIC); - if (!ev) { - printk(KERN_WARNING "mptsas: lost hotplug event\n"); - return; + INIT_WORK(&ev->work, mptsas_hotplug_work); + ev->ioc = ioc; + ev->handle = le16_to_cpu(sas_event_data->DevHandle); + ev->parent_handle = + le16_to_cpu(sas_event_data->ParentDevHandle); + ev->channel = sas_event_data->Bus; + ev->id = sas_event_data->TargetID; + ev->phy_id = sas_event_data->PhyNum; + memcpy(&sas_address, &sas_event_data->SASAddress, + sizeof(__le64)); + ev->sas_address = le64_to_cpu(sas_address); + ev->device_info = device_info; + + if (sas_event_data->ReasonCode & + MPI_EVENT_SAS_DEV_STAT_RC_ADDED) + ev->event_type = MPTSAS_ADD_DEVICE; + else + ev->event_type = MPTSAS_DEL_DEVICE; + schedule_work(&ev->work); + break; + case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED: + /* + * Persistent table is full. + */ + INIT_WORK(&ioc->sas_persist_task, + mptsas_persist_clear_table); + schedule_work(&ioc->sas_persist_task); + break; + case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA: + /* TODO */ + case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET: + /* TODO */ + default: + break; } - - - INIT_WORK(&ev->work, mptsas_hotplug_work, ev); - ev->ioc = ioc; - ev->handle = le16_to_cpu(sas_event_data->DevHandle); - ev->parent_handle = le16_to_cpu(sas_event_data->ParentDevHandle); - ev->channel = sas_event_data->Bus; - ev->id = sas_event_data->TargetID; - ev->phy_id = sas_event_data->PhyNum; - memcpy(&sas_address, &sas_event_data->SASAddress, sizeof(__le64)); - ev->sas_address = le64_to_cpu(sas_address); - ev->device_info = device_info; - - if (sas_event_data->ReasonCode & MPI_EVENT_SAS_DEV_STAT_RC_ADDED) - ev->event_type = MPTSAS_ADD_DEVICE; - else - ev->event_type = MPTSAS_DEL_DEVICE; - - schedule_work(&ev->work); } static void -mptscsih_send_raid_event(MPT_ADAPTER *ioc, +mptsas_send_raid_event(MPT_ADAPTER *ioc, EVENT_DATA_RAID *raid_event_data) { struct mptsas_hotplug_event *ev; - RAID_VOL0_STATUS * volumeStatus; + int status = le32_to_cpu(raid_event_data->SettingsStatus); + int state = (status >> 8) & 0xff; if (ioc->bus_type != SAS) return; - ev = kmalloc(sizeof(*ev), GFP_ATOMIC); + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (!ev) { printk(KERN_WARNING "mptsas: lost hotplug event\n"); return; } - memset(ev,0,sizeof(struct mptsas_hotplug_event)); - INIT_WORK(&ev->work, mptsas_hotplug_work, ev); + INIT_WORK(&ev->work, mptsas_hotplug_work); ev->ioc = ioc; ev->id = raid_event_data->VolumeID; + ev->event_type = MPTSAS_IGNORE_EVENT; switch (raid_event_data->ReasonCode) { case MPI_EVENT_RAID_RC_PHYSDISK_DELETED: ev->event_type = MPTSAS_ADD_DEVICE; break; case MPI_EVENT_RAID_RC_PHYSDISK_CREATED: + ioc->raid_data.isRaid = 1; + ev->phys_disk_num_valid = 1; + ev->phys_disk_num = raid_event_data->PhysDiskNum; ev->event_type = MPTSAS_DEL_DEVICE; break; + case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED: + switch (state) { + case MPI_PD_STATE_ONLINE: + ioc->raid_data.isRaid = 1; + ev->phys_disk_num_valid = 1; + ev->phys_disk_num = raid_event_data->PhysDiskNum; + ev->event_type = MPTSAS_ADD_DEVICE; + break; + case MPI_PD_STATE_MISSING: + case MPI_PD_STATE_NOT_COMPATIBLE: + case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST: + case MPI_PD_STATE_FAILED_AT_HOST_REQUEST: + case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON: + ev->event_type = MPTSAS_DEL_DEVICE; + break; + default: + break; + } + break; case MPI_EVENT_RAID_RC_VOLUME_DELETED: ev->event_type = MPTSAS_DEL_RAID; break; @@ -1521,11 +2442,18 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, ev->event_type = MPTSAS_ADD_RAID; break; case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED: - volumeStatus = (RAID_VOL0_STATUS *) & - raid_event_data->SettingsStatus; - ev->event_type = (volumeStatus->State == - MPI_RAIDVOL0_STATUS_STATE_FAILED) ? - MPTSAS_DEL_RAID : MPTSAS_ADD_RAID; + switch (state) { + case MPI_RAIDVOL0_STATUS_STATE_FAILED: + case MPI_RAIDVOL0_STATUS_STATE_MISSING: + ev->event_type = MPTSAS_DEL_RAID; + break; + case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL: + case MPI_RAIDVOL0_STATUS_STATE_DEGRADED: + ev->event_type = MPTSAS_ADD_RAID; + break; + default: + break; + } break; default: break; @@ -1533,15 +2461,30 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, schedule_work(&ev->work); } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* work queue thread to clear the persitency table */ static void -mptscsih_sas_persist_clear_table(void * arg) +mptsas_send_discovery_event(MPT_ADAPTER *ioc, + EVENT_DATA_SAS_DISCOVERY *discovery_data) { - MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; + struct mptsas_discovery_event *ev; + + /* + * DiscoveryStatus + * + * This flag will be non-zero when firmware + * kicks off discovery, and return to zero + * once its completed. + */ + if (discovery_data->DiscoveryStatus) + return; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return; + INIT_WORK(&ev->work, mptsas_discovery_work); + ev->ioc = ioc; + schedule_work(&ev->work); +}; - mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT); -} static int mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) @@ -1552,20 +2495,34 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) if (!ioc->sh) goto out; + /* + * sas_discovery_ignore_events + * + * This flag is to prevent anymore processing of + * sas events once mptsas_remove function is called. + */ + if (ioc->sas_discovery_ignore_events) { + rc = mptscsih_event_process(ioc, reply); + goto out; + } + switch (event) { case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: - mptscsih_send_sas_event(ioc, + mptsas_send_sas_event(ioc, (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data); break; case MPI_EVENT_INTEGRATED_RAID: - mptscsih_send_raid_event(ioc, + mptsas_send_raid_event(ioc, (EVENT_DATA_RAID *)reply->Data); break; case MPI_EVENT_PERSISTENT_TABLE_FULL: - INIT_WORK(&ioc->mptscsih_persistTask, - mptscsih_sas_persist_clear_table, - (void *)ioc); - schedule_work(&ioc->mptscsih_persistTask); + INIT_WORK(&ioc->sas_persist_task, + mptsas_persist_clear_table); + schedule_work(&ioc->sas_persist_task); + break; + case MPI_EVENT_SAS_DISCOVERY: + mptsas_send_discovery_event(ioc, + (EVENT_DATA_SAS_DISCOVERY *)reply->Data); break; default: rc = mptscsih_event_process(ioc, reply); @@ -1668,7 +2625,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->sas_topology); mutex_init(&ioc->sas_topology_mutex); - + mutex_init(&ioc->sas_discovery_mutex); mutex_init(&ioc->sas_mgmt.mutex); init_completion(&ioc->sas_mgmt.done); @@ -1754,7 +2711,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) hd->timer.data = (unsigned long) hd; hd->timer.function = mptscsih_timer_expired; - hd->mpt_pq_filter = mpt_pq_filter; ioc->sas_data.ptClear = mpt_pt_clear; if (ioc->sas_data.ptClear==1) { @@ -1762,12 +2718,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT); } - ddvprintk((MYIOC_s_INFO_FMT - "mpt_pq_filter %x mpt_pq_filter %x\n", - ioc->name, - mpt_pq_filter, - mpt_pq_filter)); - init_waitqueue_head(&hd->scandv_waitq); hd->scandv_wait_done = 0; hd->last_queue_full = 0; @@ -1781,23 +2731,9 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) mptsas_scan_sas_topology(ioc); - /* - Reporting RAID volumes. - */ - if (!ioc->raid_data.pIocPg2) - return 0; - if (!ioc->raid_data.pIocPg2->NumActiveVolumes) - return 0; - for (ii=0;iiraid_data.pIocPg2->NumActiveVolumes;ii++) { - scsi_add_device(sh, - ioc->num_ports, - ioc->raid_data.pIocPg2->RaidVolume[ii].VolumeID, - 0); - } - return 0; -out_mptsas_probe: + out_mptsas_probe: mptscsih_remove(pdev); return error; @@ -1807,12 +2743,17 @@ static void __devexit mptsas_remove(struct pci_dev *pdev) { MPT_ADAPTER *ioc = pci_get_drvdata(pdev); struct mptsas_portinfo *p, *n; + int i; + ioc->sas_discovery_ignore_events=1; sas_remove_host(ioc->sh); mutex_lock(&ioc->sas_topology_mutex); list_for_each_entry_safe(p, n, &ioc->sas_topology, list) { list_del(&p->list); + for (i = 0 ; i < p->num_phys ; i++) + mptsas_port_delete(p->phy_info[i].port_details); + kfree(p->phy_info); kfree(p); } mutex_unlock(&ioc->sas_topology_mutex); @@ -1821,17 +2762,15 @@ static void __devexit mptsas_remove(struct pci_dev *pdev) } static struct pci_device_id mptsas_pci_table[] = { - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064, - PCI_ANY_ID, PCI_ANY_ID }, - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066, + { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064, PCI_ANY_ID, PCI_ANY_ID }, - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068, + { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068, PCI_ANY_ID, PCI_ANY_ID }, - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064E, + { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064E, PCI_ANY_ID, PCI_ANY_ID }, - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066E, + { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068E, PCI_ANY_ID, PCI_ANY_ID }, - { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068E, + { PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1078, PCI_ANY_ID, PCI_ANY_ID }, {0} /* Terminating entry */ }; @@ -1867,7 +2806,7 @@ mptsas_init(void) mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER); if (mpt_event_register(mptsasDoneCtx, mptsas_event_process) == 0) { - devtprintk((KERN_INFO MYNAM + devtverboseprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n")); }