Commit 428c4913 authored by Guilherme G. Piccoli's avatar Guilherme G. Piccoli Committed by David S. Miller

net: ena: Add PCI shutdown handler to allow safe kexec

Currently ENA only provides the PCI remove() handler, used during rmmod
for example. This is not called on shutdown/kexec path; we are potentially
creating a failure scenario on kexec:

(a) Kexec is triggered, no shutdown() / remove() handler is called for ENA;
instead pci_device_shutdown() clears the master bit of the PCI device,
stopping all DMA transactions;

(b) Kexec reboot happens and the device gets enabled again, likely having
its FW with that DMA transaction buffered; then it may trigger the (now
invalid) memory operation in the new kernel, corrupting kernel memory area.

This patch aims to prevent this, by implementing a shutdown() handler
quite similar to the remove() one - the difference being the handling
of the netdev, which is unregistered on remove(), but following the
convention observed in other drivers, it's only detached on shutdown().

This prevents an odd issue in AWS Nitro instances, in which after the 2nd
kexec the next one will fail with an initrd corruption, caused by a wild
DMA write to invalid kernel memory. The lspci output for the adapter
present in my instance is:

00:05.0 Ethernet controller [0200]: Amazon.com, Inc. Elastic Network
Adapter (ENA) [1d0f:ec20]
Suggested-by: default avatarGavin Shan <gshan@redhat.com>
Signed-off-by: default avatarGuilherme G. Piccoli <gpiccoli@canonical.com>
Acked-by: default avatarSameeh Jubran <sameehj@amazon.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c085dbfb
...@@ -4336,13 +4336,15 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -4336,13 +4336,15 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/*****************************************************************************/ /*****************************************************************************/
/* ena_remove - Device Removal Routine /* __ena_shutoff - Helper used in both PCI remove/shutdown routines
* @pdev: PCI device information struct * @pdev: PCI device information struct
* @shutdown: Is it a shutdown operation? If false, means it is a removal
* *
* ena_remove is called by the PCI subsystem to alert the driver * __ena_shutoff is a helper routine that does the real work on shutdown and
* that it should release a PCI device. * removal paths; the difference between those paths is with regards to whether
* dettach or unregister the netdevice.
*/ */
static void ena_remove(struct pci_dev *pdev) static void __ena_shutoff(struct pci_dev *pdev, bool shutdown)
{ {
struct ena_adapter *adapter = pci_get_drvdata(pdev); struct ena_adapter *adapter = pci_get_drvdata(pdev);
struct ena_com_dev *ena_dev; struct ena_com_dev *ena_dev;
...@@ -4361,13 +4363,17 @@ static void ena_remove(struct pci_dev *pdev) ...@@ -4361,13 +4363,17 @@ static void ena_remove(struct pci_dev *pdev)
cancel_work_sync(&adapter->reset_task); cancel_work_sync(&adapter->reset_task);
rtnl_lock(); rtnl_lock(); /* lock released inside the below if-else block */
ena_destroy_device(adapter, true); ena_destroy_device(adapter, true);
if (shutdown) {
netif_device_detach(netdev);
dev_close(netdev);
rtnl_unlock();
} else {
rtnl_unlock(); rtnl_unlock();
unregister_netdev(netdev); unregister_netdev(netdev);
free_netdev(netdev); free_netdev(netdev);
}
ena_com_rss_destroy(ena_dev); ena_com_rss_destroy(ena_dev);
...@@ -4382,6 +4388,30 @@ static void ena_remove(struct pci_dev *pdev) ...@@ -4382,6 +4388,30 @@ static void ena_remove(struct pci_dev *pdev)
vfree(ena_dev); vfree(ena_dev);
} }
/* ena_remove - Device Removal Routine
* @pdev: PCI device information struct
*
* ena_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device.
*/
static void ena_remove(struct pci_dev *pdev)
{
__ena_shutoff(pdev, false);
}
/* ena_shutdown - Device Shutdown Routine
* @pdev: PCI device information struct
*
* ena_shutdown is called by the PCI subsystem to alert the driver that
* a shutdown/reboot (or kexec) is happening and device must be disabled.
*/
static void ena_shutdown(struct pci_dev *pdev)
{
__ena_shutoff(pdev, true);
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* ena_suspend - PM suspend callback /* ena_suspend - PM suspend callback
* @pdev: PCI device information struct * @pdev: PCI device information struct
...@@ -4431,6 +4461,7 @@ static struct pci_driver ena_pci_driver = { ...@@ -4431,6 +4461,7 @@ static struct pci_driver ena_pci_driver = {
.id_table = ena_pci_tbl, .id_table = ena_pci_tbl,
.probe = ena_probe, .probe = ena_probe,
.remove = ena_remove, .remove = ena_remove,
.shutdown = ena_shutdown,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = ena_suspend, .suspend = ena_suspend,
.resume = ena_resume, .resume = ena_resume,
......
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