|
@@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * mpt_fault_reset_work - work performed on workq after ioc fault
|
|
|
+ * @work: input argument, used to derive ioc
|
|
|
+ *
|
|
|
+**/
|
|
|
+static void
|
|
|
+mpt_fault_reset_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ MPT_ADAPTER *ioc =
|
|
|
+ container_of(work, MPT_ADAPTER, fault_reset_work.work);
|
|
|
+ u32 ioc_raw_state;
|
|
|
+ int rc;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (ioc->diagPending || !ioc->active)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ioc_raw_state = mpt_GetIocState(ioc, 0);
|
|
|
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
|
|
|
+ printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
|
|
|
+ ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
|
|
|
+ printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
|
|
|
+ ioc->name, __FUNCTION__);
|
|
|
+ rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
|
|
|
+ printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
|
|
|
+ __FUNCTION__, (rc == 0) ? "success" : "failed");
|
|
|
+ ioc_raw_state = mpt_GetIocState(ioc, 0);
|
|
|
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
|
|
|
+ printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
|
|
|
+ "reset (%04xh)\n", ioc->name, ioc_raw_state &
|
|
|
+ MPI_DOORBELL_DATA_MASK);
|
|
|
+ }
|
|
|
+
|
|
|
+ out:
|
|
|
+ /*
|
|
|
+ * Take turns polling alternate controller
|
|
|
+ */
|
|
|
+ if (ioc->alt_ioc)
|
|
|
+ ioc = ioc->alt_ioc;
|
|
|
+
|
|
|
+ /* rearm the timer */
|
|
|
+ spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
|
|
|
+ if (ioc->reset_work_q)
|
|
|
+ queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
|
|
|
+ msecs_to_jiffies(MPT_POLLING_INTERVAL));
|
|
|
+ spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* Process turbo (context) reply...
|
|
|
*/
|
|
@@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
/* Find lookup slot. */
|
|
|
INIT_LIST_HEAD(&ioc->list);
|
|
|
|
|
|
+
|
|
|
+ /* Initialize workqueue */
|
|
|
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
|
|
|
+ spin_lock_init(&ioc->fault_reset_work_lock);
|
|
|
+
|
|
|
+ snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id);
|
|
|
+ ioc->reset_work_q =
|
|
|
+ create_singlethread_workqueue(ioc->reset_work_q_name);
|
|
|
+ if (!ioc->reset_work_q) {
|
|
|
+ printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
|
|
|
+ ioc->name);
|
|
|
+ pci_release_selected_regions(pdev, ioc->bars);
|
|
|
+ kfree(ioc);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
|
|
|
ioc->name, &ioc->facts, &ioc->pfacts[0]));
|
|
|
|
|
@@ -1722,6 +1787,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
iounmap(ioc->memmap);
|
|
|
if (r != -5)
|
|
|
pci_release_selected_regions(pdev, ioc->bars);
|
|
|
+
|
|
|
+ destroy_workqueue(ioc->reset_work_q);
|
|
|
+ ioc->reset_work_q = NULL;
|
|
|
+
|
|
|
kfree(ioc);
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
return r;
|
|
@@ -1754,6 +1823,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+ if (!ioc->alt_ioc)
|
|
|
+ queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
|
|
|
+ msecs_to_jiffies(MPT_POLLING_INTERVAL));
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1769,6 +1842,19 @@ mpt_detach(struct pci_dev *pdev)
|
|
|
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
|
|
|
char pname[32];
|
|
|
u8 cb_idx;
|
|
|
+ unsigned long flags;
|
|
|
+ struct workqueue_struct *wq;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Stop polling ioc for fault condition
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
|
|
|
+ wq = ioc->reset_work_q;
|
|
|
+ ioc->reset_work_q = NULL;
|
|
|
+ spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
|
|
|
+ cancel_delayed_work(&ioc->fault_reset_work);
|
|
|
+ destroy_workqueue(wq);
|
|
|
+
|
|
|
|
|
|
sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
|
|
|
remove_proc_entry(pname, NULL);
|