nfp: add flower app
authorSimon Horman <simon.horman@netronome.com>
Fri, 23 Jun 2017 20:12:08 +0000 (22:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sun, 25 Jun 2017 15:42:02 +0000 (11:42 -0400)
Add app for flower offload. At this point the PF netdev and phys port
representor netdevs are initialised. Follow-up work will add support for
VF and PF representors and beyond that offloading the flower classifier.

Based in part on work by Benjamin LaHaise and Bert van Leeuwen.

Signed-off-by: Simon Horman <simon.horman@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/Makefile
drivers/net/ethernet/netronome/nfp/flower/main.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/nfp_app.c
drivers/net/ethernet/netronome/nfp/nfp_app.h

index e14f62863add00b20cdc60b64e9f0192c89399c3..10b556b2c59d277fb73b7b27cbd03c852eec6a36 100644 (file)
@@ -28,6 +28,7 @@ nfp-objs := \
            bpf/main.o \
            bpf/offload.o \
            flower/cmsg.o \
+           flower/main.o \
            nic/main.o
 
 ifeq ($(CONFIG_BPF_SYSCALL),y)
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c
new file mode 100644 (file)
index 0000000..54d8180
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below.  You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause License:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      1. Redistributions of source code must retain the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer.
+ *
+ *      2. Redistributions in binary form must reproduce the above
+ *         copyright notice, this list of conditions and the following
+ *         disclaimer in the documentation and/or other materials
+ *         provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <net/devlink.h>
+#include <net/dst_metadata.h>
+
+#include "../nfpcore/nfp_cpp.h"
+#include "../nfpcore/nfp_nsp.h"
+#include "../nfp_app.h"
+#include "../nfp_main.h"
+#include "../nfp_net.h"
+#include "../nfp_net_repr.h"
+#include "../nfp_port.h"
+#include "./cmsg.h"
+
+/**
+ * struct nfp_flower_priv - Flower APP per-vNIC priv data
+ * @nn:                     Pointer to vNIC
+ */
+struct nfp_flower_priv {
+       struct nfp_net *nn;
+};
+
+static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn)
+{
+       return "FLOWER";
+}
+
+static enum devlink_eswitch_mode eswitch_mode_get(struct nfp_app *app)
+{
+       return DEVLINK_ESWITCH_MODE_SWITCHDEV;
+}
+
+static enum nfp_repr_type
+nfp_flower_repr_get_type_and_port(struct nfp_app *app, u32 port_id, u8 *port)
+{
+       switch (FIELD_GET(NFP_FLOWER_CMSG_PORT_TYPE, port_id)) {
+       case NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT:
+               *port = FIELD_GET(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM,
+                                 port_id);
+               return NFP_REPR_TYPE_PHYS_PORT;
+
+       case NFP_FLOWER_CMSG_PORT_TYPE_PCIE_PORT:
+               *port = FIELD_GET(NFP_FLOWER_CMSG_PORT_VNIC, port_id);
+               if (FIELD_GET(NFP_FLOWER_CMSG_PORT_VNIC_TYPE, port_id) ==
+                   NFP_FLOWER_CMSG_PORT_VNIC_TYPE_PF)
+                       return NFP_REPR_TYPE_PF;
+               else
+                       return NFP_REPR_TYPE_VF;
+       }
+
+       return NFP_FLOWER_CMSG_PORT_TYPE_UNSPEC;
+}
+
+static struct net_device *
+nfp_flower_repr_get(struct nfp_app *app, u32 port_id)
+{
+       enum nfp_repr_type repr_type;
+       struct nfp_reprs *reprs;
+       u8 port = 0;
+
+       repr_type = nfp_flower_repr_get_type_and_port(app, port_id, &port);
+
+       reprs = rcu_dereference(app->reprs[repr_type]);
+       if (!reprs)
+               return NULL;
+
+       if (port >= reprs->num_reprs)
+               return NULL;
+
+       return reprs->reprs[port];
+}
+
+static void
+nfp_flower_repr_netdev_get_stats64(struct net_device *netdev,
+                                  struct rtnl_link_stats64 *stats)
+{
+       struct nfp_repr *repr = netdev_priv(netdev);
+       enum nfp_repr_type type;
+       u32 port_id;
+       u8 port = 0;
+
+       port_id = repr->dst->u.port_info.port_id;
+       type = nfp_flower_repr_get_type_and_port(repr->app, port_id, &port);
+       nfp_repr_get_stats64(repr->app, type, port, stats);
+}
+
+static int nfp_flower_repr_netdev_open(struct net_device *netdev)
+{
+       int err;
+
+       err = nfp_flower_cmsg_portmod(netdev, true);
+       if (err)
+               return err;
+
+       netif_carrier_on(netdev);
+       netif_tx_wake_all_queues(netdev);
+
+       return 0;
+}
+
+static int nfp_flower_repr_netdev_stop(struct net_device *netdev)
+{
+       netif_carrier_off(netdev);
+       netif_tx_disable(netdev);
+
+       return nfp_flower_cmsg_portmod(netdev, false);
+}
+
+static const struct net_device_ops nfp_flower_repr_netdev_ops = {
+       .ndo_open               = nfp_flower_repr_netdev_open,
+       .ndo_stop               = nfp_flower_repr_netdev_stop,
+       .ndo_start_xmit         = nfp_repr_xmit,
+       .ndo_get_stats64        = nfp_flower_repr_netdev_get_stats64,
+       .ndo_has_offload_stats  = nfp_repr_has_offload_stats,
+       .ndo_get_offload_stats  = nfp_repr_get_offload_stats,
+};
+
+static void nfp_flower_stop(struct nfp_app *app)
+{
+       nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+}
+
+static int nfp_flower_start(struct nfp_app *app)
+{
+       struct nfp_eth_table *eth_tbl = app->pf->eth_tbl;
+       struct nfp_flower_priv *priv = app->priv;
+       struct nfp_reprs *reprs, *old_reprs;
+       unsigned int i;
+       int err;
+
+       reprs = nfp_reprs_alloc(eth_tbl->max_index + 1);
+       if (!reprs)
+               return -ENOMEM;
+
+       for (i = 0; i < eth_tbl->count; i++) {
+               int phys_port = eth_tbl->ports[i].index;
+               struct nfp_port *port;
+               u32 cmsg_port_id;
+
+               reprs->reprs[phys_port] = nfp_repr_alloc(app);
+               if (!reprs->reprs[phys_port]) {
+                       err = -ENOMEM;
+                       goto err_reprs_clean;
+               }
+
+               port = nfp_port_alloc(app, NFP_PORT_PHYS_PORT,
+                                     reprs->reprs[phys_port]);
+               if (IS_ERR(port)) {
+                       err = PTR_ERR(port);
+                       goto err_reprs_clean;
+               }
+               err = nfp_port_init_phy_port(app->pf, app, port, i);
+               if (err) {
+                       nfp_port_free(port);
+                       goto err_reprs_clean;
+               }
+
+               SET_NETDEV_DEV(reprs->reprs[phys_port], &priv->nn->pdev->dev);
+               nfp_net_get_mac_addr(app->pf, port,
+                                    eth_tbl->ports[i].eth_index);
+
+               cmsg_port_id = nfp_flower_cmsg_phys_port(phys_port);
+               err = nfp_repr_init(app, reprs->reprs[phys_port],
+                                   &nfp_flower_repr_netdev_ops,
+                                   cmsg_port_id, port, priv->nn->dp.netdev);
+               if (err) {
+                       nfp_port_free(port);
+                       goto err_reprs_clean;
+               }
+
+               nfp_info(app->cpp, "Phys Port %d Representor(%s) created\n",
+                        phys_port, reprs->reprs[phys_port]->name);
+       }
+
+       old_reprs = nfp_app_reprs_set(app, NFP_REPR_TYPE_PHYS_PORT, reprs);
+       if (IS_ERR(old_reprs)) {
+               err = PTR_ERR(old_reprs);
+               goto err_reprs_clean;
+       }
+
+       return 0;
+err_reprs_clean:
+       nfp_reprs_clean_and_free(reprs);
+       return err;
+}
+
+static void nfp_flower_vnic_clean(struct nfp_app *app, struct nfp_net *nn)
+{
+       kfree(app->priv);
+       app->priv = NULL;
+}
+
+static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn,
+                               unsigned int id)
+{
+       struct nfp_flower_priv *priv;
+
+       if (id > 0) {
+               nfp_warn(app->cpp, "FlowerNIC doesn't support more than one data vNIC\n");
+               goto err_invalid_port;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       app->priv = priv;
+       priv->nn = nn;
+
+       eth_hw_addr_random(nn->dp.netdev);
+       netif_keep_dst(nn->dp.netdev);
+
+       return 0;
+
+err_invalid_port:
+       nn->port = nfp_port_alloc(app, NFP_PORT_INVALID, nn->dp.netdev);
+       return PTR_ERR_OR_ZERO(nn->port);
+}
+
+static int nfp_flower_init(struct nfp_app *app)
+{
+       const struct nfp_pf *pf = app->pf;
+
+       if (!pf->eth_tbl) {
+               nfp_warn(app->cpp, "FlowerNIC requires eth table\n");
+               return -EINVAL;
+       }
+
+       if (!pf->mac_stats_bar) {
+               nfp_warn(app->cpp, "FlowerNIC requires mac_stats BAR\n");
+               return -EINVAL;
+       }
+
+       if (!pf->vf_cfg_bar) {
+               nfp_warn(app->cpp, "FlowerNIC requires vf_cfg BAR\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+const struct nfp_app_type app_flower = {
+       .id             = NFP_APP_FLOWER_NIC,
+       .name           = "flower",
+       .ctrl_has_meta  = true,
+
+       .extra_cap      = nfp_flower_extra_cap,
+
+       .init           = nfp_flower_init,
+
+       .vnic_init      = nfp_flower_vnic_init,
+       .vnic_clean     = nfp_flower_vnic_clean,
+
+       .start          = nfp_flower_start,
+       .stop           = nfp_flower_stop,
+
+       .ctrl_msg_rx    = nfp_flower_cmsg_rx,
+
+       .eswitch_mode_get  = eswitch_mode_get,
+       .repr_get       = nfp_flower_repr_get,
+};
index 2b71050dc5e4ce7b19d5014269f7f80794b4555a..5620de05c9969275681166423ef41eeef79165c4 100644 (file)
@@ -43,6 +43,7 @@
 static const struct nfp_app_type *apps[] = {
        &app_nic,
        &app_bpf,
+       &app_flower,
 };
 
 const char *nfp_app_mip_name(struct nfp_app *app)
index 36949b3e91c1a85b212e402988f6a05242703ff0..ae2d02753d1a073d03590d4288a26ad454406989 100644 (file)
@@ -52,10 +52,12 @@ struct nfp_net;
 enum nfp_app_id {
        NFP_APP_CORE_NIC        = 0x1,
        NFP_APP_BPF_NIC         = 0x2,
+       NFP_APP_FLOWER_NIC      = 0x3,
 };
 
 extern const struct nfp_app_type app_nic;
 extern const struct nfp_app_type app_bpf;
+extern const struct nfp_app_type app_flower;
 
 /**
  * struct nfp_app_type - application definition
@@ -119,6 +121,7 @@ struct nfp_app_type {
  * @ctrl:      pointer to ctrl vNIC struct
  * @reprs:     array of pointers to representors
  * @type:      pointer to const application ops and info
+ * @priv:      app-specific priv data
  */
 struct nfp_app {
        struct pci_dev *pdev;
@@ -129,6 +132,7 @@ struct nfp_app {
        struct nfp_reprs __rcu *reprs[NFP_REPR_TYPE_MAX + 1];
 
        const struct nfp_app_type *type;
+       void *priv;
 };
 
 bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);