sfc: bind blocks for TC offload on EF100
authorEdward Cree <ecree.xilinx@gmail.com>
Mon, 26 Sep 2022 18:57:31 +0000 (19:57 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 28 Sep 2022 08:43:22 +0000 (09:43 +0100)
Bind direct blocks for the MAE-admin PF and each VF representor.
Currently these connect to a stub efx_tc_flower() that only returns
 -EOPNOTSUPP; subsequent patches will implement flower offloads to the
 Match-Action Engine.

Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sfc/Makefile
drivers/net/ethernet/sfc/ef100_netdev.c
drivers/net/ethernet/sfc/ef100_nic.c
drivers/net/ethernet/sfc/ef100_rep.c
drivers/net/ethernet/sfc/tc.c
drivers/net/ethernet/sfc/tc.h
drivers/net/ethernet/sfc/tc_bindings.c [new file with mode: 0644]
drivers/net/ethernet/sfc/tc_bindings.h [new file with mode: 0644]

index bb06fa228367e4a75ec40e0027548688a346b807..b5e45fc6337e84eda40beb3b66176ca2f4692fa7 100644 (file)
@@ -9,7 +9,7 @@ sfc-y                   += efx.o efx_common.o efx_channels.o nic.o \
                           ef100_ethtool.o ef100_rx.o ef100_tx.o
 sfc-$(CONFIG_SFC_MTD)  += mtd.o
 sfc-$(CONFIG_SFC_SRIOV)        += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
-                           mae.o tc.o
+                           mae.o tc.o tc_bindings.o
 
 obj-$(CONFIG_SFC)      += sfc.o
 
index 17b9d37218cbce019c657db2a245834b7c137bd2..88fa29572e230ec0691a7ca61f30c406b21d72cd 100644 (file)
@@ -23,6 +23,7 @@
 #include "mcdi_filters.h"
 #include "rx_common.h"
 #include "ef100_sriov.h"
+#include "tc_bindings.h"
 
 static void ef100_update_name(struct efx_nic *efx)
 {
@@ -246,6 +247,9 @@ static const struct net_device_ops ef100_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = efx_filter_rfs,
 #endif
+#ifdef CONFIG_SFC_SRIOV
+       .ndo_setup_tc           = efx_tc_setup,
+#endif
 };
 
 /*     Netdev registration
index 8061efdaf82ce184f2a38e398609c39cbdf39d9a..ad686c671ab893693c93f9394bc3a3597d2b3264 100644 (file)
@@ -1137,6 +1137,9 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
                 */
                netif_warn(efx, probe, net_dev, "Failed to probe MAE rc %d\n",
                           rc);
+       } else {
+               net_dev->features |= NETIF_F_HW_TC;
+               efx->fixed_features |= NETIF_F_HW_TC;
        }
 #endif
        return 0;
index 73ae4656a6e7359251febb49411ec26ad018003b..0a631e0c991431e79f4bf27ec8118830c754cca1 100644 (file)
@@ -14,6 +14,7 @@
 #include "ef100_nic.h"
 #include "mae.h"
 #include "rx_common.h"
+#include "tc_bindings.h"
 
 #define EFX_EF100_REP_DRIVER   "efx_ef100_rep"
 
@@ -107,6 +108,20 @@ static int efx_ef100_rep_get_phys_port_name(struct net_device *dev,
        return 0;
 }
 
+static int efx_ef100_rep_setup_tc(struct net_device *net_dev,
+                                 enum tc_setup_type type, void *type_data)
+{
+       struct efx_rep *efv = netdev_priv(net_dev);
+       struct efx_nic *efx = efv->parent;
+
+       if (type == TC_SETUP_CLSFLOWER)
+               return efx_tc_flower(efx, net_dev, type_data, efv);
+       if (type == TC_SETUP_BLOCK)
+               return efx_tc_setup_block(net_dev, efx, type_data, efv);
+
+       return -EOPNOTSUPP;
+}
+
 static void efx_ef100_rep_get_stats64(struct net_device *dev,
                                      struct rtnl_link_stats64 *stats)
 {
@@ -127,6 +142,7 @@ static const struct net_device_ops efx_ef100_rep_netdev_ops = {
        .ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id,
        .ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name,
        .ndo_get_stats64        = efx_ef100_rep_get_stats64,
+       .ndo_setup_tc           = efx_ef100_rep_setup_tc,
 };
 
 static void efx_ef100_rep_get_drvinfo(struct net_device *dev,
index 0c0aeb91f5009dc4a1731f0215a07f9647635359..23c4325e739a51af6e77feb1af4b6782c71e42d8 100644 (file)
@@ -58,6 +58,12 @@ static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rul
        rule->fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL;
 }
 
+int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev,
+                 struct flow_cls_offload *tc, struct efx_rep *efv)
+{
+       return -EOPNOTSUPP;
+}
+
 static int efx_tc_configure_default_rule(struct efx_nic *efx, u32 ing_port,
                                         u32 eg_port, struct efx_tc_flow_rule *rule)
 {
@@ -207,7 +213,11 @@ int efx_init_tc(struct efx_nic *efx)
        rc = efx_tc_configure_default_rule_wire(efx);
        if (rc)
                return rc;
-       return efx_tc_configure_rep_mport(efx);
+       rc = efx_tc_configure_rep_mport(efx);
+       if (rc)
+               return rc;
+       efx->tc->up = true;
+       return 0;
 }
 
 void efx_fini_tc(struct efx_nic *efx)
@@ -218,6 +228,7 @@ void efx_fini_tc(struct efx_nic *efx)
        efx_tc_deconfigure_rep_mport(efx);
        efx_tc_deconfigure_default_rule(efx, &efx->tc->dflt.pf);
        efx_tc_deconfigure_default_rule(efx, &efx->tc->dflt.wire);
+       efx->tc->up = false;
 }
 
 int efx_init_struct_tc(struct efx_nic *efx)
@@ -228,6 +239,7 @@ int efx_init_struct_tc(struct efx_nic *efx)
        efx->tc = kzalloc(sizeof(*efx->tc), GFP_KERNEL);
        if (!efx->tc)
                return -ENOMEM;
+       INIT_LIST_HEAD(&efx->tc->block_list);
 
        efx->tc->reps_filter_uc = -1;
        efx->tc->reps_filter_mc = -1;
index 309123c6b3867059e56c1d768f3d9c5dd1e20feb..7b1a6fa0097dde47de1c386ca28e0b96189912e5 100644 (file)
@@ -11,6 +11,7 @@
 
 #ifndef EFX_TC_H
 #define EFX_TC_H
+#include <net/flow_offload.h>
 #include "net_driver.h"
 
 struct efx_tc_action_set {
@@ -49,6 +50,7 @@ enum efx_tc_rule_prios {
 /**
  * struct efx_tc_state - control plane data for TC offload
  *
+ * @block_list: List of &struct efx_tc_block_binding
  * @reps_mport_id: MAE port allocated for representor RX
  * @reps_filter_uc: VNIC filter for representor unicast RX (promisc)
  * @reps_filter_mc: VNIC filter for representor multicast RX (allmulti)
@@ -57,14 +59,17 @@ enum efx_tc_rule_prios {
  *     %EFX_TC_PRIO_DFLT.  Named by *ingress* port
  * @dflt.pf: rule for traffic ingressing from PF (egresses to wire)
  * @dflt.wire: rule for traffic ingressing from wire (egresses to PF)
+ * @up: have TC datastructures been set up?
  */
 struct efx_tc_state {
+       struct list_head block_list;
        u32 reps_mport_id, reps_mport_vport_id;
        s32 reps_filter_uc, reps_filter_mc;
        struct {
                struct efx_tc_flow_rule pf;
                struct efx_tc_flow_rule wire;
        } dflt;
+       bool up;
 };
 
 struct efx_rep;
@@ -72,6 +77,8 @@ struct efx_rep;
 int efx_tc_configure_default_rule_rep(struct efx_rep *efv);
 void efx_tc_deconfigure_default_rule(struct efx_nic *efx,
                                     struct efx_tc_flow_rule *rule);
+int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev,
+                 struct flow_cls_offload *tc, struct efx_rep *efv);
 
 int efx_tc_insert_rep_filters(struct efx_nic *efx);
 void efx_tc_remove_rep_filters(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/tc_bindings.c b/drivers/net/ethernet/sfc/tc_bindings.c
new file mode 100644 (file)
index 0000000..d9401ee
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2022 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "tc_bindings.h"
+#include "tc.h"
+
+struct efx_tc_block_binding {
+       struct list_head list;
+       struct efx_nic *efx;
+       struct efx_rep *efv;
+       struct net_device *otherdev; /* may actually be us */
+       struct flow_block *block;
+};
+
+static struct efx_tc_block_binding *efx_tc_find_binding(struct efx_nic *efx,
+                                                       struct net_device *otherdev)
+{
+       struct efx_tc_block_binding *binding;
+
+       ASSERT_RTNL();
+       list_for_each_entry(binding, &efx->tc->block_list, list)
+               if (binding->otherdev == otherdev)
+                       return binding;
+       return NULL;
+}
+
+static int efx_tc_block_cb(enum tc_setup_type type, void *type_data,
+                          void *cb_priv)
+{
+       struct efx_tc_block_binding *binding = cb_priv;
+       struct flow_cls_offload *tcf = type_data;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return efx_tc_flower(binding->efx, binding->otherdev,
+                                    tcf, binding->efv);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void efx_tc_block_unbind(void *cb_priv)
+{
+       struct efx_tc_block_binding *binding = cb_priv;
+
+       list_del(&binding->list);
+       kfree(binding);
+}
+
+static struct efx_tc_block_binding *efx_tc_create_binding(
+                       struct efx_nic *efx, struct efx_rep *efv,
+                       struct net_device *otherdev, struct flow_block *block)
+{
+       struct efx_tc_block_binding *binding = kmalloc(sizeof(*binding), GFP_KERNEL);
+
+       if (!binding)
+               return ERR_PTR(-ENOMEM);
+       binding->efx = efx;
+       binding->efv = efv;
+       binding->otherdev = otherdev;
+       binding->block = block;
+       list_add(&binding->list, &efx->tc->block_list);
+       return binding;
+}
+
+int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx,
+                      struct flow_block_offload *tcb, struct efx_rep *efv)
+{
+       struct efx_tc_block_binding *binding;
+       struct flow_block_cb *block_cb;
+       int rc;
+
+       if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       if (WARN_ON(!efx->tc))
+               return -ENETDOWN;
+
+       switch (tcb->command) {
+       case FLOW_BLOCK_BIND:
+               binding = efx_tc_create_binding(efx, efv, net_dev, tcb->block);
+               if (IS_ERR(binding))
+                       return PTR_ERR(binding);
+               block_cb = flow_block_cb_alloc(efx_tc_block_cb, binding,
+                                              binding, efx_tc_block_unbind);
+               rc = PTR_ERR_OR_ZERO(block_cb);
+               netif_dbg(efx, drv, efx->net_dev,
+                         "bind %sdirect block for device %s, rc %d\n",
+                         net_dev == efx->net_dev ? "" :
+                         efv ? "semi" : "in",
+                         net_dev ? net_dev->name : NULL, rc);
+               if (rc) {
+                       list_del(&binding->list);
+                       kfree(binding);
+               } else {
+                       flow_block_cb_add(block_cb, tcb);
+               }
+               return rc;
+       case FLOW_BLOCK_UNBIND:
+               binding = efx_tc_find_binding(efx, net_dev);
+               if (binding) {
+                       block_cb = flow_block_cb_lookup(tcb->block,
+                                                       efx_tc_block_cb,
+                                                       binding);
+                       if (block_cb) {
+                               flow_block_cb_remove(block_cb, tcb);
+                               netif_dbg(efx, drv, efx->net_dev,
+                                         "unbound %sdirect block for device %s\n",
+                                         net_dev == efx->net_dev ? "" :
+                                         binding->efv ? "semi" : "in",
+                                         net_dev ? net_dev->name : NULL);
+                               return 0;
+                       }
+               }
+               /* If we're in driver teardown, then we expect to have
+                * already unbound all our blocks (we did it early while
+                * we still had MCDI to remove the filters), so getting
+                * unbind callbacks now isn't a problem.
+                */
+               netif_cond_dbg(efx, drv, efx->net_dev,
+                              !efx->tc->up, warn,
+                              "%sdirect block unbind for device %s, was never bound\n",
+                              net_dev == efx->net_dev ? "" : "in",
+                              net_dev ? net_dev->name : NULL);
+               return -ENOENT;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+/* .ndo_setup_tc implementation
+ * Entry point for flower block and filter management.
+ */
+int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,
+                void *type_data)
+{
+       struct efx_nic *efx = efx_netdev_priv(net_dev);
+
+       if (efx->type->is_vf)
+               return -EOPNOTSUPP;
+       if (!efx->tc)
+               return -EOPNOTSUPP;
+
+       if (type == TC_SETUP_CLSFLOWER)
+               return efx_tc_flower(efx, net_dev, type_data, NULL);
+       if (type == TC_SETUP_BLOCK)
+               return efx_tc_setup_block(net_dev, efx, type_data, NULL);
+
+       return -EOPNOTSUPP;
+}
diff --git a/drivers/net/ethernet/sfc/tc_bindings.h b/drivers/net/ethernet/sfc/tc_bindings.h
new file mode 100644 (file)
index 0000000..bcd63c2
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2022 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_TC_BINDINGS_H
+#define EFX_TC_BINDINGS_H
+#include "net_driver.h"
+
+#include <net/sch_generic.h>
+
+struct efx_rep;
+
+int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx,
+                      struct flow_block_offload *tcb, struct efx_rep *efv);
+int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,
+                void *type_data);
+#endif /* EFX_TC_BINDINGS_H */