Commit 635096a8 authored by Dexuan Cui's avatar Dexuan Cui Committed by David S. Miller

net: mana: Support hibernation and kexec

Implement the suspend/resume/shutdown callbacks for hibernation/kexec.

Add mana_gd_setup() and mana_gd_cleanup() for some common code, and
use them in the mand_gd_* callbacks.

Reuse mana_probe/remove() for the hibernation path.
Signed-off-by: default avatarDexuan Cui <decui@microsoft.com>
Reviewed-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 62ea8b77
...@@ -1258,6 +1258,52 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev) ...@@ -1258,6 +1258,52 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev)
gc->irq_contexts = NULL; gc->irq_contexts = NULL;
} }
static int mana_gd_setup(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
int err;
mana_gd_init_registers(pdev);
mana_smc_init(&gc->shm_channel, gc->dev, gc->shm_base);
err = mana_gd_setup_irqs(pdev);
if (err)
return err;
err = mana_hwc_create_channel(gc);
if (err)
goto remove_irq;
err = mana_gd_verify_vf_version(pdev);
if (err)
goto destroy_hwc;
err = mana_gd_query_max_resources(pdev);
if (err)
goto destroy_hwc;
err = mana_gd_detect_devices(pdev);
if (err)
goto destroy_hwc;
return 0;
destroy_hwc:
mana_hwc_destroy_channel(gc);
remove_irq:
mana_gd_remove_irqs(pdev);
return err;
}
static void mana_gd_cleanup(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
mana_hwc_destroy_channel(gc);
mana_gd_remove_irqs(pdev);
}
static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
struct gdma_context *gc; struct gdma_context *gc;
...@@ -1287,6 +1333,9 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1287,6 +1333,9 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!gc) if (!gc)
goto release_region; goto release_region;
mutex_init(&gc->eq_test_event_mutex);
pci_set_drvdata(pdev, gc);
bar0_va = pci_iomap(pdev, bar, 0); bar0_va = pci_iomap(pdev, bar, 0);
if (!bar0_va) if (!bar0_va)
goto free_gc; goto free_gc;
...@@ -1294,47 +1343,23 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1294,47 +1343,23 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
gc->bar0_va = bar0_va; gc->bar0_va = bar0_va;
gc->dev = &pdev->dev; gc->dev = &pdev->dev;
pci_set_drvdata(pdev, gc);
mana_gd_init_registers(pdev);
mana_smc_init(&gc->shm_channel, gc->dev, gc->shm_base); err = mana_gd_setup(pdev);
err = mana_gd_setup_irqs(pdev);
if (err) if (err)
goto unmap_bar; goto unmap_bar;
mutex_init(&gc->eq_test_event_mutex); err = mana_probe(&gc->mana, false);
err = mana_hwc_create_channel(gc);
if (err) if (err)
goto remove_irq; goto cleanup_gd;
err = mana_gd_verify_vf_version(pdev);
if (err)
goto remove_irq;
err = mana_gd_query_max_resources(pdev);
if (err)
goto remove_irq;
err = mana_gd_detect_devices(pdev);
if (err)
goto remove_irq;
err = mana_probe(&gc->mana);
if (err)
goto clean_up_gdma;
return 0; return 0;
clean_up_gdma: cleanup_gd:
mana_hwc_destroy_channel(gc); mana_gd_cleanup(pdev);
remove_irq:
mana_gd_remove_irqs(pdev);
unmap_bar: unmap_bar:
pci_iounmap(pdev, bar0_va); pci_iounmap(pdev, bar0_va);
free_gc: free_gc:
pci_set_drvdata(pdev, NULL);
vfree(gc); vfree(gc);
release_region: release_region:
pci_release_regions(pdev); pci_release_regions(pdev);
...@@ -1349,11 +1374,9 @@ static void mana_gd_remove(struct pci_dev *pdev) ...@@ -1349,11 +1374,9 @@ static void mana_gd_remove(struct pci_dev *pdev)
{ {
struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_context *gc = pci_get_drvdata(pdev);
mana_remove(&gc->mana); mana_remove(&gc->mana, false);
mana_hwc_destroy_channel(gc); mana_gd_cleanup(pdev);
mana_gd_remove_irqs(pdev);
pci_iounmap(pdev, gc->bar0_va); pci_iounmap(pdev, gc->bar0_va);
...@@ -1364,6 +1387,52 @@ static void mana_gd_remove(struct pci_dev *pdev) ...@@ -1364,6 +1387,52 @@ static void mana_gd_remove(struct pci_dev *pdev)
pci_disable_device(pdev); pci_disable_device(pdev);
} }
/* The 'state' parameter is not used. */
static int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
mana_remove(&gc->mana, true);
mana_gd_cleanup(pdev);
return 0;
}
/* In case the NIC hardware stops working, the suspend and resume callbacks will
* fail -- if this happens, it's safer to just report an error than try to undo
* what has been done.
*/
static int mana_gd_resume(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
int err;
err = mana_gd_setup(pdev);
if (err)
return err;
err = mana_probe(&gc->mana, true);
if (err)
return err;
return 0;
}
/* Quiesce the device for kexec. This is also called upon reboot/shutdown. */
static void mana_gd_shutdown(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
dev_info(&pdev->dev, "Shutdown was calledd\n");
mana_remove(&gc->mana, true);
mana_gd_cleanup(pdev);
pci_disable_device(pdev);
}
#ifndef PCI_VENDOR_ID_MICROSOFT #ifndef PCI_VENDOR_ID_MICROSOFT
#define PCI_VENDOR_ID_MICROSOFT 0x1414 #define PCI_VENDOR_ID_MICROSOFT 0x1414
#endif #endif
...@@ -1378,6 +1447,9 @@ static struct pci_driver mana_driver = { ...@@ -1378,6 +1447,9 @@ static struct pci_driver mana_driver = {
.id_table = mana_id_table, .id_table = mana_id_table,
.probe = mana_gd_probe, .probe = mana_gd_probe,
.remove = mana_gd_remove, .remove = mana_gd_remove,
.suspend = mana_gd_suspend,
.resume = mana_gd_resume,
.shutdown = mana_gd_shutdown,
}; };
module_pci_driver(mana_driver); module_pci_driver(mana_driver);
......
...@@ -374,8 +374,8 @@ int mana_alloc_queues(struct net_device *ndev); ...@@ -374,8 +374,8 @@ int mana_alloc_queues(struct net_device *ndev);
int mana_attach(struct net_device *ndev); int mana_attach(struct net_device *ndev);
int mana_detach(struct net_device *ndev, bool from_close); int mana_detach(struct net_device *ndev, bool from_close);
int mana_probe(struct gdma_dev *gd); int mana_probe(struct gdma_dev *gd, bool resuming);
void mana_remove(struct gdma_dev *gd); void mana_remove(struct gdma_dev *gd, bool suspending);
extern const struct ethtool_ops mana_ethtool_ops; extern const struct ethtool_ops mana_ethtool_ops;
......
...@@ -1828,11 +1828,12 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, ...@@ -1828,11 +1828,12 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
return err; return err;
} }
int mana_probe(struct gdma_dev *gd) int mana_probe(struct gdma_dev *gd, bool resuming)
{ {
struct gdma_context *gc = gd->gdma_context; struct gdma_context *gc = gd->gdma_context;
struct mana_context *ac = gd->driver_data;
struct device *dev = gc->dev; struct device *dev = gc->dev;
struct mana_context *ac; u16 num_ports = 0;
int err; int err;
int i; int i;
...@@ -1844,44 +1845,70 @@ int mana_probe(struct gdma_dev *gd) ...@@ -1844,44 +1845,70 @@ int mana_probe(struct gdma_dev *gd)
if (err) if (err)
return err; return err;
ac = kzalloc(sizeof(*ac), GFP_KERNEL); if (!resuming) {
if (!ac) ac = kzalloc(sizeof(*ac), GFP_KERNEL);
return -ENOMEM; if (!ac)
return -ENOMEM;
ac->gdma_dev = gd; ac->gdma_dev = gd;
ac->num_ports = 1; gd->driver_data = ac;
gd->driver_data = ac; }
err = mana_create_eq(ac); err = mana_create_eq(ac);
if (err) if (err)
goto out; goto out;
err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION, err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
MANA_MICRO_VERSION, &ac->num_ports); MANA_MICRO_VERSION, &num_ports);
if (err) if (err)
goto out; goto out;
if (!resuming) {
ac->num_ports = num_ports;
} else {
if (ac->num_ports != num_ports) {
dev_err(dev, "The number of vPorts changed: %d->%d\n",
ac->num_ports, num_ports);
err = -EPROTO;
goto out;
}
}
if (ac->num_ports == 0)
dev_err(dev, "Failed to detect any vPort\n");
if (ac->num_ports > MAX_PORTS_IN_MANA_DEV) if (ac->num_ports > MAX_PORTS_IN_MANA_DEV)
ac->num_ports = MAX_PORTS_IN_MANA_DEV; ac->num_ports = MAX_PORTS_IN_MANA_DEV;
for (i = 0; i < ac->num_ports; i++) { if (!resuming) {
err = mana_probe_port(ac, i, &ac->ports[i]); for (i = 0; i < ac->num_ports; i++) {
if (err) err = mana_probe_port(ac, i, &ac->ports[i]);
break; if (err)
break;
}
} else {
for (i = 0; i < ac->num_ports; i++) {
rtnl_lock();
err = mana_attach(ac->ports[i]);
rtnl_unlock();
if (err)
break;
}
} }
out: out:
if (err) if (err)
mana_remove(gd); mana_remove(gd, false);
return err; return err;
} }
void mana_remove(struct gdma_dev *gd) void mana_remove(struct gdma_dev *gd, bool suspending)
{ {
struct gdma_context *gc = gd->gdma_context; struct gdma_context *gc = gd->gdma_context;
struct mana_context *ac = gd->driver_data; struct mana_context *ac = gd->driver_data;
struct device *dev = gc->dev; struct device *dev = gc->dev;
struct net_device *ndev; struct net_device *ndev;
int err;
int i; int i;
for (i = 0; i < ac->num_ports; i++) { for (i = 0; i < ac->num_ports; i++) {
...@@ -1897,7 +1924,16 @@ void mana_remove(struct gdma_dev *gd) ...@@ -1897,7 +1924,16 @@ void mana_remove(struct gdma_dev *gd)
*/ */
rtnl_lock(); rtnl_lock();
mana_detach(ndev, false); err = mana_detach(ndev, false);
if (err)
netdev_err(ndev, "Failed to detach vPort %d: %d\n",
i, err);
if (suspending) {
/* No need to unregister the ndev. */
rtnl_unlock();
continue;
}
unregister_netdevice(ndev); unregister_netdevice(ndev);
...@@ -1910,6 +1946,10 @@ void mana_remove(struct gdma_dev *gd) ...@@ -1910,6 +1946,10 @@ void mana_remove(struct gdma_dev *gd)
out: out:
mana_gd_deregister_device(gd); mana_gd_deregister_device(gd);
if (suspending)
return;
gd->driver_data = NULL; gd->driver_data = NULL;
gd->gdma_context = NULL; gd->gdma_context = NULL;
kfree(ac); kfree(ac);
......
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