From: John Hurley <john.hur...@netronome.com>

Compile set tunnel actions for tc flower. Only support VXLAN and ensure a
tunnel destination port of 4789 is used.

Signed-off-by: John Hurley <john.hur...@netronome.com>
Signed-off-by: Simon Horman <simon.hor...@netronome.com>
---
 drivers/net/ethernet/netronome/nfp/flower/action.c | 169 ++++++++++++++++++---
 drivers/net/ethernet/netronome/nfp/flower/cmsg.h   |  31 +++-
 2 files changed, 179 insertions(+), 21 deletions(-)

diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c 
b/drivers/net/ethernet/netronome/nfp/flower/action.c
index db9750695dc7..38f3835ae176 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/action.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/action.c
@@ -37,6 +37,7 @@
 #include <net/tc_act/tc_gact.h>
 #include <net/tc_act/tc_mirred.h>
 #include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_tunnel_key.h>
 
 #include "cmsg.h"
 #include "main.h"
@@ -80,14 +81,27 @@ nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
        push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci);
 }
 
+static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev,
+                                        enum nfp_flower_tun_type tun_type)
+{
+       if (!out_dev->rtnl_link_ops)
+               return false;
+
+       if (!strcmp(out_dev->rtnl_link_ops->kind, "vxlan"))
+               return tun_type == NFP_FL_TUNNEL_VXLAN;
+
+       return false;
+}
+
 static int
 nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action,
              struct nfp_fl_payload *nfp_flow, bool last,
-             struct net_device *in_dev)
+             struct net_device *in_dev, enum nfp_flower_tun_type tun_type,
+             int *tun_out_cnt)
 {
        size_t act_size = sizeof(struct nfp_fl_output);
+       u16 tmp_output_op, tmp_flags;
        struct net_device *out_dev;
-       u16 tmp_output_op;
        int ifindex;
 
        /* Set action opcode to output action. */
@@ -97,25 +111,114 @@ nfp_fl_output(struct nfp_fl_output *output, const struct 
tc_action *action,
 
        output->a_op = cpu_to_be16(tmp_output_op);
 
-       /* Set action output parameters. */
-       output->flags = cpu_to_be16(last ? NFP_FL_OUT_FLAGS_LAST : 0);
-
        ifindex = tcf_mirred_ifindex(action);
        out_dev = __dev_get_by_index(dev_net(in_dev), ifindex);
        if (!out_dev)
                return -EOPNOTSUPP;
 
-       /* Only offload egress ports are on the same device as the ingress
-        * port.
+       tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0;
+
+       if (tun_type) {
+               /* Verify the egress netdev matches the tunnel type. */
+               if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type))
+                       return -EOPNOTSUPP;
+
+               if (*tun_out_cnt)
+                       return -EOPNOTSUPP;
+               (*tun_out_cnt)++;
+
+               output->flags = cpu_to_be16(tmp_flags |
+                                           NFP_FL_OUT_FLAGS_USE_TUN);
+               output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
+       } else {
+               /* Set action output parameters. */
+               output->flags = cpu_to_be16(tmp_flags);
+
+               /* Only offload if egress ports are on the same device as the
+                * ingress port.
+                */
+               if (!switchdev_port_same_parent_id(in_dev, out_dev))
+                       return -EOPNOTSUPP;
+
+               output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
+               if (!output->port)
+                       return -EOPNOTSUPP;
+       }
+       nfp_flow->meta.shortcut = output->port;
+
+       return 0;
+}
+
+static bool nfp_fl_supported_tun_port(const struct tc_action *action)
+{
+       struct ip_tunnel_info *tun = tcf_tunnel_info(action);
+
+       return tun->key.tp_dst == htons(NFP_FL_VXLAN_PORT);
+}
+
+static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
+{
+       size_t act_size = sizeof(struct nfp_fl_pre_tunnel);
+       struct nfp_fl_pre_tunnel *pre_tun_act;
+       u16 tmp_pre_tun_op;
+
+       /* Pre_tunnel action must be first on action list.
+        * If other actions already exist they need pushed forward.
         */
-       if (!switchdev_port_same_parent_id(in_dev, out_dev))
-               return -EOPNOTSUPP;
+       if (act_len)
+               memmove(act_data + act_size, act_data, act_len);
+
+       pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data;
+
+       memset(pre_tun_act, 0, act_size);
+
+       tmp_pre_tun_op =
+               FIELD_PREP(NFP_FL_ACT_LEN_LW, act_size >> NFP_FL_LW_SIZ) |
+               FIELD_PREP(NFP_FL_ACT_JMP_ID, NFP_FL_ACTION_OPCODE_PRE_TUNNEL);
+
+       pre_tun_act->a_op = cpu_to_be16(tmp_pre_tun_op);
 
-       output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
-       if (!output->port)
+       return pre_tun_act;
+}
+
+static int
+nfp_fl_set_vxlan(struct nfp_fl_set_vxlan *set_vxlan,
+                const struct tc_action *action,
+                struct nfp_fl_pre_tunnel *pre_tun)
+{
+       struct ip_tunnel_info *vxlan = tcf_tunnel_info(action);
+       size_t act_size = sizeof(struct nfp_fl_set_vxlan);
+       u32 tmp_set_vxlan_type_index = 0;
+       u16 tmp_set_vxlan_op;
+       /* Currently support one pre-tunnel so index is always 0. */
+       int pretun_idx = 0;
+
+       if (vxlan->options_len) {
+               /* Do not support options e.g. vxlan gpe. */
                return -EOPNOTSUPP;
+       }
 
-       nfp_flow->meta.shortcut = output->port;
+       tmp_set_vxlan_op =
+               FIELD_PREP(NFP_FL_ACT_LEN_LW, act_size >> NFP_FL_LW_SIZ) |
+               FIELD_PREP(NFP_FL_ACT_JMP_ID,
+                          NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL);
+
+       set_vxlan->a_op = cpu_to_be16(tmp_set_vxlan_op);
+
+       /* Set tunnel type and pre-tunnel index. */
+       tmp_set_vxlan_type_index |=
+               FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, NFP_FL_TUNNEL_VXLAN) |
+               FIELD_PREP(NFP_FL_IPV4_PRE_TUN_INDEX, pretun_idx);
+
+       set_vxlan->tun_type_index = cpu_to_be32(tmp_set_vxlan_type_index);
+
+       set_vxlan->tun_id = vxlan->key.tun_id;
+       set_vxlan->tun_flags = vxlan->key.tun_flags;
+       set_vxlan->ipv4_ttl = vxlan->key.ttl;
+       set_vxlan->ipv4_tos = vxlan->key.tos;
+
+       /* Complete pre_tunnel action. */
+       pre_tun->ipv4_dst = vxlan->key.u.ipv4.dst;
 
        return 0;
 }
@@ -123,8 +226,11 @@ nfp_fl_output(struct nfp_fl_output *output, const struct 
tc_action *action,
 static int
 nfp_flower_loop_action(const struct tc_action *a,
                       struct nfp_fl_payload *nfp_fl, int *a_len,
-                      struct net_device *netdev)
+                      struct net_device *netdev,
+                      enum nfp_flower_tun_type *tun_type, int *tun_out_cnt)
 {
+       struct nfp_fl_pre_tunnel *pre_tun;
+       struct nfp_fl_set_vxlan *s_vxl;
        struct nfp_fl_push_vlan *psh_v;
        struct nfp_fl_pop_vlan *pop_v;
        struct nfp_fl_output *output;
@@ -137,7 +243,8 @@ nfp_flower_loop_action(const struct tc_action *a,
                        return -EOPNOTSUPP;
 
                output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
-               err = nfp_fl_output(output, a, nfp_fl, true, netdev);
+               err = nfp_fl_output(output, a, nfp_fl, true, netdev, *tun_type,
+                                   tun_out_cnt);
                if (err)
                        return err;
 
@@ -147,7 +254,8 @@ nfp_flower_loop_action(const struct tc_action *a,
                        return -EOPNOTSUPP;
 
                output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
-               err = nfp_fl_output(output, a, nfp_fl, false, netdev);
+               err = nfp_fl_output(output, a, nfp_fl, false, netdev, *tun_type,
+                                   tun_out_cnt);
                if (err)
                        return err;
 
@@ -170,6 +278,29 @@ nfp_flower_loop_action(const struct tc_action *a,
 
                nfp_fl_push_vlan(psh_v, a);
                *a_len += sizeof(struct nfp_fl_push_vlan);
+       } else if (is_tcf_tunnel_set(a) && nfp_fl_supported_tun_port(a)) {
+               /* Pre-tunnel action is required for tunnel encap.
+                * This checks for next hop entries on NFP.
+                * If none, the packet falls back before applying other actions.
+                */
+               if (*a_len + sizeof(struct nfp_fl_pre_tunnel) +
+                   sizeof(struct nfp_fl_set_vxlan) > NFP_FL_MAX_A_SIZ)
+                       return -EOPNOTSUPP;
+
+               *tun_type = NFP_FL_TUNNEL_VXLAN;
+               pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len);
+               nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
+               *a_len += sizeof(struct nfp_fl_pre_tunnel);
+
+               s_vxl = (struct nfp_fl_set_vxlan *)&nfp_fl->action_data[*a_len];
+               err = nfp_fl_set_vxlan(s_vxl, a, pre_tun);
+               if (err)
+                       return err;
+
+               *a_len += sizeof(struct nfp_fl_set_vxlan);
+       } else if (is_tcf_tunnel_release(a)) {
+               /* Tunnel decap is handled by default so accept action. */
+               return 0;
        } else {
                /* Currently we do not handle any other actions. */
                return -EOPNOTSUPP;
@@ -182,18 +313,22 @@ int nfp_flower_compile_action(struct 
tc_cls_flower_offload *flow,
                              struct net_device *netdev,
                              struct nfp_fl_payload *nfp_flow)
 {
-       int act_len, act_cnt, err;
+       int act_len, act_cnt, err, tun_out_cnt;
+       enum nfp_flower_tun_type tun_type;
        const struct tc_action *a;
        LIST_HEAD(actions);
 
        memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ);
        nfp_flow->meta.act_len = 0;
+       tun_type = NFP_FL_TUNNEL_NONE;
        act_len = 0;
        act_cnt = 0;
+       tun_out_cnt = 0;
 
        tcf_exts_to_list(flow->exts, &actions);
        list_for_each_entry(a, &actions, list) {
-               err = nfp_flower_loop_action(a, nfp_flow, &act_len, netdev);
+               err = nfp_flower_loop_action(a, nfp_flow, &act_len, netdev,
+                                            &tun_type, &tun_out_cnt);
                if (err)
                        return err;
                act_cnt++;
diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h 
b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
index af9165b3b652..ff42ce8a1e9c 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
+++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
@@ -67,10 +67,12 @@
 #define NFP_FL_LW_SIZ                  2
 
 /* Action opcodes */
-#define NFP_FL_ACTION_OPCODE_OUTPUT    0
-#define NFP_FL_ACTION_OPCODE_PUSH_VLAN 1
-#define NFP_FL_ACTION_OPCODE_POP_VLAN  2
-#define NFP_FL_ACTION_OPCODE_NUM       32
+#define NFP_FL_ACTION_OPCODE_OUTPUT            0
+#define NFP_FL_ACTION_OPCODE_PUSH_VLAN         1
+#define NFP_FL_ACTION_OPCODE_POP_VLAN          2
+#define NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL   6
+#define NFP_FL_ACTION_OPCODE_PRE_TUNNEL                17
+#define NFP_FL_ACTION_OPCODE_NUM               32
 
 #define NFP_FL_ACT_JMP_ID              GENMASK(15, 8)
 #define NFP_FL_ACT_LEN_LW              GENMASK(7, 0)
@@ -85,6 +87,8 @@
 
 /* Tunnel ports */
 #define NFP_FL_PORT_TYPE_TUN           0x50000000
+#define NFP_FL_IPV4_TUNNEL_TYPE                GENMASK(7, 4)
+#define NFP_FL_IPV4_PRE_TUN_INDEX      GENMASK(2, 0)
 
 enum nfp_flower_tun_type {
        NFP_FL_TUNNEL_NONE =    0,
@@ -123,6 +127,25 @@ struct nfp_flower_meta_one {
        u16 reserved;
 };
 
+struct nfp_fl_pre_tunnel {
+       __be16 a_op;
+       __be16 reserved;
+       __be32 ipv4_dst;
+       /* reserved for use with IPv6 addresses */
+       __be32 extra[3];
+};
+
+struct nfp_fl_set_vxlan {
+       __be16 a_op;
+       __be16 reserved;
+       __be64 tun_id;
+       __be32 tun_type_index;
+       __be16 tun_flags;
+       u8 ipv4_ttl;
+       u8 ipv4_tos;
+       __be32 extra[2];
+} __packed;
+
 /* Metadata with L2 (1W/4B)
  * ----------------------------------------------------------------
  *    3                   2                   1
-- 
2.1.4

Reply via email to