[SCSI] fusion - inactive raid support, and raid event bug fix's

inactive raid support, e.g. exposing hidden raid components
belonging to a volume that are inactive.  Also misc bug fix's for
various raid asyn events.

Signed-off-by: Eric Moore <Eric.Moore@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index f7c5e0d..a8df06c 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -94,12 +94,14 @@
 static int	mptsasInternalCtx = -1; /* Used only for internal commands */
 static int	mptsasMgmtCtx = -1;
 
+static void mptsas_hotplug_work(struct work_struct *work);
 
 enum mptsas_hotplug_action {
 	MPTSAS_ADD_DEVICE,
 	MPTSAS_DEL_DEVICE,
 	MPTSAS_ADD_RAID,
 	MPTSAS_DEL_RAID,
+	MPTSAS_ADD_INACTIVE_VOLUME,
 	MPTSAS_IGNORE_EVENT,
 };
 
@@ -108,14 +110,15 @@
 	MPT_ADAPTER		*ioc;
 	enum mptsas_hotplug_action event_type;
 	u64			sas_address;
-	u32			channel;
-	u32			id;
+	u8			channel;
+	u8			id;
 	u32			device_info;
 	u16			handle;
 	u16			parent_handle;
 	u8			phy_id;
-	u8			phys_disk_num;
-	u8			phys_disk_num_valid;
+	u8			phys_disk_num_valid;	/* hrc (hidden raid component) */
+	u8			phys_disk_num;		/* hrc - unique index*/
+	u8			hidden_raid_component;	/* hrc - don't expose*/
 };
 
 struct mptsas_discovery_event {
@@ -140,6 +143,7 @@
 	u8	port_id;	/* sas physical port this device
 				   is assoc'd with */
 	u8	id;		/* logical target id of this device */
+	u32	phys_disk_num;	/* phys disk id, for csmi-ioctls */
 	u8	channel;	/* logical bus number of this device */
 	u64	sas_address;    /* WWN of this device,
 				   SATA is assigned by HBA,expander */
@@ -711,6 +715,7 @@
 						channel, id);
 				vtarget->tflags |=
 				    MPT_TARGET_FLAGS_RAID_COMPONENT;
+				p->phy_info[i].attached.phys_disk_num = id;
 			}
 			mutex_unlock(&hd->ioc->sas_topology_mutex);
 			goto out;
@@ -1272,6 +1277,7 @@
 	device_info->phy_id = buffer->PhyNum;
 	device_info->port_id = buffer->PhysicalPort;
 	device_info->id = buffer->TargetID;
+	device_info->phys_disk_num = ~0;
 	device_info->channel = buffer->Bus;
 	memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
 	device_info->sas_address = le64_to_cpu(sas_address);
@@ -1983,6 +1989,8 @@
 	/*
 	  Reporting RAID volumes.
 	*/
+	if (!ioc->ir_firmware)
+		goto out;
 	if (!ioc->raid_data.pIocPg2)
 		goto out;
 	if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
@@ -2041,12 +2049,12 @@
 	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.sas_address
-			    != sas_address)
-				continue;
 			if (!mptsas_is_end_device(
 				&port_info->phy_info[i].attached))
 				continue;
+			if (port_info->phy_info[i].attached.sas_address
+			    != sas_address)
+				continue;
 			phy_info = &port_info->phy_info[i];
 			break;
 		}
@@ -2056,7 +2064,7 @@
 }
 
 static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u32 id)
+mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u8 channel, u8 id)
 {
 	struct mptsas_portinfo *port_info;
 	struct mptsas_phyinfo *phy_info = NULL;
@@ -2065,11 +2073,40 @@
 	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.id != id)
-				continue;
 			if (!mptsas_is_end_device(
 				&port_info->phy_info[i].attached))
 				continue;
+			if (port_info->phy_info[i].attached.id != id)
+				continue;
+			if (port_info->phy_info[i].attached.channel != channel)
+				continue;
+			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_phys_disk_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+	struct mptsas_portinfo *port_info;
+	struct mptsas_phyinfo *phy_info = NULL;
+	int i;
+
+	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))
+				continue;
+			if (port_info->phy_info[i].attached.phys_disk_num == ~0)
+				continue;
+			if (port_info->phy_info[i].attached.phys_disk_num != id)
+				continue;
+			if (port_info->phy_info[i].attached.channel != channel)
+				continue;
 			phy_info = &port_info->phy_info[i];
 			break;
 		}
@@ -2105,6 +2142,76 @@
 			mptsas_reprobe_lun);
 }
 
+static void
+mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+	CONFIGPARMS			cfg;
+	ConfigPageHeader_t		hdr;
+	dma_addr_t			dma_handle;
+	pRaidVolumePage0_t		buffer = NULL;
+	RaidPhysDiskPage0_t 		phys_disk;
+	int				i;
+	struct mptsas_hotplug_event 	*ev;
+
+	memset(&cfg, 0 , sizeof(CONFIGPARMS));
+	memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+	hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
+	cfg.pageAddr = (channel << 8) + id;
+	cfg.cfghdr.hdr = &hdr;
+	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+	if (mpt_config(ioc, &cfg) != 0)
+		goto out;
+
+	if (!hdr.PageLength)
+		goto out;
+
+	buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+	    &dma_handle);
+
+	if (!buffer)
+		goto out;
+
+	cfg.physAddr = dma_handle;
+	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+	if (mpt_config(ioc, &cfg) != 0)
+		goto out;
+
+	if (!(buffer->VolumeStatus.Flags &
+	    MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE))
+		goto out;
+
+	if (!buffer->NumPhysDisks)
+		goto out;
+
+	for (i = 0; i < buffer->NumPhysDisks; i++) {
+
+		if (mpt_raid_phys_disk_pg0(ioc,
+		    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
+			continue;
+
+		ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+		if (!ev) {
+			printk(KERN_WARNING "mptsas: lost hotplug event\n");
+			goto out;
+		}
+
+		INIT_WORK(&ev->work, mptsas_hotplug_work);
+		ev->ioc = ioc;
+		ev->id = phys_disk.PhysDiskID;
+		ev->channel = phys_disk.PhysDiskBus;
+		ev->phys_disk_num_valid = 1;
+		ev->phys_disk_num = phys_disk.PhysDiskNum;
+		ev->event_type = MPTSAS_ADD_DEVICE;
+		schedule_work(&ev->work);
+	}
+
+ out:
+	if (buffer)
+		pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+		    dma_handle);
+}
 /*
  * Work queue thread to handle SAS hotplug events
  */
@@ -2113,6 +2220,7 @@
 {
 	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;
@@ -2125,17 +2233,43 @@
 	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);
+		phy_info = NULL;
+		if (ev->phys_disk_num_valid) {
+			if (ev->hidden_raid_component){
+				if (mptsas_sas_device_pg0(ioc, &sas_device,
+				    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+				     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+				    (ev->channel << 8) + ev->id)) {
+					dfailprintk((MYIOC_s_ERR_FMT
+					"%s: exit at line=%d\n", ioc->name,
+						__FUNCTION__, __LINE__));
+					break;
+				}
+				phy_info = mptsas_find_phyinfo_by_sas_address(
+				    ioc, sas_device.sas_address);
+			}else
+				phy_info = mptsas_find_phyinfo_by_phys_disk_num(
+				    ioc, ev->channel, ev->phys_disk_num);
+		}
+
+		if (!phy_info)
+			phy_info = mptsas_find_phyinfo_by_target(ioc,
+			    ev->channel, ev->id);
 
 		/*
 		 * Sanity checks, for non-existing phys and remote rphys.
 		 */
-		if (!phy_info || !phy_info->port_details) {
+		if (!phy_info){
+			dfailprintk((MYIOC_s_ERR_FMT
+				"%s: exit at line=%d\n", ioc->name,
+				__FUNCTION__, __LINE__));
+			break;
+		}
+		if (!phy_info->port_details) {
 			dfailprintk((MYIOC_s_ERR_FMT
 				"%s: exit at line=%d\n", ioc->name,
 			       	__FUNCTION__, __LINE__));
@@ -2148,6 +2282,7 @@
 			       	__FUNCTION__, __LINE__));
 			break;
 		}
+
 		port = mptsas_get_port(phy_info);
 		if (!port) {
 			dfailprintk((MYIOC_s_ERR_FMT
@@ -2170,28 +2305,38 @@
 			/*
 			 * Handling  RAID components
 			 */
-			if (ev->phys_disk_num_valid) {
+			if (ev->phys_disk_num_valid &&
+			    ev->hidden_raid_component) {
+				printk(MYIOC_s_INFO_FMT
+				    "RAID Hidding: channel=%d, id=%d, "
+				    "physdsk %d \n", ioc->name, ev->channel,
+				    ev->id, ev->phys_disk_num);
 				vtarget->id = ev->phys_disk_num;
-				vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
+				vtarget->tflags |=
+				    MPT_TARGET_FLAGS_RAID_COMPONENT;
 				mptsas_reprobe_target(starget, 1);
-				break;
+				phy_info->attached.phys_disk_num =
+				    ev->phys_disk_num;
+			break;
 			}
 
 			vtarget->deleted = 1;
 			mptsas_target_reset(ioc, vtarget);
 		}
 
-		if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET)
+		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)
+		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)
+		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);
-
 #ifdef MPT_DEBUG_SAS_WIDE
 		dev_printk(KERN_DEBUG, &port->dev,
 		    "delete port (%d)\n", port->port_identifier);
@@ -2209,14 +2354,14 @@
 		 */
 		if (mptsas_sas_device_pg0(ioc, &sas_device,
 		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
-		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT), ev->id)) {
+		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+			(ev->channel << 8) + 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,
@@ -2230,7 +2375,8 @@
 		}
 
 		starget = mptsas_get_starget(phy_info);
-		if (starget) {
+		if (starget && (!ev->hidden_raid_component)){
+
 			vtarget = starget->hostdata;
 
 			if (!vtarget) {
@@ -2243,9 +2389,15 @@
 			 * Handling  RAID components
 			 */
 			if (vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
-				vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+				printk(MYIOC_s_INFO_FMT
+				    "RAID Exposing: channel=%d, id=%d, "
+				    "physdsk %d \n", ioc->name, ev->channel,
+				    ev->id, ev->phys_disk_num);
+				vtarget->tflags &=
+				    ~MPT_TARGET_FLAGS_RAID_COMPONENT;
 				vtarget->id = ev->id;
 				mptsas_reprobe_target(starget, 0);
+				phy_info->attached.phys_disk_num = ~0;
 			}
 			break;
 		}
@@ -2254,8 +2406,10 @@
 			dfailprintk((MYIOC_s_ERR_FMT
 				"%s: exit at line=%d\n", ioc->name,
 			       	__FUNCTION__, __LINE__));
+			if (ev->channel) printk("%d\n", __LINE__);
 			break;
 		}
+
 		port = mptsas_get_port(phy_info);
 		if (!port) {
 			dfailprintk((MYIOC_s_ERR_FMT
@@ -2263,15 +2417,17 @@
 			       	__FUNCTION__, __LINE__));
 			break;
 		}
-
 		memcpy(&phy_info->attached, &sas_device,
 		    sizeof(struct mptsas_devinfo));
 
-		if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_TARGET)
+		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)
+		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)
+		if (phy_info->attached.device_info &
+		    MPI_SAS_DEVICE_INFO_SATA_DEVICE)
 			ds = "sata";
 
 		printk(MYIOC_s_INFO_FMT
@@ -2312,19 +2468,23 @@
 		break;
 	case MPTSAS_DEL_RAID:
 		sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
-	    	    ev->id, 0);
+		    ev->id, 0);
 		if (!sdev)
 			break;
 		printk(MYIOC_s_INFO_FMT
 		       "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);
+		vdevice = sdev->hostdata;
 		scsi_remove_device(sdev);
 		scsi_device_put(sdev);
 		mpt_findImVolumes(ioc);
 		break;
+	case MPTSAS_ADD_INACTIVE_VOLUME:
+		mptsas_adding_inactive_raid_components(ioc,
+		    ev->channel, ev->id);
+		break;
 	case MPTSAS_IGNORE_EVENT:
 	default:
 		break;
@@ -2332,7 +2492,6 @@
 
 	mutex_unlock(&ioc->sas_discovery_mutex);
 	kfree(ev);
-
 }
 
 static void
@@ -2386,15 +2545,20 @@
 		    mptsas_persist_clear_table);
 		schedule_work(&ioc->sas_persist_task);
 		break;
+	/*
+	 * TODO, handle other events
+	 */
 	case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
-	/* TODO */
+	case MPI_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
 	case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
-	/* TODO */
+	case MPI_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
+	case MPI_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
+	case MPI_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
+	case MPI_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
 	default:
 		break;
 	}
 }
-
 static void
 mptsas_send_raid_event(MPT_ADAPTER *ioc,
 		EVENT_DATA_RAID *raid_event_data)
@@ -2415,31 +2579,36 @@
 	INIT_WORK(&ev->work, mptsas_hotplug_work);
 	ev->ioc = ioc;
 	ev->id = raid_event_data->VolumeID;
+	ev->channel = raid_event_data->VolumeBus;
 	ev->event_type = MPTSAS_IGNORE_EVENT;
 
 	switch (raid_event_data->ReasonCode) {
 	case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
+		ev->phys_disk_num_valid = 1;
+		ev->phys_disk_num = raid_event_data->PhysDiskNum;
 		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->hidden_raid_component = 1;
 		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;
+		case MPI_PD_STATE_NOT_COMPATIBLE:
 			ev->phys_disk_num_valid = 1;
 			ev->phys_disk_num = raid_event_data->PhysDiskNum;
+			ev->hidden_raid_component = 1;
 			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->phys_disk_num_valid = 1;
+			ev->phys_disk_num = raid_event_data->PhysDiskNum;
 			ev->event_type = MPTSAS_DEL_DEVICE;
 			break;
 		default:
@@ -2496,6 +2665,35 @@
 	schedule_work(&ev->work);
 };
 
+/*
+ * mptsas_send_ir2_event - handle exposing hidden disk when
+ * an inactive raid volume is added
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @ir2_data
+ *
+ */
+static void
+mptsas_send_ir2_event(MPT_ADAPTER *ioc, PTR_MPI_EVENT_DATA_IR2 ir2_data)
+{
+	struct mptsas_hotplug_event *ev;
+
+	if (ir2_data->ReasonCode !=
+	    MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED)
+		return;
+
+	ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+	if (!ev)
+		return;
+
+	INIT_WORK(&ev->work, mptsas_hotplug_work);
+	ev->ioc = ioc;
+	ev->id = ir2_data->TargetID;
+	ev->channel = ir2_data->Bus;
+	ev->event_type = MPTSAS_ADD_INACTIVE_VOLUME;
+
+	schedule_work(&ev->work);
+};
 
 static int
 mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
@@ -2535,6 +2733,10 @@
 		mptsas_send_discovery_event(ioc,
 			(EVENT_DATA_SAS_DISCOVERY *)reply->Data);
 		break;
+	case MPI_EVENT_IR2:
+		mptsas_send_ir2_event(ioc,
+		    (PTR_MPI_EVENT_DATA_IR2)reply->Data);
+		break;
 	default:
 		rc = mptscsih_event_process(ioc, reply);
 		break;
@@ -2742,7 +2944,7 @@
 	struct mptsas_portinfo *p, *n;
 	int i;
 
-	ioc->sas_discovery_ignore_events=1;
+	ioc->sas_discovery_ignore_events = 1;
 	sas_remove_host(ioc->sh);
 
 	mutex_lock(&ioc->sas_topology_mutex);