aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/pseries/eeh_driver.c95
1 files changed, 74 insertions, 21 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index baf92cd9dfa..041e28d0e70 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)
{
@@ -126,18 +162,20 @@ static void eeh_enable_irq(struct pci_dev *dev)
static int eeh_report_error(struct pci_dev *dev, void *userdata)
{
enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver = dev->driver;
+ struct pci_driver *driver;
dev->error_state = pci_channel_io_frozen;
- if (!driver)
- return 0;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return 0;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected)
+ !driver->err_handler->error_detected) {
+ eeh_pcid_put(dev);
return 0;
+ }
rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
@@ -145,6 +183,7 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+ eeh_pcid_put(dev);
return 0;
}
@@ -160,12 +199,16 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
{
enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver = dev->driver;
+ struct pci_driver *driver;
+
+ driver = eeh_pcid_get(dev);
+ if (!driver) return 0;
- if (!driver ||
- !driver->err_handler ||
- !driver->err_handler->mmio_enabled)
+ if (!driver->err_handler ||
+ !driver->err_handler->mmio_enabled) {
+ eeh_pcid_put(dev);
return 0;
+ }
rc = driver->err_handler->mmio_enabled(dev);
@@ -173,6 +216,7 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+ eeh_pcid_put(dev);
return 0;
}
@@ -189,18 +233,20 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
static int eeh_report_reset(struct pci_dev *dev, void *userdata)
{
enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver = dev->driver;
-
- if (!driver)
- return 0;
+ struct pci_driver *driver;
dev->error_state = pci_channel_io_normal;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return 0;
+
eeh_enable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->slot_reset)
+ !driver->err_handler->slot_reset) {
+ eeh_pcid_put(dev);
return 0;
+ }
rc = driver->err_handler->slot_reset(dev);
if ((*res == PCI_ERS_RESULT_NONE) ||
@@ -208,6 +254,7 @@ static int eeh_report_reset(struct pci_dev *dev, void *userdata)
if (*res == PCI_ERS_RESULT_DISCONNECT &&
rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+ eeh_pcid_put(dev);
return 0;
}
@@ -222,21 +269,24 @@ static int eeh_report_reset(struct pci_dev *dev, void *userdata)
*/
static int eeh_report_resume(struct pci_dev *dev, void *userdata)
{
- struct pci_driver *driver = dev->driver;
+ struct pci_driver *driver;
dev->error_state = pci_channel_io_normal;
- if (!driver)
- return 0;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return 0;
eeh_enable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->resume)
+ !driver->err_handler->resume) {
+ eeh_pcid_put(dev);
return 0;
+ }
driver->err_handler->resume(dev);
+ eeh_pcid_put(dev);
return 0;
}
@@ -250,21 +300,24 @@ static int eeh_report_resume(struct pci_dev *dev, void *userdata)
*/
static int eeh_report_failure(struct pci_dev *dev, void *userdata)
{
- struct pci_driver *driver = dev->driver;
+ struct pci_driver *driver;
dev->error_state = pci_channel_io_perm_failure;
- if (!driver)
- return 0;
+ driver = eeh_pcid_get(dev);
+ if (!driver) return 0;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected)
+ !driver->err_handler->error_detected) {
+ eeh_pcid_put(dev);
return 0;
+ }
driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
+ eeh_pcid_put(dev);
return 0;
}