[SCSI] fusion: add support for raid hot add/del support
authorMoore, Eric <Eric.Moore@lsil.com>
Thu, 26 Jan 2006 23:20:06 +0000 (16:20 -0700)
committerJames Bottomley <jejb@mulgrave.(none)>
Tue, 31 Jan 2006 20:39:37 +0000 (14:39 -0600)
RAID event support.

This will hot add and remove raid volumes
when managment application creates and
deletes the volumes.  The driver is basically
responding to firmware asyn events, and reporting
the changes to the above layers.

Signed-off-by: Eric Moore <Eric.Moore@lsil.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/message/fusion/mptsas.c

index 5a06d8d8694eb3cff0322e479a089ffb5d7021c1..8e77837c07ec4e983618149596740883766091b4 100644 (file)
@@ -89,6 +89,8 @@ static int    mptsasMgmtCtx = -1;
 enum mptsas_hotplug_action {
        MPTSAS_ADD_DEVICE,
        MPTSAS_DEL_DEVICE,
+       MPTSAS_ADD_RAID,
+       MPTSAS_DEL_RAID,
 };
 
 struct mptsas_hotplug_event {
@@ -114,6 +116,7 @@ struct mptsas_hotplug_event {
 
 struct mptsas_devinfo {
        u16     handle;         /* unique id to address this device */
+       u16     handle_parent;  /* unique id to address parent device */
        u8      phy_id;         /* phy number of parent device */
        u8      port_id;        /* sas physical port this device
                                   is assoc'd with */
@@ -714,6 +717,7 @@ 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->phy_id = buffer->PhyNum;
        device_info->port_id = buffer->PhysicalPort;
        device_info->id = buffer->TargetID;
@@ -863,6 +867,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)
@@ -1227,7 +1251,7 @@ mptsas_find_phyinfo_by_parent(MPT_ADAPTER *ioc, u16 parent_handle, u8 phy_id)
 }
 
 static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
+mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u32 id)
 {
        struct mptsas_portinfo *port_info;
        struct mptsas_phyinfo *phy_info = NULL;
@@ -1239,12 +1263,12 @@ mptsas_find_phyinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
         */
        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].attached.handle == handle) {
-                               phy_info = &port_info->phy_info[i];
-                               break;
-                       }
-               }
+               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);
 
@@ -1258,36 +1282,58 @@ mptsas_hotplug_work(void *arg)
        MPT_ADAPTER *ioc = ev->ioc;
        struct mptsas_phyinfo *phy_info;
        struct sas_rphy *rphy;
+       struct scsi_device *sdev;
        char *ds = NULL;
-
-       if (ev->device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET)
-               ds = "ssp";
-       if (ev->device_info & MPI_SAS_DEVICE_INFO_STP_TARGET)
-               ds = "stp";
-       if (ev->device_info & MPI_SAS_DEVICE_INFO_SATA_DEVICE)
-               ds = "sata";
+       struct mptsas_devinfo sas_device;
 
        switch (ev->event_type) {
        case MPTSAS_DEL_DEVICE:
-               printk(MYIOC_s_INFO_FMT
-                      "removing %s device, channel %d, id %d, phy %d\n",
-                      ioc->name, ds, ev->channel, ev->id, ev->phy_id);
 
-               phy_info = mptsas_find_phyinfo_by_handle(ioc, ev->handle);
+               phy_info = mptsas_find_phyinfo_by_target(ioc, ev->id);
                if (!phy_info) {
                        printk("mptsas: remove event for non-existant PHY.\n");
                        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);
+
                if (phy_info->rphy) {
                        sas_rphy_delete(phy_info->rphy);
                        phy_info->rphy = NULL;
                }
                break;
        case MPTSAS_ADD_DEVICE:
-               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);
+
+               /*
+                * 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
+                */
+               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;
+               }
 
                phy_info = mptsas_find_phyinfo_by_parent(ioc,
                                ev->parent_handle, ev->phy_id);
@@ -1310,10 +1356,23 @@ mptsas_hotplug_work(void *arg)
                phy_info->attached.sas_address = ev->sas_address;
                phy_info->attached.device_info = ev->device_info;
 
+               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 */
 
+               rphy->scsi_target_id = phy_info->attached.id;
                mptsas_parse_device_info(&rphy->identify, &phy_info->attached);
                if (sas_rphy_add(rphy)) {
                        sas_rphy_free(rphy);
@@ -1322,6 +1381,40 @@ mptsas_hotplug_work(void *arg)
 
                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 device, 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 device, 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);
@@ -1372,23 +1465,78 @@ mptscsih_send_sas_event(MPT_ADAPTER *ioc,
        schedule_work(&ev->work);
 }
 
+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:
+               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 int
 mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
 {
+       int rc=1;
        u8 event = le32_to_cpu(reply->Event) & 0xFF;
 
        if (!ioc->sh)
-               return 1;
+               goto out;
 
        switch (event) {
        case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
                mptscsih_send_sas_event(ioc,
                        (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data);
-               return 1;               /* currently means nothing really */
-
+               break;
+       case MPI_EVENT_INTEGRATED_RAID:
+               mptscsih_send_raid_event(ioc,
+                       (EVENT_DATA_RAID *)reply->Data);
+               break;
        default:
-               return mptscsih_event_process(ioc, reply);
+               rc = mptscsih_event_process(ioc, reply);
+               break;
        }
+ out:
+
+       return rc;
 }
 
 static int