nfp: flower: allow offloading of matches on 'internal' ports
authorJohn Hurley <john.hurley@netronome.com>
Mon, 15 Apr 2019 14:55:54 +0000 (16:55 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 15 Apr 2019 22:45:36 +0000 (15:45 -0700)
Recent FW modifications allow the offloading of non repr ports. These
ports exist internally on the NFP. So if a rule outputs to an 'internal'
port, then the packet will recirculate back into the system but will now
have this internal port as it's incoming port. These ports are indicated
by a specific type field combined with an 8 bit port id.

Add private app data to assign additional port ids for use in offloads.
Provide functions to lookup or create new ids when a rule attempts to
match on an internal netdev - the only internal netdevs currently
supported are of type openvswitch. Have a netdev notifier to release
port ids on netdev unregister.

OvS offloads rules that match on internal ports as TC egress filters.
Ensure that such rules are accepted by the driver.

Signed-off-by: John Hurley <john.hurley@netronome.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/flower/cmsg.h
drivers/net/ethernet/netronome/nfp/flower/main.c
drivers/net/ethernet/netronome/nfp/flower/main.h
drivers/net/ethernet/netronome/nfp/flower/match.c
drivers/net/ethernet/netronome/nfp/flower/offload.c

index cf4ab10..41a2290 100644 (file)
@@ -474,6 +474,13 @@ enum nfp_flower_cmsg_port_vnic_type {
 #define NFP_FLOWER_CMSG_PORT_PCIE_Q            GENMASK(5, 0)
 #define NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM     GENMASK(7, 0)
 
+static inline u32 nfp_flower_internal_port_get_port_id(u8 internal_port)
+{
+       return FIELD_PREP(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM, internal_port) |
+               FIELD_PREP(NFP_FLOWER_CMSG_PORT_TYPE,
+                          NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT);
+}
+
 static inline u32 nfp_flower_cmsg_phys_port(u8 phys_port)
 {
        return FIELD_PREP(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM, phys_port) |
index 1569fb6..d0d8c56 100644 (file)
@@ -22,6 +22,9 @@
 
 #define NFP_FLOWER_ALLOWED_VER 0x0001000000010000UL
 
+#define NFP_MIN_INT_PORT_ID    1
+#define NFP_MAX_INT_PORT_ID    256
+
 static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn)
 {
        return "FLOWER";
@@ -32,6 +35,100 @@ static enum devlink_eswitch_mode eswitch_mode_get(struct nfp_app *app)
        return DEVLINK_ESWITCH_MODE_SWITCHDEV;
 }
 
+static int
+nfp_flower_lookup_internal_port_id(struct nfp_flower_priv *priv,
+                                  struct net_device *netdev)
+{
+       struct net_device *entry;
+       int i, id = 0;
+
+       rcu_read_lock();
+       idr_for_each_entry(&priv->internal_ports.port_ids, entry, i)
+               if (entry == netdev) {
+                       id = i;
+                       break;
+               }
+       rcu_read_unlock();
+
+       return id;
+}
+
+static int
+nfp_flower_get_internal_port_id(struct nfp_app *app, struct net_device *netdev)
+{
+       struct nfp_flower_priv *priv = app->priv;
+       int id;
+
+       id = nfp_flower_lookup_internal_port_id(priv, netdev);
+       if (id > 0)
+               return id;
+
+       idr_preload(GFP_ATOMIC);
+       spin_lock_bh(&priv->internal_ports.lock);
+       id = idr_alloc(&priv->internal_ports.port_ids, netdev,
+                      NFP_MIN_INT_PORT_ID, NFP_MAX_INT_PORT_ID, GFP_ATOMIC);
+       spin_unlock_bh(&priv->internal_ports.lock);
+       idr_preload_end();
+
+       return id;
+}
+
+u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app,
+                                      struct net_device *netdev)
+{
+       int ext_port;
+
+       if (nfp_netdev_is_nfp_repr(netdev)) {
+               return nfp_repr_get_port_id(netdev);
+       } else if (nfp_flower_internal_port_can_offload(app, netdev)) {
+               ext_port = nfp_flower_get_internal_port_id(app, netdev);
+               if (ext_port < 0)
+                       return 0;
+
+               return nfp_flower_internal_port_get_port_id(ext_port);
+       }
+
+       return 0;
+}
+
+static void
+nfp_flower_free_internal_port_id(struct nfp_app *app, struct net_device *netdev)
+{
+       struct nfp_flower_priv *priv = app->priv;
+       int id;
+
+       id = nfp_flower_lookup_internal_port_id(priv, netdev);
+       if (!id)
+               return;
+
+       spin_lock_bh(&priv->internal_ports.lock);
+       idr_remove(&priv->internal_ports.port_ids, id);
+       spin_unlock_bh(&priv->internal_ports.lock);
+}
+
+static int
+nfp_flower_internal_port_event_handler(struct nfp_app *app,
+                                      struct net_device *netdev,
+                                      unsigned long event)
+{
+       if (event == NETDEV_UNREGISTER &&
+           nfp_flower_internal_port_can_offload(app, netdev))
+               nfp_flower_free_internal_port_id(app, netdev);
+
+       return NOTIFY_OK;
+}
+
+static void nfp_flower_internal_port_init(struct nfp_flower_priv *priv)
+{
+       spin_lock_init(&priv->internal_ports.lock);
+       idr_init(&priv->internal_ports.port_ids);
+}
+
+static void nfp_flower_internal_port_cleanup(struct nfp_flower_priv *priv)
+{
+       idr_destroy(&priv->internal_ports.port_ids);
+}
+
 static struct nfp_flower_non_repr_priv *
 nfp_flower_non_repr_priv_lookup(struct nfp_app *app, struct net_device *netdev)
 {
@@ -645,12 +742,14 @@ static int nfp_flower_init(struct nfp_app *app)
                /* Tell the firmware that the driver supports flow merging. */
                err = nfp_rtsym_write_le(app->pf->rtbl,
                                         "_abi_flower_merge_hint_enable", 1);
-               if (!err)
+               if (!err) {
                        app_priv->flower_ext_feats |= NFP_FL_FEATS_FLOW_MERGE;
-               else if (err == -ENOENT)
+                       nfp_flower_internal_port_init(app_priv);
+               } else if (err == -ENOENT) {
                        nfp_warn(app->cpp, "Flow merge not supported by FW.\n");
-               else
+               } else {
                        goto err_lag_clean;
+               }
        } else {
                nfp_warn(app->cpp, "Flow mod/merge not supported by FW.\n");
        }
@@ -681,6 +780,9 @@ static void nfp_flower_clean(struct nfp_app *app)
        if (app_priv->flower_ext_feats & NFP_FL_FEATS_LAG)
                nfp_flower_lag_cleanup(&app_priv->nfp_lag);
 
+       if (app_priv->flower_ext_feats & NFP_FL_FEATS_FLOW_MERGE)
+               nfp_flower_internal_port_cleanup(app_priv);
+
        nfp_flower_metadata_cleanup(app);
        vfree(app->priv);
        app->priv = NULL;
@@ -779,6 +881,10 @@ nfp_flower_netdev_event(struct nfp_app *app, struct net_device *netdev,
        if (ret & NOTIFY_STOP_MASK)
                return ret;
 
+       ret = nfp_flower_internal_port_event_handler(app, netdev, event);
+       if (ret & NOTIFY_STOP_MASK)
+               return ret;
+
        return nfp_tunnel_mac_event_handler(app, netdev, event, ptr);
 }
 
index f557008..485bdc0 100644 (file)
@@ -116,6 +116,16 @@ struct nfp_fl_lag {
        struct sk_buff_head retrans_skbs;
 };
 
+/**
+ * struct nfp_fl_internal_ports - Flower APP priv data for additional ports
+ * @port_ids:  Assignment of ids to any additional ports
+ * @lock:      Lock for extra ports list
+ */
+struct nfp_fl_internal_ports {
+       struct idr port_ids;
+       spinlock_t lock;
+};
+
 /**
  * struct nfp_flower_priv - Flower APP per-vNIC priv data
  * @app:               Back pointer to app
@@ -145,6 +155,7 @@ struct nfp_fl_lag {
  * @non_repr_priv:     List of offloaded non-repr ports and their priv data
  * @active_mem_unit:   Current active memory unit for flower rules
  * @total_mem_units:   Total number of available memory units for flower rules
+ * @internal_ports:    Internal port ids used in offloaded rules
  */
 struct nfp_flower_priv {
        struct nfp_app *app;
@@ -171,6 +182,7 @@ struct nfp_flower_priv {
        struct list_head non_repr_priv;
        unsigned int active_mem_unit;
        unsigned int total_mem_units;
+       struct nfp_fl_internal_ports internal_ports;
 };
 
 /**
@@ -249,6 +261,22 @@ struct nfp_fl_stats_frame {
        __be64 stats_cookie;
 };
 
+static inline bool
+nfp_flower_internal_port_can_offload(struct nfp_app *app,
+                                    struct net_device *netdev)
+{
+       struct nfp_flower_priv *app_priv = app->priv;
+
+       if (!(app_priv->flower_ext_feats & NFP_FL_FEATS_FLOW_MERGE))
+               return false;
+       if (!netdev->rtnl_link_ops)
+               return false;
+       if (!strcmp(netdev->rtnl_link_ops->kind, "openvswitch"))
+               return true;
+
+       return false;
+}
+
 int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
                             unsigned int host_ctx_split);
 void nfp_flower_metadata_cleanup(struct nfp_app *app);
@@ -313,4 +341,6 @@ void
 __nfp_flower_non_repr_priv_put(struct nfp_flower_non_repr_priv *non_repr_priv);
 void
 nfp_flower_non_repr_priv_put(struct nfp_app *app, struct net_device *netdev);
+u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app,
+                                      struct net_device *netdev);
 #endif
index 9b8b843..bfa4bf3 100644 (file)
@@ -326,13 +326,12 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
                                  struct nfp_fl_payload *nfp_flow,
                                  enum nfp_flower_tun_type tun_type)
 {
-       u32 cmsg_port = 0;
+       u32 port_id;
        int err;
        u8 *ext;
        u8 *msk;
 
-       if (nfp_netdev_is_nfp_repr(netdev))
-               cmsg_port = nfp_repr_get_port_id(netdev);
+       port_id = nfp_flower_get_port_id_from_netdev(app, netdev);
 
        memset(nfp_flow->unmasked_data, 0, key_ls->key_size);
        memset(nfp_flow->mask_data, 0, key_ls->key_size);
@@ -358,13 +357,13 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
 
        /* Populate Exact Port data. */
        err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext,
-                                     cmsg_port, false, tun_type);
+                                     port_id, false, tun_type);
        if (err)
                return err;
 
        /* Populate Mask Port Data. */
        err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk,
-                                     cmsg_port, true, tun_type);
+                                     port_id, true, tun_type);
        if (err)
                return err;
 
index 9f16920..af406e6 100644 (file)
@@ -682,7 +682,9 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app,
        struct nfp_flower_priv *priv = app->priv;
        int err;
 
-       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
+           !(f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS &&
+             nfp_flower_internal_port_can_offload(app, netdev)))
                return -EOPNOTSUPP;
 
        switch (f->command) {