nfp: support port splitting via devlink
authorJakub Kicinski <jakub.kicinski@netronome.com>
Fri, 26 May 2017 08:03:36 +0000 (01:03 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 May 2017 15:01:49 +0000 (11:01 -0400)
Add support for configuring port split with devlink.  Add devlink
callbacks to validate requested config and call NSP helpers.
Getting the right nfp_port structure can be done with simple iteration.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfp_devlink.c
drivers/net/ethernet/netronome/nfp/nfp_net_main.c
drivers/net/ethernet/netronome/nfp/nfp_port.c
drivers/net/ethernet/netronome/nfp/nfp_port.h

index 2cfcb66b04bbbc525c614672c9ceb0eaee1c4e68..2609a0f28e818b13d78feb5177034849a188a475 100644 (file)
@@ -54,7 +54,104 @@ nfp_devlink_fill_eth_port(struct nfp_port *port,
        return 0;
 }
 
+static int
+nfp_devlink_fill_eth_port_from_id(struct nfp_pf *pf, unsigned int port_index,
+                                 struct nfp_eth_table_port *copy)
+{
+       struct nfp_port *port;
+
+       port = nfp_port_from_id(pf, NFP_PORT_PHYS_PORT, port_index);
+
+       return nfp_devlink_fill_eth_port(port, copy);
+}
+
+static int
+nfp_devlink_set_lanes(struct nfp_pf *pf, unsigned int idx, unsigned int lanes)
+{
+       struct nfp_nsp *nsp;
+       int ret;
+
+       nsp = nfp_eth_config_start(pf->cpp, idx);
+       if (IS_ERR(nsp))
+               return PTR_ERR(nsp);
+
+       ret = __nfp_eth_set_split(nsp, lanes);
+       if (ret) {
+               nfp_eth_config_cleanup_end(nsp);
+               return ret;
+       }
+
+       ret = nfp_eth_config_commit_end(nsp);
+       if (ret < 0)
+               return ret;
+       if (ret) /* no change */
+               return 0;
+
+       return nfp_net_refresh_port_table_sync(pf);
+}
+
+static int
+nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index,
+                      unsigned int count)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+       struct nfp_eth_table_port eth_port;
+       int ret;
+
+       if (count < 2)
+               return -EINVAL;
+
+       mutex_lock(&pf->lock);
+
+       rtnl_lock();
+       ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, &eth_port);
+       rtnl_unlock();
+       if (ret)
+               goto out;
+
+       if (eth_port.is_split || eth_port.port_lanes % count) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = nfp_devlink_set_lanes(pf, eth_port.index,
+                                   eth_port.port_lanes / count);
+out:
+       mutex_unlock(&pf->lock);
+
+       return ret;
+}
+
+static int
+nfp_devlink_port_unsplit(struct devlink *devlink, unsigned int port_index)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+       struct nfp_eth_table_port eth_port;
+       int ret;
+
+       mutex_lock(&pf->lock);
+
+       rtnl_lock();
+       ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, &eth_port);
+       rtnl_unlock();
+       if (ret)
+               goto out;
+
+       if (!eth_port.is_split) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = nfp_devlink_set_lanes(pf, eth_port.index, eth_port.port_lanes);
+out:
+       mutex_unlock(&pf->lock);
+
+       return ret;
+}
+
 const struct devlink_ops nfp_devlink_ops = {
+       .port_split             = nfp_devlink_port_split,
+       .port_unsplit           = nfp_devlink_port_unsplit,
 };
 
 int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
index b733c97677fb6b8f11b0aa0d140938c069fef67d..388759e047d8866fade95bed1f8fbb928acb51cf 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/lockdep.h>
 #include <linux/pci.h>
 #include <linux/pci_regs.h>
 #include <linux/msi.h>
@@ -561,19 +562,17 @@ nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
        return 0;
 }
 
-static void nfp_net_refresh_vnics(struct work_struct *work)
+int nfp_net_refresh_port_table_sync(struct nfp_pf *pf)
 {
-       struct nfp_pf *pf = container_of(work, struct nfp_pf,
-                                        port_refresh_work);
        struct nfp_eth_table *eth_table;
        struct nfp_net *nn, *next;
        struct nfp_port *port;
 
-       mutex_lock(&pf->lock);
+       lockdep_assert_held(&pf->lock);
 
        /* Check for nfp_net_pci_remove() racing against us */
        if (list_empty(&pf->vnics))
-               goto out;
+               return 0;
 
        /* Update state of all ports */
        rtnl_lock();
@@ -587,7 +586,7 @@ static void nfp_net_refresh_vnics(struct work_struct *work)
                                set_bit(NFP_PORT_CHANGED, &port->flags);
                rtnl_unlock();
                nfp_err(pf->cpp, "Error refreshing port config!\n");
-               goto out;
+               return -EIO;
        }
 
        list_for_each_entry(port, &pf->ports, port_list)
@@ -608,7 +607,17 @@ static void nfp_net_refresh_vnics(struct work_struct *work)
 
        if (list_empty(&pf->vnics))
                nfp_net_pci_remove_finish(pf);
-out:
+
+       return 0;
+}
+
+static void nfp_net_refresh_vnics(struct work_struct *work)
+{
+       struct nfp_pf *pf = container_of(work, struct nfp_pf,
+                                        port_refresh_work);
+
+       mutex_lock(&pf->lock);
+       nfp_net_refresh_port_table_sync(pf);
        mutex_unlock(&pf->lock);
 }
 
index 3beed4167e2ffd9a75b5bad8ff151520a649fd2f..a17410ac01ab0c67925e066300499aa343fcc05d 100644 (file)
@@ -31,6 +31,8 @@
  * SOFTWARE.
  */
 
+#include <linux/lockdep.h>
+
 #include "nfpcore/nfp_nsp.h"
 #include "nfp_app.h"
 #include "nfp_main.h"
@@ -48,6 +50,23 @@ struct nfp_port *nfp_port_from_netdev(struct net_device *netdev)
        return nn->port;
 }
 
+struct nfp_port *
+nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id)
+{
+       struct nfp_port *port;
+
+       lockdep_assert_held(&pf->lock);
+
+       if (type != NFP_PORT_PHYS_PORT)
+               return NULL;
+
+       list_for_each_entry(port, &pf->ports, port_list)
+               if (port->eth_id == id)
+                       return port;
+
+       return NULL;
+}
+
 struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port)
 {
        if (!port)
index 36a540b514be334a5bb3b3e983f7ecef2b24b6a9..4d1a9b3fed4141b2c0c1330b7304529114f348a8 100644 (file)
@@ -38,6 +38,7 @@
 
 struct net_device;
 struct nfp_app;
+struct nfp_pf;
 struct nfp_port;
 
 /**
@@ -90,6 +91,8 @@ struct nfp_port {
 };
 
 struct nfp_port *nfp_port_from_netdev(struct net_device *netdev);
+struct nfp_port *
+nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id);
 struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port);
 struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port);
 
@@ -103,6 +106,7 @@ void nfp_port_free(struct nfp_port *port);
 
 int nfp_net_refresh_eth_port(struct nfp_port *port);
 void nfp_net_refresh_port_table(struct nfp_port *port);
+int nfp_net_refresh_port_table_sync(struct nfp_pf *pf);
 
 int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port);
 void nfp_devlink_port_unregister(struct nfp_port *port);