summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/platforms/pseries/eeh_driver.c91
1 files changed, 70 insertions, 21 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 37c2cf743133..a3fefb61097c 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -25,6 +25,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <asm/eeh.h>
#include <asm/eeh_event.h>
@@ -47,6 +48,41 @@ static inline const char *eeh_pcid_name(struct pci_dev *pdev)
return "";
}
+/**
+ * eeh_pcid_get - Get the PCI device driver
+ * @pdev: PCI device
+ *
+ * The function is used to retrieve the PCI device driver for
+ * the indicated PCI device. Besides, we will increase the reference
+ * of the PCI device driver to prevent that being unloaded on
+ * the fly. Otherwise, kernel crash would be seen.
+ */
+static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
+{
+ if (!pdev || !pdev->driver)
+ return NULL;
+
+ if (!try_module_get(pdev->driver->driver.owner))
+ return NULL;
+
+ return pdev->driver;
+}
+
+/**
+ * eeh_pcid_put - Dereference on the PCI device driver
+ * @pdev: PCI device
+ *
+ * The function is called to do dereference on the PCI device
+ * driver of the indicated PCI device.
+ */
+static inline void eeh_pcid_put(struct pci_dev *pdev)
+{
+ if (!pdev || !pdev->driver)
+ return;
+
+ module_put(pdev->driver->driver.owner);
+}
+
#if 0
static void print_device_node_tree(struct pci_dn *pdn, int dent)
{
@@ -128,23 +164,24 @@ static void *eeh_report_error(void *data, void *userdata)
struct eeh_dev *edev = (struct eeh_dev *)data;
struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver = dev->driver;
+ struct pci_driver *driver;
/* We might not have the associated PCI device,
* then we should continue for next one.
*/
if (!dev) return NULL;
-
dev->error_state = pci_channel_io_frozen;
- if (!driver)
- return NULL;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return NULL;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected)
+ !driver->err_handler->error_detected) {
+ eeh_pcid_put(dev);
return NULL;
+ }
rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
@@ -152,6 +189,7 @@ static void *eeh_report_error(void *data, void *userdata)
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+ eeh_pcid_put(dev);
return NULL;
}
@@ -171,12 +209,14 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata)
enum pci_ers_result rc, *res = userdata;
struct pci_driver *driver;
- if (!dev) return NULL;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return NULL;
- if (!(driver = dev->driver) ||
- !driver->err_handler ||
- !driver->err_handler->mmio_enabled)
+ if (!driver->err_handler ||
+ !driver->err_handler->mmio_enabled) {
+ eeh_pcid_put(dev);
return NULL;
+ }
rc = driver->err_handler->mmio_enabled(dev);
@@ -184,6 +224,7 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata)
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+ eeh_pcid_put(dev);
return NULL;
}
@@ -204,16 +245,19 @@ static void *eeh_report_reset(void *data, void *userdata)
enum pci_ers_result rc, *res = userdata;
struct pci_driver *driver;
- if (!dev || !(driver = dev->driver))
- return NULL;
-
+ if (!dev) return NULL;
dev->error_state = pci_channel_io_normal;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return NULL;
+
eeh_enable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->slot_reset)
+ !driver->err_handler->slot_reset) {
+ eeh_pcid_put(dev);
return NULL;
+ }
rc = driver->err_handler->slot_reset(dev);
if ((*res == PCI_ERS_RESULT_NONE) ||
@@ -221,6 +265,7 @@ static void *eeh_report_reset(void *data, void *userdata)
if (*res == PCI_ERS_RESULT_DISCONNECT &&
rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+ eeh_pcid_put(dev);
return NULL;
}
@@ -240,20 +285,22 @@ static void *eeh_report_resume(void *data, void *userdata)
struct pci_driver *driver;
if (!dev) return NULL;
-
dev->error_state = pci_channel_io_normal;
- if (!(driver = dev->driver))
- return NULL;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return NULL;
eeh_enable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->resume)
+ !driver->err_handler->resume) {
+ eeh_pcid_put(dev);
return NULL;
+ }
driver->err_handler->resume(dev);
+ eeh_pcid_put(dev);
return NULL;
}
@@ -272,20 +319,22 @@ static void *eeh_report_failure(void *data, void *userdata)
struct pci_driver *driver;
if (!dev) return NULL;
-
dev->error_state = pci_channel_io_perm_failure;
- if (!(driver = dev->driver))
- return NULL;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return NULL;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected)
+ !driver->err_handler->error_detected) {
+ eeh_pcid_put(dev);
return NULL;
+ }
driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
+ eeh_pcid_put(dev);
return NULL;
}