Commit ac601978 authored by Sumit Semwal's avatar Sumit Semwal Committed by Greg Kroah-Hartman

serial: 8250_pci: Detach low-level driver during PCI error recovery

From: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com>

[ Upstream commit f209fa03 ]

During a PCI error recovery, like the ones provoked by EEH in the ppc64
platform, all IO to the device must be blocked while the recovery is
completed.  Current 8250_pci implementation only suspends the port
instead of detaching it, which doesn't prevent incoming accesses like
TIOCMGET and TIOCMSET calls from reaching the device.  Those end up
racing with the EEH recovery, crashing it.  Similar races were also
observed when opening the device and when shutting it down during
recovery.

This patch implements a more robust IO blockage for the 8250_pci
recovery by unregistering the port at the beginning of the procedure and
re-adding it afterwards.  Since the port is detached from the uart
layer, we can be sure that no request will make through to the device
during recovery.  This is similar to the solution used by the JSM serial
driver.

I thank Peter Hurley <peter@hurleysoftware.com> for valuable input on
this one over one year ago.
Signed-off-by: default avatarGabriel Krisman Bertazi <krisman@linux.vnet.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSumit Semwal <sumit.semwal@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b8687d83
...@@ -57,6 +57,7 @@ struct serial_private { ...@@ -57,6 +57,7 @@ struct serial_private {
unsigned int nr; unsigned int nr;
void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES];
struct pci_serial_quirk *quirk; struct pci_serial_quirk *quirk;
const struct pciserial_board *board;
int line[0]; int line[0];
}; };
...@@ -4058,6 +4059,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) ...@@ -4058,6 +4059,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
} }
} }
priv->nr = i; priv->nr = i;
priv->board = board;
return priv; return priv;
err_deinit: err_deinit:
...@@ -4068,7 +4070,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) ...@@ -4068,7 +4070,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
} }
EXPORT_SYMBOL_GPL(pciserial_init_ports); EXPORT_SYMBOL_GPL(pciserial_init_ports);
void pciserial_remove_ports(struct serial_private *priv) void pciserial_detach_ports(struct serial_private *priv)
{ {
struct pci_serial_quirk *quirk; struct pci_serial_quirk *quirk;
int i; int i;
...@@ -4088,7 +4090,11 @@ void pciserial_remove_ports(struct serial_private *priv) ...@@ -4088,7 +4090,11 @@ void pciserial_remove_ports(struct serial_private *priv)
quirk = find_quirk(priv->dev); quirk = find_quirk(priv->dev);
if (quirk->exit) if (quirk->exit)
quirk->exit(priv->dev); quirk->exit(priv->dev);
}
void pciserial_remove_ports(struct serial_private *priv)
{
pciserial_detach_ports(priv);
kfree(priv); kfree(priv);
} }
EXPORT_SYMBOL_GPL(pciserial_remove_ports); EXPORT_SYMBOL_GPL(pciserial_remove_ports);
...@@ -5819,7 +5825,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, ...@@ -5819,7 +5825,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev,
return PCI_ERS_RESULT_DISCONNECT; return PCI_ERS_RESULT_DISCONNECT;
if (priv) if (priv)
pciserial_suspend_ports(priv); pciserial_detach_ports(priv);
pci_disable_device(dev); pci_disable_device(dev);
...@@ -5844,9 +5850,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) ...@@ -5844,9 +5850,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev)
static void serial8250_io_resume(struct pci_dev *dev) static void serial8250_io_resume(struct pci_dev *dev)
{ {
struct serial_private *priv = pci_get_drvdata(dev); struct serial_private *priv = pci_get_drvdata(dev);
const struct pciserial_board *board;
if (priv) if (!priv)
pciserial_resume_ports(priv); return;
board = priv->board;
kfree(priv);
priv = pciserial_init_ports(dev, board);
if (!IS_ERR(priv)) {
pci_set_drvdata(dev, priv);
}
} }
static const struct pci_error_handlers serial8250_err_handler = { static const struct pci_error_handlers serial8250_err_handler = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment