]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/message/fusion/mptsas.c
[SCSI] fusion - expander hotplug suport in mptsas module
[mv-sheeva.git] / drivers / message / fusion / mptsas.c
index 17e9757e728be780211d1e7a0008aaa9e2724d18..be4eb8a308b7a13a05d36424f7752e4695210f36 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  Copyright (c) 1999-2005 LSI Logic Corporation
  *  (mailto:mpt_linux_developer@lsil.com)
- *  Copyright (c) 2005 Dell
+ *  Copyright (c) 2005-2006 Dell
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -86,6 +86,33 @@ static int   mptsasInternalCtx = -1; /* Used only for internal commands */
 static int     mptsasMgmtCtx = -1;
 
 
+enum mptsas_hotplug_action {
+       MPTSAS_ADD_DEVICE,
+       MPTSAS_DEL_DEVICE,
+       MPTSAS_ADD_RAID,
+       MPTSAS_DEL_RAID,
+};
+
+struct mptsas_hotplug_event {
+       struct work_struct      work;
+       MPT_ADAPTER             *ioc;
+       enum mptsas_hotplug_action event_type;
+       u64                     sas_address;
+       u32                     channel;
+       u32                     id;
+       u32                     device_info;
+       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;
+};
+
 /*
  * SAS topology structures
  *
@@ -96,11 +123,14 @@ static int mptsasMgmtCtx = -1;
 
 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 */
-       u8      target;         /* logical target id of this device */
-       u8      bus;            /* logical bus number of this device */
+       u8      id;             /* logical target id of this device */
+       u8      channel;        /* logical bus number of this device */
        u64     sas_address;    /* WWN of this device,
                                   SATA is assigned by HBA,expander */
        u32     device_info;    /* bitfield detailed info about this device */
@@ -114,7 +144,9 @@ struct mptsas_phyinfo {
        u8      programmed_link_rate;   /* programmed max/min phy link rate */
        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 scsi_target *starget;
 };
 
 struct mptsas_portinfo {
@@ -124,6 +156,17 @@ struct mptsas_portinfo {
        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
 static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
@@ -183,6 +226,7 @@ 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));
@@ -221,6 +265,103 @@ 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;
+}
+
+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;
+}
 
 /*
  * This is pretty ugly.  We will be able to seriously clean it up
@@ -237,51 +378,105 @@ mptsas_slave_alloc(struct scsi_device *sdev)
        VirtTarget              *vtarget;
        VirtDevice              *vdev;
        struct scsi_target      *starget;
+       u32                     target_id;
        int i;
 
-       vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
+       vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
        if (!vdev) {
                printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
                                hd->ioc->name, sizeof(VirtDevice));
                return -ENOMEM;
        }
-       memset(vdev, 0, sizeof(VirtDevice));
-       vdev->ioc_id = hd->ioc->id;
        sdev->hostdata = vdev;
        starget = scsi_target(sdev);
        vtarget = starget->hostdata;
+       vtarget->ioc_id = hd->ioc->id;
        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;
        }
 
+       /*
+         RAID volumes placed beyond the last expected port.
+       */
+       if (sdev->channel == hd->ioc->num_ports) {
+               target_id = sdev->id;
+               vtarget->bus_id = 0;
+               vdev->lun = 0;
+               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.target;
-                               vdev->bus_id = p->phy_info[i].attached.bus;
+                               target_id = p->phy_info[i].attached.id;
+                               vtarget->bus_id = p->phy_info[i].attached.channel;
                                vdev->lun = sdev->lun;
+                               p->phy_info[i].starget = sdev->sdev_target;
+                               /*
+                                * Exposing hidden disk (RAID)
+                                */
+                               if (mptscsih_is_phys_disk(hd->ioc, target_id)) {
+                                       target_id = mptscsih_raid_id_to_num(hd,
+                                                       target_id);
+                                       vdev->vtarget->tflags |=
+                                           MPT_TARGET_FLAGS_RAID_COMPONENT;
+                                       sdev->no_uld_attach = 1;
+                               }
+                               mutex_unlock(&hd->ioc->sas_topology_mutex);
                                goto out;
                        }
                }
        }
+       mutex_unlock(&hd->ioc->sas_topology_mutex);
 
-       printk("No matching SAS device found!!\n");
        kfree(vdev);
-       return -ENODEV;
+       return -ENXIO;
 
  out:
-       vtarget->ioc_id = vdev->ioc_id;
-       vtarget->target_id = vdev->target_id;
-       vtarget->bus_id = vdev->bus_id;
+       vtarget->target_id = target_id;
        vtarget->num_luns++;
        return 0;
 }
 
+static void
+mptsas_slave_destroy(struct scsi_device *sdev)
+{
+       struct Scsi_Host *host = sdev->host;
+       MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
+       VirtDevice *vdev;
+
+       /*
+        * 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->vtarget->bus_id,
+                    vdev->vtarget->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->vtarget->target_id);
+
+                       hd->tmPending = 0;
+                       hd->tmState = TM_STATE_NONE;
+               }
+       }
+       mptscsih_slave_destroy(sdev);
+}
+
 static struct scsi_host_template mptsas_driver_template = {
        .module                         = THIS_MODULE,
        .proc_name                      = "mptsas",
@@ -293,7 +488,7 @@ static struct scsi_host_template mptsas_driver_template = {
        .slave_alloc                    = mptsas_slave_alloc,
        .slave_configure                = mptscsih_slave_configure,
        .target_destroy                 = mptscsih_target_destroy,
-       .slave_destroy                  = mptscsih_slave_destroy,
+       .slave_destroy                  = mptsas_slave_destroy,
        .change_queue_depth             = mptscsih_change_queue_depth,
        .eh_abort_handler               = mptscsih_abort,
        .eh_device_reset_handler        = mptscsih_dev_reset,
@@ -308,12 +503,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);
@@ -399,7 +588,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
        if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP)
                return -ENXIO;
 
-       if (down_interruptible(&ioc->sas_mgmt.mutex))
+       if (mutex_lock_interruptible(&ioc->sas_mgmt.mutex))
                goto out;
 
        mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
@@ -450,13 +639,72 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
        error = 0;
 
  out_unlock:
-       up(&ioc->sas_mgmt.mutex);
+       mutex_unlock(&ioc->sas_mgmt.mutex);
  out:
        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,
 };
 
@@ -516,6 +764,9 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
                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;
@@ -622,6 +873,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;
@@ -647,10 +899,14 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
        mptsas_print_device_pg0(buffer);
 
        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->target = buffer->TargetID;
-       device_info->bus = buffer->Bus;
+       device_info->id = buffer->TargetID;
+       device_info->channel = buffer->Bus;
        memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
        device_info->sas_address = le64_to_cpu(sas_address);
        device_info->device_info =
@@ -688,6 +944,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;
@@ -788,7 +1045,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);
@@ -796,6 +1052,26 @@ 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)
@@ -858,36 +1134,43 @@ 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)
 {
-       struct sas_phy *port;
+       MPT_ADAPTER *ioc;
+       struct sas_phy *phy;
        int error;
 
-       port = sas_phy_alloc(dev, index);
-       if (!port)
-               return -ENOMEM;
+       if (!dev)
+               return -ENODEV;
 
-       port->port_identifier = phy_info->port_id;
-       mptsas_parse_device_info(&port->identify, &phy_info->identify);
+       if (!phy_info->phy) {
+               phy = sas_phy_alloc(dev, index);
+               if (!phy)
+                       return -ENOMEM;
+       } else
+               phy = phy_info->phy;
+
+       phy->port_identifier = phy_info->port_id;
+       mptsas_parse_device_info(&phy->identify, &phy_info->identify);
 
        /*
         * Set Negotiated link rate.
         */
        switch (phy_info->negotiated_link_rate) {
        case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED:
-               port->negotiated_linkrate = SAS_PHY_DISABLED;
+               phy->negotiated_linkrate = SAS_PHY_DISABLED;
                break;
        case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION:
-               port->negotiated_linkrate = SAS_LINK_RATE_FAILED;
+               phy->negotiated_linkrate = SAS_LINK_RATE_FAILED;
                break;
        case MPI_SAS_IOUNIT0_RATE_1_5:
-               port->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
+               phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
                break;
        case MPI_SAS_IOUNIT0_RATE_3_0:
-               port->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
+               phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
                break;
        case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE:
        case MPI_SAS_IOUNIT0_RATE_UNKNOWN:
        default:
-               port->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+               phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
                break;
        }
 
@@ -896,10 +1179,10 @@ static int mptsas_probe_one_phy(struct device *dev,
         */
        switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
        case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5:
-               port->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+               phy->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
                break;
        case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
-               port->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
+               phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
                break;
        default:
                break;
@@ -911,10 +1194,10 @@ static int mptsas_probe_one_phy(struct device *dev,
        switch (phy_info->programmed_link_rate &
                        MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
        case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5:
-               port->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+               phy->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS;
                break;
        case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
-               port->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
+               phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
                break;
        default:
                break;
@@ -925,10 +1208,10 @@ static int mptsas_probe_one_phy(struct device *dev,
         */
        switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) {
        case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5:
-               port->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+               phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
                break;
        case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
-               port->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
+               phy->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
                break;
        default:
                break;
@@ -940,28 +1223,45 @@ static int mptsas_probe_one_phy(struct device *dev,
        switch (phy_info->programmed_link_rate &
                        MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) {
        case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5:
-               port->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+               phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
                break;
        case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
-               port->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS;
+               phy->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS;
                break;
        default:
                break;
        }
 
-       if (local)
-               port->local_attached = 1;
+       if (!phy_info->phy) {
 
-       error = sas_phy_add(port);
-       if (error) {
-               sas_phy_free(port);
-               return error;
+               if (local)
+                       phy->local_attached = 1;
+
+               error = sas_phy_add(phy);
+               if (error) {
+                       sas_phy_free(phy);
+                       return error;
+               }
+               phy_info->phy = phy;
        }
 
-       if (phy_info->attached.handle) {
+       if ((phy_info->attached.handle) &&
+           (!phy_info->rphy)) {
+
                struct sas_rphy *rphy;
 
-               rphy = sas_rphy_alloc(port);
+               ioc = phy_to_ioc(phy_info->phy);
+
+               /*
+                * 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))
+                       return 0;
+
+               rphy = sas_rphy_alloc(phy);
                if (!rphy)
                        return 0; /* non-fatal: an rphy can be added later */
 
@@ -979,22 +1279,38 @@ static int mptsas_probe_one_phy(struct device *dev,
 }
 
 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 = kmalloc(sizeof(*port_info), GFP_KERNEL);
-       if (!port_info)
+       hba = kzalloc(sizeof(*port_info), GFP_KERNEL);
+       if (! hba)
                goto out;
-       memset(port_info, 0, sizeof(*port_info));
 
-       error = mptsas_sas_io_unit_pg0(ioc, port_info);
+       error = mptsas_sas_io_unit_pg0(ioc, hba);
        if (error)
                goto out_free_port_info;
 
-       list_add_tail(&port_info->list, &ioc->sas_topology);
+       mutex_lock(&ioc->sas_topology_mutex);
+       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;
+               if (hba->phy_info)
+                       kfree(hba->phy_info);
+               kfree(hba);
+               hba = NULL;
+       }
+       mutex_unlock(&ioc->sas_topology_mutex);
+       ioc->num_ports = port_info->num_phys;
+
        for (i = 0; i < port_info->num_phys; i++) {
                mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
                        (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
@@ -1016,38 +1332,51 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *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);
+               ioc->sas_index++;
        }
 
        return 0;
 
  out_free_port_info:
-       kfree(port_info);
+       if (hba)
+               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;
        int error = -ENOMEM, i, j;
 
-       port_info = kmalloc(sizeof(*port_info), GFP_KERNEL);
-       if (!port_info)
+       ex = kzalloc(sizeof(*port_info), GFP_KERNEL);
+       if (!ex)
                goto out;
-       memset(port_info, 0, sizeof(*port_info));
 
-       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);
+       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;
+               if (ex->phy_info)
+                       kfree(ex->phy_info);
+               kfree(ex);
+               ex = NULL;
+       }
+       mutex_unlock(&ioc->sas_topology_mutex);
 
-       list_add_tail(&port_info->list, &ioc->sas_topology);
        for (i = 0; i < port_info->num_phys; i++) {
                struct device *parent;
 
@@ -1071,6 +1400,8 @@ 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;
                }
 
                /*
@@ -1079,6 +1410,7 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index)
                 * HBA phys.
                 */
                parent = &ioc->sh->shost_gendev;
+               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 ==
@@ -1086,29 +1418,602 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index)
                                        parent = &p->phy_info[j].rphy->dev;
                        }
                }
+               mutex_unlock(&ioc->sas_topology_mutex);
 
                mptsas_probe_one_phy(parent, &port_info->phy_info[i],
-                                    *index, 0);
-               (*index)++;
+                   ioc->sas_index, 0);
+               ioc->sas_index++;
        }
 
        return 0;
 
  out_free_port_info:
-       kfree(port_info);
+       if (ex) {
+               if (ex->phy_info)
+                       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;
+       int i;
+
+       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)) {
+
+                       /*
+                        * 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;
+
+                       /*
+                        * Delete rphys in the parent that point
+                        * to this expander.  The transport layer will
+                        * cleanup all the children.
+                        */
+                       for (i = 0; i < parent->num_phys; i++) {
+                               if ((!parent->phy_info[i].rphy) ||
+                                   (parent->phy_info[i].attached.sas_address !=
+                                  port_info->phy_info[i].identify.sas_address))
+                                       continue;
+                               sas_rphy_delete(parent->phy_info[i].rphy);
+                               memset(&parent->phy_info[i].attached, 0,
+                                   sizeof(struct mptsas_devinfo));
+                               parent->phy_info[i].rphy = NULL;
+                               parent->phy_info[i].starget = NULL;
+                       }
+ next_port:
+                       list_del(&port_info->list);
+                       if (port_info->phy_info)
+                               kfree(port_info->phy_info);
+                       kfree(port_info);
+               }
+               /*
+               * Free this memory allocated from inside
+               * mptsas_sas_expander_pg0
+               */
+               if (buffer.phy_info)
+                       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;
 
-       mptsas_probe_hba_phys(ioc, &index);
-       while (!mptsas_probe_expander_phys(ioc, &handle, &index))
+       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; i<ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+               scsi_add_device(ioc->sh, ioc->num_ports,
+                   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
+ */
+static void
+mptscsih_discovery_work(void * arg)
+{
+       struct mptsas_discovery_event *ev = arg;
+       MPT_ADAPTER *ioc = ev->ioc;
+       u32 handle = 0xFFFF;
+
+       mutex_lock(&ioc->sas_discovery_mutex);
+       ioc->sas_discovery_runtime=1;
+       mptsas_delete_expander_phys(ioc);
+       mptsas_probe_hba_phys(ioc);
+       while (!mptsas_probe_expander_phys(ioc, &handle))
+               ;
+       kfree(ev);
+       ioc->sas_discovery_runtime=0;
+       mutex_unlock(&ioc->sas_discovery_mutex);
+}
+
+static struct mptsas_phyinfo *
+mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id)
+{
+       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)
+               return NULL;
+
+       /*
+        * 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;
+                       }
+               }
+       }
+       mutex_unlock(&ioc->sas_topology_mutex);
+
+       return phy_info;
+}
+
+static struct mptsas_phyinfo *
+mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u32 id)
+{
+       struct mptsas_portinfo *port_info;
+       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;
+                               }
+       }
+       mutex_unlock(&ioc->sas_topology_mutex);
+
+       return phy_info;
+}
+
+/*
+ * Work queue thread to clear the persitency table
+ */
+static void
+mptscsih_sas_persist_clear_table(void * arg)
+{
+       MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg;
+
+       mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT);
+}
+
+static void
+mptsas_reprobe_lun(struct scsi_device *sdev, void *data)
+{
+       sdev->no_uld_attach = data ? 1 : 0;
+       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)
+{
+       struct mptsas_hotplug_event *ev = arg;
+       MPT_ADAPTER *ioc = ev->ioc;
+       struct mptsas_phyinfo *phy_info;
+       struct sas_rphy *rphy;
+       struct scsi_device *sdev;
+       char *ds = NULL;
+       struct mptsas_devinfo sas_device;
+       VirtTarget *vtarget;
+
+       mutex_lock(&ioc->sas_discovery_mutex);
+
+       switch (ev->event_type) {
+       case MPTSAS_DEL_DEVICE:
+
+               phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id);
+
+               /*
+                * Sanity checks, for non-existing phys and remote rphys.
+                */
+               if (!phy_info)
+                       break;
+               if (!phy_info->rphy)
+                       break;
+               if (phy_info->starget) {
+                       vtarget = phy_info->starget->hostdata;
+
+                       if (!vtarget)
+                               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(vtarget->starget, 1);
+                               break;
+                       }
+               }
+
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET)
+                       ds = "ssp";
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_STP_TARGET)
+                       ds = "stp";
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+                       ds = "sata";
+
+               printk(MYIOC_s_INFO_FMT
+                      "removing %s device, channel %d, id %d, phy %d\n",
+                      ioc->name, ds, ev->channel, ev->id, phy_info->phy_id);
+
+               sas_rphy_delete(phy_info->rphy);
+               memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
+               phy_info->rphy = NULL;
+               phy_info->starget = NULL;
+               break;
+       case MPTSAS_ADD_DEVICE:
+
+               /*
+                * Refresh sas device pg0 data
+                */
+               if (mptsas_sas_device_pg0(ioc, &sas_device,
+                   (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+                    MPI_SAS_DEVICE_PGAD_FORM_SHIFT), ev->id))
+                       break;
+
+               phy_info = mptsas_find_phyinfo_by_parent(ioc,
+                               sas_device.handle_parent, sas_device.phy_id);
+
+               if (!phy_info) {
+                       u32 handle = 0xFFFF;
+
+                       /*
+                       * Its possible when an expander has been hot added
+                       * containing attached devices, the sas firmware
+                       * may send a RC_ADDED event prior to the
+                       * DISCOVERY STOP event. If that occurs, our
+                       * view of the topology in the driver in respect to this
+                       * expander might of not been setup, and we hit this
+                       * condition.
+                       * Therefore, this code kicks off discovery to
+                       * refresh the data.
+                       * Then again, we check whether the parent phy has
+                       * been created.
+                       */
+                       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;
+
+                       phy_info = mptsas_find_phyinfo_by_parent(ioc,
+                               sas_device.handle_parent, sas_device.phy_id);
+                       if (!phy_info)
+                               break;
+               }
+
+               if (phy_info->starget) {
+                       vtarget = phy_info->starget->hostdata;
+
+                       if (!vtarget)
+                               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(phy_info->starget, 0);
+                       }
+                       break;
+               }
+
+               if (phy_info->rphy)
+                       break;
+
+               memcpy(&phy_info->attached, &sas_device,
+                   sizeof(struct mptsas_devinfo));
+
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET)
+                       ds = "ssp";
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_STP_TARGET)
+                       ds = "stp";
+               if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+                       ds = "sata";
+
+               printk(MYIOC_s_INFO_FMT
+                      "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)
+                       break; /* non-fatal: an rphy can be added later */
+
+               mptsas_parse_device_info(&rphy->identify, &phy_info->attached);
+               if (sas_rphy_add(rphy)) {
+                       sas_rphy_free(rphy);
+                       break;
+               }
+
+               phy_info->rphy = rphy;
+               break;
+       case MPTSAS_ADD_RAID:
+               sdev = scsi_device_lookup(
+                       ioc->sh,
+                       ioc->num_ports,
+                       ev->id,
+                       0);
+               if (sdev) {
+                       scsi_device_put(sdev);
+                       break;
+               }
+               printk(MYIOC_s_INFO_FMT
+                      "attaching raid volume, channel %d, id %d\n",
+                      ioc->name, ioc->num_ports, ev->id);
+               scsi_add_device(ioc->sh,
+                       ioc->num_ports,
+                       ev->id,
+                       0);
+               mpt_findImVolumes(ioc);
+               break;
+       case MPTSAS_DEL_RAID:
+               sdev = scsi_device_lookup(
+                       ioc->sh,
+                       ioc->num_ports,
+                       ev->id,
+                       0);
+               if (!sdev)
+                       break;
+               printk(MYIOC_s_INFO_FMT
+                      "removing raid volume, channel %d, id %d\n",
+                      ioc->name, ioc->num_ports, ev->id);
+               scsi_remove_device(sdev);
+               scsi_device_put(sdev);
+               mpt_findImVolumes(ioc);
+               break;
+       }
+
+       kfree(ev);
+       mutex_unlock(&ioc->sas_discovery_mutex);
+}
+
+static void
+mptscsih_send_sas_event(MPT_ADAPTER *ioc,
+               EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
+{
+       struct mptsas_hotplug_event *ev;
+       u32 device_info = le32_to_cpu(sas_event_data->DeviceInfo);
+       __le64 sas_address;
+
+       if ((device_info &
+            (MPI_SAS_DEVICE_INFO_SSP_TARGET |
+             MPI_SAS_DEVICE_INFO_STP_TARGET |
+             MPI_SAS_DEVICE_INFO_SATA_DEVICE )) == 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 = kmalloc(sizeof(*ev), GFP_ATOMIC);
+               if (!ev) {
+                       printk(KERN_WARNING "mptsas: lost hotplug event\n");
+                       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);
+               break;
+       case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED:
+       /*
+        * Persistent table is full.
+        */
+               INIT_WORK(&ioc->mptscsih_persistTask,
+                   mptscsih_sas_persist_clear_table,
+                   (void *)ioc);
+               schedule_work(&ioc->mptscsih_persistTask);
+               break;
+       case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
+       /* TODO */
+       case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
+       /* TODO */
+       default:
+               break;
+       }
+}
+
+static void
+mptscsih_send_raid_event(MPT_ADAPTER *ioc,
+               EVENT_DATA_RAID *raid_event_data)
+{
+       struct mptsas_hotplug_event *ev;
+       RAID_VOL0_STATUS * volumeStatus;
+
+       if (ioc->bus_type != SAS)
+               return;
+
+       ev = kmalloc(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);
+       ev->ioc = ioc;
+       ev->id = raid_event_data->VolumeID;
+
+       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_VOLUME_DELETED:
+               ev->event_type = MPTSAS_DEL_RAID;
+               break;
+       case MPI_EVENT_RAID_RC_VOLUME_CREATED:
+               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;
+               break;
+       default:
+               break;
+       }
+       schedule_work(&ev->work);
+}
+
+static void
+mptscsih_send_discovery(MPT_ADAPTER *ioc,
+       EVENT_DATA_SAS_DISCOVERY *discovery_data)
+{
+       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 = kmalloc(sizeof(*ev), GFP_ATOMIC);
+       if (!ev)
+               return;
+       memset(ev,0,sizeof(struct mptsas_discovery_event));
+       INIT_WORK(&ev->work, mptscsih_discovery_work, ev);
+       ev->ioc = ioc;
+       schedule_work(&ev->work);
+};
+
+
+static int
+mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
+{
+       int rc=1;
+       u8 event = le32_to_cpu(reply->Event) & 0xFF;
+
+       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,
+                       (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data);
+               break;
+       case MPI_EVENT_INTEGRATED_RAID:
+               mptscsih_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);
+               break;
+        case MPI_EVENT_SAS_DISCOVERY:
+               mptscsih_send_discovery(ioc,
+                       (EVENT_DATA_SAS_DISCOVERY *)reply->Data);
+               break;
+       default:
+               rc = mptscsih_event_process(ioc, reply);
+               break;
+       }
+ out:
+
+       return rc;
 }
 
 static int
@@ -1118,11 +2023,10 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        MPT_SCSI_HOST           *hd;
        MPT_ADAPTER             *ioc;
        unsigned long            flags;
-       int                      sz, ii;
+       int                      ii;
        int                      numSGE = 0;
        int                      scale;
        int                      ioc_cap;
-       u8                      *mem;
        int                     error=0;
        int                     r;
 
@@ -1203,7 +2107,9 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sh->unique_id = ioc->id;
 
        INIT_LIST_HEAD(&ioc->sas_topology);
-       init_MUTEX(&ioc->sas_mgmt.mutex);
+       mutex_init(&ioc->sas_topology_mutex);
+       mutex_init(&ioc->sas_discovery_mutex);
+       mutex_init(&ioc->sas_mgmt.mutex);
        init_completion(&ioc->sas_mgmt.done);
 
        /* Verify that we won't exceed the maximum
@@ -1244,36 +2150,27 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        /* SCSI needs scsi_cmnd lookup table!
         * (with size equal to req_depth*PtrSz!)
         */
-       sz = ioc->req_depth * sizeof(void *);
-       mem = kmalloc(sz, GFP_ATOMIC);
-       if (mem == NULL) {
+       hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
+       if (!hd->ScsiLookup) {
                error = -ENOMEM;
                goto out_mptsas_probe;
        }
 
-       memset(mem, 0, sz);
-       hd->ScsiLookup = (struct scsi_cmnd **) mem;
-
-       dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
-                ioc->name, hd->ScsiLookup, sz));
+       dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p\n",
+                ioc->name, hd->ScsiLookup));
 
        /* Allocate memory for the device structures.
         * A non-Null pointer at an offset
         * indicates a device exists.
         * max_id = 1 + maximum id (hosts.h)
         */
-       sz = sh->max_id * sizeof(void *);
-       mem = kmalloc(sz, GFP_ATOMIC);
-       if (mem == NULL) {
+       hd->Targets = kcalloc(sh->max_id, sizeof(void *), GFP_ATOMIC);
+       if (!hd->Targets) {
                error = -ENOMEM;
                goto out_mptsas_probe;
        }
 
-       memset(mem, 0, sz);
-       hd->Targets = (VirtTarget **) mem;
-
-       dprintk((KERN_INFO
-         "  vtarget @ %p, sz=%d\n", hd->Targets, sz));
+       dprintk((KERN_INFO "  vtarget @ %p\n", hd->Targets));
 
        /* Clear the TM flags
         */
@@ -1337,12 +2234,17 @@ static void __devexit mptsas_remove(struct pci_dev *pdev)
        MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
        struct mptsas_portinfo *p, *n;
 
+       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);
+               if (p->phy_info)
+                       kfree(p->phy_info);
                kfree(p);
        }
+       mutex_unlock(&ioc->sas_topology_mutex);
 
        mptscsih_remove(pdev);
 }
@@ -1393,8 +2295,8 @@ mptsas_init(void)
                mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER);
        mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER);
 
-       if (mpt_event_register(mptsasDoneCtx, mptscsih_event_process) == 0) {
-               devtprintk((KERN_INFO MYNAM
+       if (mpt_event_register(mptsasDoneCtx, mptsas_event_process) == 0) {
+               devtverboseprintk((KERN_INFO MYNAM
                  ": Registered for IOC event notifications\n"));
        }