summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/netronome/nfp/nfp_net_main.c')
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_main.c238
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 &eth_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);
}