|
@@ -96,6 +96,12 @@ static int mptsasMgmtCtx = -1;
|
|
|
|
|
|
static void mptsas_hotplug_work(struct work_struct *work);
|
|
|
|
|
|
+struct mptsas_target_reset_event {
|
|
|
+ struct list_head list;
|
|
|
+ EVENT_DATA_SAS_DEVICE_STATUS_CHANGE sas_event_data;
|
|
|
+ u8 target_reset_issued;
|
|
|
+};
|
|
|
+
|
|
|
enum mptsas_hotplug_action {
|
|
|
MPTSAS_ADD_DEVICE,
|
|
|
MPTSAS_DEL_DEVICE,
|
|
@@ -571,20 +577,271 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
|
|
|
mutex_unlock(&ioc->sas_topology_mutex);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * csmisas_find_vtarget
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ * @volume_id
|
|
|
+ * @volume_bus
|
|
|
+ *
|
|
|
+ **/
|
|
|
+static VirtTarget *
|
|
|
+mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id)
|
|
|
+{
|
|
|
+ struct scsi_device *sdev;
|
|
|
+ VirtDevice *vdev;
|
|
|
+ VirtTarget *vtarget = NULL;
|
|
|
+
|
|
|
+ shost_for_each_device(sdev, ioc->sh) {
|
|
|
+ if ((vdev = sdev->hostdata) == NULL)
|
|
|
+ continue;
|
|
|
+ if (vdev->vtarget->id == id &&
|
|
|
+ vdev->vtarget->channel == channel)
|
|
|
+ vtarget = vdev->vtarget;
|
|
|
+ }
|
|
|
+ return vtarget;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mptsas_target_reset
|
|
|
+ *
|
|
|
+ * Issues TARGET_RESET to end device using handshaking method
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ * @channel
|
|
|
+ * @id
|
|
|
+ *
|
|
|
+ * Returns (1) success
|
|
|
+ * (0) failure
|
|
|
+ *
|
|
|
+ **/
|
|
|
+static int
|
|
|
+mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
|
|
|
+{
|
|
|
+ MPT_FRAME_HDR *mf;
|
|
|
+ SCSITaskMgmt_t *pScsiTm;
|
|
|
+
|
|
|
+ if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
|
|
|
+ dfailprintk((MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n",
|
|
|
+ ioc->name,__FUNCTION__, __LINE__));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Format the Request
|
|
|
+ */
|
|
|
+ pScsiTm = (SCSITaskMgmt_t *) mf;
|
|
|
+ memset (pScsiTm, 0, sizeof(SCSITaskMgmt_t));
|
|
|
+ pScsiTm->TargetID = id;
|
|
|
+ pScsiTm->Bus = channel;
|
|
|
+ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
|
|
|
+ pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
|
|
|
+ pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
|
|
|
+
|
|
|
+ DBG_DUMP_TM_REQUEST_FRAME(mf);
|
|
|
+
|
|
|
+ if (mpt_send_handshake_request(ioc->TaskCtx, ioc,
|
|
|
+ sizeof(SCSITaskMgmt_t), (u32 *)mf, NO_SLEEP)) {
|
|
|
+ mpt_free_msg_frame(ioc, mf);
|
|
|
+ dfailprintk((MYIOC_s_WARN_FMT "%s, tm handshake failed @%d!!\n",
|
|
|
+ ioc->name,__FUNCTION__, __LINE__));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mptsas_target_reset_queue
|
|
|
+ *
|
|
|
+ * Receive request for TARGET_RESET after recieving an firmware
|
|
|
+ * event NOT_RESPONDING_EVENT, then put command in link list
|
|
|
+ * and queue if task_queue already in use.
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ * @sas_event_data
|
|
|
+ *
|
|
|
+ **/
|
|
|
static void
|
|
|
-mptsas_target_reset(MPT_ADAPTER *ioc, VirtTarget * vtarget)
|
|
|
+mptsas_target_reset_queue(MPT_ADAPTER *ioc,
|
|
|
+ EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
|
|
|
{
|
|
|
- MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
|
|
|
+ MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
|
|
|
+ VirtTarget *vtarget = NULL;
|
|
|
+ struct mptsas_target_reset_event *target_reset_list;
|
|
|
+ u8 id, channel;
|
|
|
|
|
|
- if (mptscsih_TMHandler(hd,
|
|
|
- MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
|
|
|
- vtarget->channel, vtarget->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->id);
|
|
|
+ id = sas_event_data->TargetID;
|
|
|
+ channel = sas_event_data->Bus;
|
|
|
+
|
|
|
+ if (!(vtarget = mptsas_find_vtarget(ioc, channel, id)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ vtarget->deleted = 1; /* block IO */
|
|
|
+
|
|
|
+ target_reset_list = kzalloc(sizeof(*target_reset_list),
|
|
|
+ GFP_ATOMIC);
|
|
|
+ if (!target_reset_list) {
|
|
|
+ dfailprintk((MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
|
|
|
+ ioc->name,__FUNCTION__, __LINE__));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&target_reset_list->sas_event_data, sas_event_data,
|
|
|
+ sizeof(*sas_event_data));
|
|
|
+ list_add_tail(&target_reset_list->list, &hd->target_reset_list);
|
|
|
+
|
|
|
+ if (hd->resetPending)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (mptsas_target_reset(ioc, channel, id)) {
|
|
|
+ target_reset_list->target_reset_issued = 1;
|
|
|
+ hd->resetPending = 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mptsas_dev_reset_complete
|
|
|
+ *
|
|
|
+ * Completion for TARGET_RESET after NOT_RESPONDING_EVENT,
|
|
|
+ * enable work queue to finish off removing device from upper layers.
|
|
|
+ * then send next TARGET_RESET in the queue.
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ *
|
|
|
+ **/
|
|
|
+static void
|
|
|
+mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
|
|
|
+{
|
|
|
+ MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
|
|
|
+ struct list_head *head = &hd->target_reset_list;
|
|
|
+ struct mptsas_target_reset_event *target_reset_list;
|
|
|
+ struct mptsas_hotplug_event *ev;
|
|
|
+ EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
|
|
|
+ u8 id, channel;
|
|
|
+ __le64 sas_address;
|
|
|
+
|
|
|
+ if (list_empty(head))
|
|
|
+ return;
|
|
|
+
|
|
|
+ target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, list);
|
|
|
+
|
|
|
+ sas_event_data = &target_reset_list->sas_event_data;
|
|
|
+ id = sas_event_data->TargetID;
|
|
|
+ channel = sas_event_data->Bus;
|
|
|
+ hd->resetPending = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * retry target reset
|
|
|
+ */
|
|
|
+ if (!target_reset_list->target_reset_issued) {
|
|
|
+ if (mptsas_target_reset(ioc, channel, id)) {
|
|
|
+ target_reset_list->target_reset_issued = 1;
|
|
|
+ hd->resetPending = 1;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * enable work queue to remove device from upper layers
|
|
|
+ */
|
|
|
+ list_del(&target_reset_list->list);
|
|
|
+
|
|
|
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
|
|
|
+ if (!ev) {
|
|
|
+ dfailprintk((MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
|
|
|
+ ioc->name,__FUNCTION__, __LINE__));
|
|
|
+ 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 = channel;
|
|
|
+ ev->id =id;
|
|
|
+ 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 = le32_to_cpu(sas_event_data->DeviceInfo);
|
|
|
+ ev->event_type = MPTSAS_DEL_DEVICE;
|
|
|
+ schedule_work(&ev->work);
|
|
|
+ kfree(target_reset_list);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * issue target reset to next device in the queue
|
|
|
+ */
|
|
|
+
|
|
|
+ head = &hd->target_reset_list;
|
|
|
+ if (list_empty(head))
|
|
|
+ return;
|
|
|
+
|
|
|
+ target_reset_list = list_entry(head->next, struct mptsas_target_reset_event,
|
|
|
+ list);
|
|
|
+
|
|
|
+ sas_event_data = &target_reset_list->sas_event_data;
|
|
|
+ id = sas_event_data->TargetID;
|
|
|
+ channel = sas_event_data->Bus;
|
|
|
+
|
|
|
+ if (mptsas_target_reset(ioc, channel, id)) {
|
|
|
+ target_reset_list->target_reset_issued = 1;
|
|
|
+ hd->resetPending = 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mptsas_taskmgmt_complete
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ * @mf
|
|
|
+ * @mr
|
|
|
+ *
|
|
|
+ **/
|
|
|
+static int
|
|
|
+mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
|
|
|
+{
|
|
|
+ mptsas_dev_reset_complete(ioc);
|
|
|
+ return mptscsih_taskmgmt_complete(ioc, mf, mr);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mptscsih_ioc_reset
|
|
|
+ *
|
|
|
+ * @ioc
|
|
|
+ * @reset_phase
|
|
|
+ *
|
|
|
+ **/
|
|
|
+static int
|
|
|
+mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
|
|
|
+{
|
|
|
+ MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
|
|
|
+ struct mptsas_target_reset_event *target_reset_list, *n;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = mptscsih_ioc_reset(ioc, reset_phase);
|
|
|
+
|
|
|
+ if (ioc->bus_type != SAS)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (reset_phase != MPT_IOC_POST_RESET)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (!hd || !hd->ioc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (list_empty(&hd->target_reset_list))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* flush the target_reset_list */
|
|
|
+ list_for_each_entry_safe(target_reset_list, n,
|
|
|
+ &hd->target_reset_list, list) {
|
|
|
+ list_del(&target_reset_list->list);
|
|
|
+ kfree(target_reset_list);
|
|
|
}
|
|
|
+
|
|
|
+ out:
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -1885,8 +2142,6 @@ 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;
|
|
@@ -1903,25 +2158,6 @@ mptsas_delete_expander_phys(MPT_ADAPTER *ioc)
|
|
|
(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
|
|
|
*/
|
|
@@ -2319,9 +2555,6 @@ mptsas_hotplug_work(struct work_struct *work)
|
|
|
ev->phys_disk_num;
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
- vtarget->deleted = 1;
|
|
|
- mptsas_target_reset(ioc, vtarget);
|
|
|
}
|
|
|
|
|
|
if (phy_info->attached.device_info &
|
|
@@ -2474,8 +2707,6 @@ mptsas_hotplug_work(struct work_struct *work)
|
|
|
printk(MYIOC_s_INFO_FMT
|
|
|
"removing raid volume, channel %d, id %d\n",
|
|
|
ioc->name, MPTSAS_RAID_CHANNEL, ev->id);
|
|
|
- vdevice->vtarget->deleted = 1;
|
|
|
- mptsas_target_reset(ioc, vdevice->vtarget);
|
|
|
vdevice = sdev->hostdata;
|
|
|
scsi_remove_device(sdev);
|
|
|
scsi_device_put(sdev);
|
|
@@ -2509,8 +2740,12 @@ mptsas_send_sas_event(MPT_ADAPTER *ioc,
|
|
|
return;
|
|
|
|
|
|
switch (sas_event_data->ReasonCode) {
|
|
|
- case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
|
|
|
case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING:
|
|
|
+
|
|
|
+ mptsas_target_reset_queue(ioc, sas_event_data);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
|
|
|
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
|
|
|
if (!ev) {
|
|
|
printk(KERN_WARNING "mptsas: lost hotplug event\n");
|
|
@@ -2871,8 +3106,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
sh->sg_tablesize = numSGE;
|
|
|
}
|
|
|
|
|
|
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
|
|
|
-
|
|
|
hd = (MPT_SCSI_HOST *) sh->hostdata;
|
|
|
hd->ioc = ioc;
|
|
|
|
|
@@ -2912,15 +3145,17 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
|
|
|
ioc->sas_data.ptClear = mpt_pt_clear;
|
|
|
|
|
|
+ init_waitqueue_head(&hd->scandv_waitq);
|
|
|
+ hd->scandv_wait_done = 0;
|
|
|
+ hd->last_queue_full = 0;
|
|
|
+ INIT_LIST_HEAD(&hd->target_reset_list);
|
|
|
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
|
|
|
+
|
|
|
if (ioc->sas_data.ptClear==1) {
|
|
|
mptbase_sas_persist_operation(
|
|
|
ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT);
|
|
|
}
|
|
|
|
|
|
- init_waitqueue_head(&hd->scandv_waitq);
|
|
|
- hd->scandv_wait_done = 0;
|
|
|
- hd->last_queue_full = 0;
|
|
|
-
|
|
|
error = scsi_add_host(sh, &ioc->pcidev->dev);
|
|
|
if (error) {
|
|
|
dprintk((KERN_ERR MYNAM
|
|
@@ -2999,7 +3234,7 @@ mptsas_init(void)
|
|
|
return -ENODEV;
|
|
|
|
|
|
mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER);
|
|
|
- mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER);
|
|
|
+ mptsasTaskCtx = mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER);
|
|
|
mptsasInternalCtx =
|
|
|
mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER);
|
|
|
mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER);
|
|
@@ -3009,7 +3244,7 @@ mptsas_init(void)
|
|
|
": Registered for IOC event notifications\n"));
|
|
|
}
|
|
|
|
|
|
- if (mpt_reset_register(mptsasDoneCtx, mptscsih_ioc_reset) == 0) {
|
|
|
+ if (mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset) == 0) {
|
|
|
dprintk((KERN_INFO MYNAM
|
|
|
": Registered for IOC reset notifications\n"));
|
|
|
}
|