diff options
author | David S. Miller <davem@davemloft.net> | 2021-11-01 16:21:49 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2021-11-01 16:21:49 +0300 |
commit | c6e03dbe0c7cdbe7b259deeae0f193d15ec5002f (patch) | |
tree | d739b2dcd39b03b1a7db755215e76a1816166f8c | |
parent | 986d2e3da7d7f24c16d419f926c65af1a994a04a (diff) | |
parent | 635096a86edb067d55a1e04b4a918f5c6dac0c51 (diff) | |
download | linux-c6e03dbe0c7cdbe7b259deeae0f193d15ec5002f.tar.xz |
Merge branch 'mana-misc'
Dexuan Cui says:
====================
net: mana: some misc patches
Patch 1 is a small fix.
Patch 2 reports OS info to the PF driver.
Before the patch, the req fields were all zeros.
Patch 3 fixes and cleans up the error handling of HWC creation failure.
Patch 4 adds the callbacks for hibernation/kexec. It's based on patch 3.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/microsoft/mana/gdma_main.c | 155 | ||||
-rw-r--r-- | drivers/net/ethernet/microsoft/mana/hw_channel.c | 71 | ||||
-rw-r--r-- | drivers/net/ethernet/microsoft/mana/mana.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/microsoft/mana/mana_en.c | 75 |
4 files changed, 208 insertions, 97 deletions
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index cee75b561f59..c96ac81212f7 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -3,6 +3,8 @@ #include <linux/module.h> #include <linux/pci.h> +#include <linux/utsname.h> +#include <linux/version.h> #include "mana.h" @@ -848,6 +850,15 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev) req.gd_drv_cap_flags3 = GDMA_DRV_CAP_FLAGS3; req.gd_drv_cap_flags4 = GDMA_DRV_CAP_FLAGS4; + req.drv_ver = 0; /* Unused*/ + req.os_type = 0x10; /* Linux */ + req.os_ver_major = LINUX_VERSION_MAJOR; + req.os_ver_minor = LINUX_VERSION_PATCHLEVEL; + req.os_ver_build = LINUX_VERSION_SUBLEVEL; + strscpy(req.os_ver_str1, utsname()->sysname, sizeof(req.os_ver_str1)); + strscpy(req.os_ver_str2, utsname()->release, sizeof(req.os_ver_str2)); + strscpy(req.os_ver_str3, utsname()->version, sizeof(req.os_ver_str3)); + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); if (err || resp.hdr.status) { dev_err(gc->dev, "VfVerifyVersionOutput: %d, status=0x%x\n", @@ -1247,6 +1258,52 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev) 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) { struct gdma_context *gc; @@ -1276,6 +1333,9 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!gc) goto release_region; + mutex_init(&gc->eq_test_event_mutex); + pci_set_drvdata(pdev, gc); + bar0_va = pci_iomap(pdev, bar, 0); if (!bar0_va) goto free_gc; @@ -1283,49 +1343,23 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) gc->bar0_va = bar0_va; 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_irqs(pdev); + err = mana_gd_setup(pdev); if (err) goto unmap_bar; - mutex_init(&gc->eq_test_event_mutex); - - err = mana_hwc_create_channel(gc); - if (err) - goto remove_irq; - - 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); + err = mana_probe(&gc->mana, false); if (err) - goto remove_irq; - - err = mana_probe(&gc->mana); - if (err) - goto clean_up_gdma; + goto cleanup_gd; return 0; -clean_up_gdma: - mana_hwc_destroy_channel(gc); - vfree(gc->cq_table); - gc->cq_table = NULL; -remove_irq: - mana_gd_remove_irqs(pdev); +cleanup_gd: + mana_gd_cleanup(pdev); unmap_bar: pci_iounmap(pdev, bar0_va); free_gc: + pci_set_drvdata(pdev, NULL); vfree(gc); release_region: pci_release_regions(pdev); @@ -1340,13 +1374,9 @@ static void mana_gd_remove(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); - mana_remove(&gc->mana); + mana_remove(&gc->mana, false); - mana_hwc_destroy_channel(gc); - vfree(gc->cq_table); - gc->cq_table = NULL; - - mana_gd_remove_irqs(pdev); + mana_gd_cleanup(pdev); pci_iounmap(pdev, gc->bar0_va); @@ -1357,6 +1387,52 @@ static void mana_gd_remove(struct pci_dev *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 #define PCI_VENDOR_ID_MICROSOFT 0x1414 #endif @@ -1371,6 +1447,9 @@ static struct pci_driver mana_driver = { .id_table = mana_id_table, .probe = mana_gd_probe, .remove = mana_gd_remove, + .suspend = mana_gd_suspend, + .resume = mana_gd_resume, + .shutdown = mana_gd_shutdown, }; module_pci_driver(mana_driver); diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 7c7a5fb91f79..34b971ff8ef8 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -309,9 +309,6 @@ static void mana_hwc_comp_event(void *ctx, struct gdma_queue *q_self) static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq) { - if (!hwc_cq) - return; - kfree(hwc_cq->comp_buf); if (hwc_cq->gdma_cq) @@ -446,9 +443,6 @@ static void mana_hwc_dealloc_dma_buf(struct hw_channel_context *hwc, static void mana_hwc_destroy_wq(struct hw_channel_context *hwc, struct hwc_wq *hwc_wq) { - if (!hwc_wq) - return; - mana_hwc_dealloc_dma_buf(hwc, hwc_wq->msg_buf); if (hwc_wq->gdma_wq) @@ -621,6 +615,7 @@ static int mana_hwc_establish_channel(struct gdma_context *gc, u16 *q_depth, *max_req_msg_size = hwc->hwc_init_max_req_msg_size; *max_resp_msg_size = hwc->hwc_init_max_resp_msg_size; + /* Both were set in mana_hwc_init_event_handler(). */ if (WARN_ON(cq->id >= gc->max_num_cqs)) return -EPROTO; @@ -636,9 +631,6 @@ static int mana_hwc_establish_channel(struct gdma_context *gc, u16 *q_depth, static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, u32 max_req_msg_size, u32 max_resp_msg_size) { - struct hwc_wq *hwc_rxq = NULL; - struct hwc_wq *hwc_txq = NULL; - struct hwc_cq *hwc_cq = NULL; int err; err = mana_hwc_init_inflight_msg(hwc, q_depth); @@ -651,44 +643,32 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth, err = mana_hwc_create_cq(hwc, q_depth * 2, mana_hwc_init_event_handler, hwc, mana_hwc_rx_event_handler, hwc, - mana_hwc_tx_event_handler, hwc, &hwc_cq); + mana_hwc_tx_event_handler, hwc, &hwc->cq); if (err) { dev_err(hwc->dev, "Failed to create HWC CQ: %d\n", err); goto out; } - hwc->cq = hwc_cq; err = mana_hwc_create_wq(hwc, GDMA_RQ, q_depth, max_req_msg_size, - hwc_cq, &hwc_rxq); + hwc->cq, &hwc->rxq); if (err) { dev_err(hwc->dev, "Failed to create HWC RQ: %d\n", err); goto out; } - hwc->rxq = hwc_rxq; err = mana_hwc_create_wq(hwc, GDMA_SQ, q_depth, max_resp_msg_size, - hwc_cq, &hwc_txq); + hwc->cq, &hwc->txq); if (err) { dev_err(hwc->dev, "Failed to create HWC SQ: %d\n", err); goto out; } - hwc->txq = hwc_txq; hwc->num_inflight_msg = q_depth; hwc->max_req_msg_size = max_req_msg_size; return 0; out: - if (hwc_txq) - mana_hwc_destroy_wq(hwc, hwc_txq); - - if (hwc_rxq) - mana_hwc_destroy_wq(hwc, hwc_rxq); - - if (hwc_cq) - mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc_cq); - - mana_gd_free_res_map(&hwc->inflight_msg_res); + /* mana_hwc_create_channel() will do the cleanup.*/ return err; } @@ -716,6 +696,9 @@ int mana_hwc_create_channel(struct gdma_context *gc) gd->pdid = INVALID_PDID; gd->doorbell = INVALID_DOORBELL; + /* mana_hwc_init_queues() only creates the required data structures, + * and doesn't touch the HWC device. + */ err = mana_hwc_init_queues(hwc, HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH, HW_CHANNEL_MAX_REQUEST_SIZE, HW_CHANNEL_MAX_RESPONSE_SIZE); @@ -741,42 +724,50 @@ int mana_hwc_create_channel(struct gdma_context *gc) return 0; out: - kfree(hwc); + mana_hwc_destroy_channel(gc); return err; } void mana_hwc_destroy_channel(struct gdma_context *gc) { struct hw_channel_context *hwc = gc->hwc.driver_data; - struct hwc_caller_ctx *ctx; - mana_smc_teardown_hwc(&gc->shm_channel, false); + if (!hwc) + return; + + /* gc->max_num_cqs is set in mana_hwc_init_event_handler(). If it's + * non-zero, the HWC worked and we should tear down the HWC here. + */ + if (gc->max_num_cqs > 0) { + mana_smc_teardown_hwc(&gc->shm_channel, false); + gc->max_num_cqs = 0; + } - ctx = hwc->caller_ctx; - kfree(ctx); + kfree(hwc->caller_ctx); hwc->caller_ctx = NULL; - mana_hwc_destroy_wq(hwc, hwc->txq); - hwc->txq = NULL; + if (hwc->txq) + mana_hwc_destroy_wq(hwc, hwc->txq); - mana_hwc_destroy_wq(hwc, hwc->rxq); - hwc->rxq = NULL; + if (hwc->rxq) + mana_hwc_destroy_wq(hwc, hwc->rxq); - mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc->cq); - hwc->cq = NULL; + if (hwc->cq) + mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc->cq); mana_gd_free_res_map(&hwc->inflight_msg_res); hwc->num_inflight_msg = 0; - if (hwc->gdma_dev->pdid != INVALID_PDID) { - hwc->gdma_dev->doorbell = INVALID_DOORBELL; - hwc->gdma_dev->pdid = INVALID_PDID; - } + hwc->gdma_dev->doorbell = INVALID_DOORBELL; + hwc->gdma_dev->pdid = INVALID_PDID; kfree(hwc); gc->hwc.driver_data = NULL; gc->hwc.gdma_context = NULL; + + vfree(gc->cq_table); + gc->cq_table = NULL; } int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len, diff --git a/drivers/net/ethernet/microsoft/mana/mana.h b/drivers/net/ethernet/microsoft/mana/mana.h index fc98a5ba5ed0..d047ee876f12 100644 --- a/drivers/net/ethernet/microsoft/mana/mana.h +++ b/drivers/net/ethernet/microsoft/mana/mana.h @@ -374,8 +374,8 @@ int mana_alloc_queues(struct net_device *ndev); int mana_attach(struct net_device *ndev); int mana_detach(struct net_device *ndev, bool from_close); -int mana_probe(struct gdma_dev *gd); -void mana_remove(struct gdma_dev *gd); +int mana_probe(struct gdma_dev *gd, bool resuming); +void mana_remove(struct gdma_dev *gd, bool suspending); extern const struct ethtool_ops mana_ethtool_ops; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index ef95f4017c6d..72cbf45c42d8 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1599,7 +1599,8 @@ static int mana_init_port(struct net_device *ndev) err = mana_query_vport_cfg(apc, port_idx, &max_txq, &max_rxq, &num_indirect_entries); if (err) { - netdev_err(ndev, "Failed to query info for vPort 0\n"); + netdev_err(ndev, "Failed to query info for vPort %d\n", + port_idx); goto reset_apc; } @@ -1827,11 +1828,12 @@ free_net: 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 mana_context *ac = gd->driver_data; struct device *dev = gc->dev; - struct mana_context *ac; + u16 num_ports = 0; int err; int i; @@ -1843,44 +1845,70 @@ int mana_probe(struct gdma_dev *gd) if (err) return err; - ac = kzalloc(sizeof(*ac), GFP_KERNEL); - if (!ac) - return -ENOMEM; + if (!resuming) { + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + return -ENOMEM; - ac->gdma_dev = gd; - ac->num_ports = 1; - gd->driver_data = ac; + ac->gdma_dev = gd; + gd->driver_data = ac; + } err = mana_create_eq(ac); if (err) goto out; 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) 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) ac->num_ports = MAX_PORTS_IN_MANA_DEV; - for (i = 0; i < ac->num_ports; i++) { - err = mana_probe_port(ac, i, &ac->ports[i]); - if (err) - break; + if (!resuming) { + for (i = 0; i < ac->num_ports; i++) { + err = mana_probe_port(ac, i, &ac->ports[i]); + 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: if (err) - mana_remove(gd); + mana_remove(gd, false); 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 mana_context *ac = gd->driver_data; struct device *dev = gc->dev; struct net_device *ndev; + int err; int i; for (i = 0; i < ac->num_ports; i++) { @@ -1896,7 +1924,16 @@ void mana_remove(struct gdma_dev *gd) */ 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); @@ -1909,6 +1946,10 @@ void mana_remove(struct gdma_dev *gd) out: mana_gd_deregister_device(gd); + + if (suspending) + return; + gd->driver_data = NULL; gd->gdma_context = NULL; kfree(ac); |