diff options
Diffstat (limited to 'drivers/net/ethernet/netronome/nfp/nfp_net_main.c')
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 238 |
1 files changed, 183 insertions, 55 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 3afcdc11480c..8cb87cbe1120 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -47,11 +47,12 @@ #include <linux/pci_regs.h> #include <linux/msi.h> #include <linux/random.h> +#include <linux/rtnetlink.h> #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_nffw.h" -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfpcore/nfp6000_pcie.h" #include "nfp_net_ctrl.h" @@ -129,61 +130,61 @@ err_area: return (u8 __iomem *)ERR_PTR(err); } +/** + * nfp_net_get_mac_addr() - Get the MAC address. + * @nn: NFP Network structure + * @cpp: NFP CPP handle + * @id: NFP port id + * + * First try to get the MAC address from NSP ETH table. If that + * fails try HWInfo. As a last resort generate a random address. + */ static void -nfp_net_get_mac_addr_hwinfo(struct nfp_net *nn, struct nfp_cpp *cpp, - unsigned int id) +nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id) { + struct nfp_net_dp *dp = &nn->dp; u8 mac_addr[ETH_ALEN]; const char *mac_str; char name[32]; + if (nn->eth_port) { + ether_addr_copy(dp->netdev->dev_addr, nn->eth_port->mac_addr); + ether_addr_copy(dp->netdev->perm_addr, nn->eth_port->mac_addr); + return; + } + snprintf(name, sizeof(name), "eth%d.mac", id); mac_str = nfp_hwinfo_lookup(cpp, name); if (!mac_str) { - dev_warn(&nn->pdev->dev, - "Can't lookup MAC address. Generate\n"); - eth_hw_addr_random(nn->netdev); + dev_warn(dp->dev, "Can't lookup MAC address. Generate\n"); + eth_hw_addr_random(dp->netdev); return; } if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { - dev_warn(&nn->pdev->dev, + dev_warn(dp->dev, "Can't parse MAC address (%s). Generate.\n", mac_str); - eth_hw_addr_random(nn->netdev); + eth_hw_addr_random(dp->netdev); return; } - ether_addr_copy(nn->netdev->dev_addr, mac_addr); - ether_addr_copy(nn->netdev->perm_addr, mac_addr); + ether_addr_copy(dp->netdev->dev_addr, mac_addr); + ether_addr_copy(dp->netdev->perm_addr, mac_addr); } -/** - * nfp_net_get_mac_addr() - Get the MAC address. - * @nn: NFP Network structure - * @pf: NFP PF device structure - * @id: NFP port id - * - * First try to get the MAC address from NSP ETH table. If that - * fails try HWInfo. As a last resort generate a random address. - */ -static void -nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_pf *pf, unsigned int id) +static struct nfp_eth_table_port * +nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int id) { int i; - for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++) - if (pf->eth_tbl->ports[i].eth_index == id) { - const u8 *mac_addr = pf->eth_tbl->ports[i].mac_addr; + for (i = 0; eth_tbl && i < eth_tbl->count; i++) + if (eth_tbl->ports[i].eth_index == id) + return ð_tbl->ports[i]; - ether_addr_copy(nn->netdev->dev_addr, mac_addr); - ether_addr_copy(nn->netdev->perm_addr, mac_addr); - return; - } - - nfp_net_get_mac_addr_hwinfo(nn, pf->cpp, id); + return NULL; } static unsigned int nfp_net_pf_get_num_ports(struct nfp_pf *pf) @@ -282,6 +283,7 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) while (!list_empty(&pf->ports)) { nn = list_first_entry(&pf->ports, struct nfp_net, port_list); list_del(&nn->port_list); + pf->num_netdevs--; nfp_net_netdev_free(nn); } @@ -290,7 +292,8 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) static struct nfp_net * nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, void __iomem *tx_bar, void __iomem *rx_bar, - int stride, struct nfp_net_fw_version *fw_ver) + int stride, struct nfp_net_fw_version *fw_ver, + struct nfp_eth_table_port *eth_port) { u32 n_tx_rings, n_rx_rings; struct nfp_net *nn; @@ -305,12 +308,13 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, nn->cpp = pf->cpp; nn->fw_ver = *fw_ver; - nn->ctrl_bar = ctrl_bar; + nn->dp.ctrl_bar = ctrl_bar; nn->tx_bar = tx_bar; nn->rx_bar = rx_bar; - nn->is_vf = 0; + nn->dp.is_vf = 0; nn->stride_rx = stride; nn->stride_tx = stride; + nn->eth_port = eth_port; return nn; } @@ -322,7 +326,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn, int err; /* Get MAC address */ - nfp_net_get_mac_addr(nn, pf, id); + nfp_net_get_mac_addr(nn, pf->cpp, id); /* Get ME clock frequency from ctrl BAR * XXX for now frequency is hardcoded until we figure out how @@ -330,7 +334,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn, */ nn->me_freq_mhz = 1200; - err = nfp_net_netdev_init(nn->netdev); + err = nfp_net_netdev_init(nn->dp.netdev); if (err) return err; @@ -347,6 +351,7 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, int stride, struct nfp_net_fw_version *fw_ver) { u32 prev_tx_base, prev_rx_base, tgt_tx_base, tgt_rx_base; + struct nfp_eth_table_port *eth_port; struct nfp_net *nn; unsigned int i; int err; @@ -362,17 +367,27 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, prev_tx_base = tgt_tx_base; prev_rx_base = tgt_rx_base; - nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, rx_bar, - stride, fw_ver); - if (IS_ERR(nn)) { - err = PTR_ERR(nn); - goto err_free_prev; + eth_port = nfp_net_find_port(pf->eth_tbl, i); + if (eth_port && eth_port->override_changed) { + nfp_warn(pf->cpp, "Config changed for port #%d, reboot required before port will be operational\n", i); + } else { + nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, + rx_bar, stride, + fw_ver, eth_port); + if (IS_ERR(nn)) { + err = PTR_ERR(nn); + goto err_free_prev; + } + list_add_tail(&nn->port_list, &pf->ports); + pf->num_netdevs++; } - list_add_tail(&nn->port_list, &pf->ports); ctrl_bar += NFP_PF_CSR_SLICE_SIZE; } + if (list_empty(&pf->ports)) + return -ENODEV; + return 0; err_free_prev: @@ -399,7 +414,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, /* Get MSI-X vectors */ wanted_irqs = 0; list_for_each_entry(nn, &pf->ports, port_list) - wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->num_r_vecs; + wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs; pf->irq_entries = kcalloc(wanted_irqs, sizeof(*pf->irq_entries), GFP_KERNEL); if (!pf->irq_entries) { @@ -408,7 +423,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, } num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries, - NFP_NET_MIN_PORT_IRQS * pf->num_ports, + NFP_NET_MIN_PORT_IRQS * pf->num_netdevs, wanted_irqs); if (!num_irqs) { nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); @@ -418,7 +433,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, /* Distribute IRQs to ports */ irqs_left = num_irqs; - ports_left = pf->num_ports; + ports_left = pf->num_netdevs; list_for_each_entry(nn, &pf->ports, port_list) { unsigned int n; @@ -444,7 +459,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, err_prev_deinit: list_for_each_entry_continue_reverse(nn, &pf->ports, port_list) { nfp_net_debugfs_dir_clean(&nn->debugfs_dir); - nfp_net_netdev_clean(nn->netdev); + nfp_net_netdev_clean(nn->dp.netdev); } nfp_net_irqs_disable(pf->pdev); err_vec_free: @@ -454,6 +469,108 @@ err_nn_free: return err; } +static void nfp_net_pci_remove_finish(struct nfp_pf *pf) +{ + nfp_net_debugfs_dir_clean(&pf->ddir); + + nfp_net_irqs_disable(pf->pdev); + kfree(pf->irq_entries); + + nfp_cpp_area_release_free(pf->rx_area); + nfp_cpp_area_release_free(pf->tx_area); + nfp_cpp_area_release_free(pf->ctrl_area); +} + +static void nfp_net_refresh_netdevs(struct work_struct *work) +{ + struct nfp_pf *pf = container_of(work, struct nfp_pf, + port_refresh_work); + struct nfp_eth_table *eth_table; + struct nfp_net *nn, *next; + + mutex_lock(&pf->port_lock); + + /* Check for nfp_net_pci_remove() racing against us */ + if (list_empty(&pf->ports)) + goto out; + + list_for_each_entry(nn, &pf->ports, port_list) + nfp_net_link_changed_read_clear(nn); + + eth_table = nfp_eth_read_ports(pf->cpp); + if (!eth_table) { + nfp_err(pf->cpp, "Error refreshing port config!\n"); + goto out; + } + + rtnl_lock(); + list_for_each_entry(nn, &pf->ports, port_list) { + if (!nn->eth_port) + continue; + nn->eth_port = nfp_net_find_port(eth_table, + nn->eth_port->eth_index); + } + rtnl_unlock(); + + kfree(pf->eth_tbl); + pf->eth_tbl = eth_table; + + list_for_each_entry_safe(nn, next, &pf->ports, port_list) { + if (!nn->eth_port) { + nfp_warn(pf->cpp, "Warning: port not present after reconfig\n"); + continue; + } + if (!nn->eth_port->override_changed) + continue; + + nn_warn(nn, "Port config changed, unregistering. Reboot required before port will be operational again.\n"); + + nfp_net_debugfs_dir_clean(&nn->debugfs_dir); + nfp_net_netdev_clean(nn->dp.netdev); + + list_del(&nn->port_list); + pf->num_netdevs--; + nfp_net_netdev_free(nn); + } + + if (list_empty(&pf->ports)) + nfp_net_pci_remove_finish(pf); +out: + mutex_unlock(&pf->port_lock); +} + +void nfp_net_refresh_port_table(struct nfp_net *nn) +{ + struct nfp_pf *pf = pci_get_drvdata(nn->pdev); + + schedule_work(&pf->port_refresh_work); +} + +int nfp_net_refresh_eth_port(struct nfp_net *nn) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_eth_table *eth_table; + + eth_table = nfp_eth_read_ports(nn->cpp); + if (!eth_table) { + nn_err(nn, "Error refreshing port state table!\n"); + return -EIO; + } + + eth_port = nfp_net_find_port(eth_table, nn->eth_port->eth_index); + if (!eth_port) { + nn_err(nn, "Error finding state of the port!\n"); + kfree(eth_table); + return -EIO; + } + + memcpy(nn->eth_port, eth_port, sizeof(*eth_port)); + + kfree(eth_table); + + return 0; +} + /* * PCI device functions */ @@ -467,17 +584,23 @@ int nfp_net_pci_probe(struct nfp_pf *pf) int stride; int err; + INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_netdevs); + mutex_init(&pf->port_lock); + /* Verify that the board has completed initialization */ if (!nfp_is_ready(pf->cpp)) { nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); return -EINVAL; } + mutex_lock(&pf->port_lock); pf->num_ports = nfp_net_pf_get_num_ports(pf); ctrl_bar = nfp_net_pf_map_ctrl_bar(pf); - if (!ctrl_bar) - return pf->fw_loaded ? -EINVAL : -EPROBE_DEFER; + if (!ctrl_bar) { + err = pf->fw_loaded ? -EINVAL : -EPROBE_DEFER; + goto err_unlock; + } nfp_net_get_fw_version(&fw_ver, ctrl_bar); if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { @@ -551,6 +674,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_clean_ddir; + mutex_unlock(&pf->port_lock); + return 0; err_clean_ddir: @@ -560,6 +685,8 @@ err_unmap_tx: nfp_cpp_area_release_free(pf->tx_area); err_ctrl_unmap: nfp_cpp_area_release_free(pf->ctrl_area); +err_unlock: + mutex_unlock(&pf->port_lock); return err; } @@ -567,20 +694,21 @@ void nfp_net_pci_remove(struct nfp_pf *pf) { struct nfp_net *nn; + mutex_lock(&pf->port_lock); + if (list_empty(&pf->ports)) + goto out; + list_for_each_entry(nn, &pf->ports, port_list) { nfp_net_debugfs_dir_clean(&nn->debugfs_dir); - nfp_net_netdev_clean(nn->netdev); + nfp_net_netdev_clean(nn->dp.netdev); } nfp_net_pf_free_netdevs(pf); - nfp_net_debugfs_dir_clean(&pf->ddir); - - nfp_net_irqs_disable(pf->pdev); - kfree(pf->irq_entries); + nfp_net_pci_remove_finish(pf); +out: + mutex_unlock(&pf->port_lock); - nfp_cpp_area_release_free(pf->rx_area); - nfp_cpp_area_release_free(pf->tx_area); - nfp_cpp_area_release_free(pf->ctrl_area); + cancel_work_sync(&pf->port_refresh_work); } |