nfp: flower: add ipv6 set flow label and hop limit offload
authorPieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Wed, 7 Nov 2018 01:07:29 +0000 (17:07 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 Nov 2018 19:45:21 +0000 (11:45 -0800)
Add ipv6 set flow label and hop limit action offload. Since pedit sets
headers per 4 byte word, we need to ensure that setting either version,
priority, payload_len or nexthdr does not get offloaded.

Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/flower/action.c
drivers/net/ethernet/netronome/nfp/flower/cmsg.h

index b79b924ef56de30d91b3f02eaceb0e314741648f..cfea8f790f957c366604758b45254fef1aaa9505 100644 (file)
@@ -476,12 +476,57 @@ nfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask,
        ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
 }
 
+struct ipv6_hop_limit_word {
+       __be16 payload_len;
+       u8 nexthdr;
+       u8 hop_limit;
+};
+
+static int
+nfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask,
+                                   struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl)
+{
+       struct ipv6_hop_limit_word *fl_hl_mask;
+       struct ipv6_hop_limit_word *fl_hl;
+
+       switch (off) {
+       case offsetof(struct ipv6hdr, payload_len):
+               fl_hl_mask = (struct ipv6_hop_limit_word *)&mask;
+               fl_hl = (struct ipv6_hop_limit_word *)&exact;
+
+               if (fl_hl_mask->nexthdr || fl_hl_mask->payload_len)
+                       return -EOPNOTSUPP;
+
+               ip_hl_fl->ipv6_hop_limit_mask |= fl_hl_mask->hop_limit;
+               ip_hl_fl->ipv6_hop_limit &= ~fl_hl_mask->hop_limit;
+               ip_hl_fl->ipv6_hop_limit |= fl_hl->hop_limit &
+                                           fl_hl_mask->hop_limit;
+               break;
+       case round_down(offsetof(struct ipv6hdr, flow_lbl), 4):
+               if (mask & ~IPV6_FLOW_LABEL_MASK ||
+                   exact & ~IPV6_FLOW_LABEL_MASK)
+                       return -EOPNOTSUPP;
+
+               ip_hl_fl->ipv6_label_mask |= mask;
+               ip_hl_fl->ipv6_label &= ~mask;
+               ip_hl_fl->ipv6_label |= exact & mask;
+               break;
+       }
+
+       ip_hl_fl->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL;
+       ip_hl_fl->head.len_lw = sizeof(*ip_hl_fl) >> NFP_FL_LW_SIZ;
+
+       return 0;
+}
+
 static int
 nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
               struct nfp_fl_set_ipv6_addr *ip_dst,
-              struct nfp_fl_set_ipv6_addr *ip_src)
+              struct nfp_fl_set_ipv6_addr *ip_src,
+              struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl)
 {
        __be32 exact, mask;
+       int err = 0;
        u8 word;
 
        /* We are expecting tcf_pedit to return a big endian value */
@@ -492,7 +537,8 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
                return -EOPNOTSUPP;
 
        if (off < offsetof(struct ipv6hdr, saddr)) {
-               return -EOPNOTSUPP;
+               err = nfp_fl_set_ip6_hop_limit_flow_label(off, exact, mask,
+                                                         ip_hl_fl);
        } else if (off < offsetof(struct ipv6hdr, daddr)) {
                word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact);
                nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word,
@@ -506,7 +552,7 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
                return -EOPNOTSUPP;
        }
 
-       return 0;
+       return err;
 }
 
 static int
@@ -557,6 +603,7 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
             char *nfp_action, int *a_len, u32 *csum_updated)
 {
        struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
+       struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl;
        struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos;
        struct nfp_fl_set_ip4_addrs set_ip_addr;
        struct nfp_fl_set_tport set_tport;
@@ -567,6 +614,7 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
        u32 offset, cmd;
        u8 ip_proto = 0;
 
+       memset(&set_ip6_tc_hl_fl, 0, sizeof(set_ip6_tc_hl_fl));
        memset(&set_ip_ttl_tos, 0, sizeof(set_ip_ttl_tos));
        memset(&set_ip6_dst, 0, sizeof(set_ip6_dst));
        memset(&set_ip6_src, 0, sizeof(set_ip6_src));
@@ -593,7 +641,7 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
                        err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
-                                            &set_ip6_src);
+                                            &set_ip6_src, &set_ip6_tc_hl_fl);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
                        err = nfp_fl_set_tport(action, idx, offset, &set_tport,
@@ -644,6 +692,15 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
                                nfp_fl_csum_l4_to_flag(ip_proto);
        }
+       if (set_ip6_tc_hl_fl.head.len_lw) {
+               nfp_action += act_size;
+               act_size = sizeof(set_ip6_tc_hl_fl);
+               memcpy(nfp_action, &set_ip6_tc_hl_fl, act_size);
+               *a_len += act_size;
+
+               /* Hardware will automatically fix TCP/UDP checksum. */
+               *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
+       }
        if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) {
                /* TC compiles set src and dst IPv6 address as a single action,
                 * the hardware requires this to be 2 separate actions.
index a00f45b5e16c0b052b7b088a23f71ce005b544fe..3e391555e191219f4b1c909b86eab986682ea4ae 100644 (file)
@@ -68,6 +68,7 @@
 #define NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS  10
 #define NFP_FL_ACTION_OPCODE_SET_IPV6_SRC      11
 #define NFP_FL_ACTION_OPCODE_SET_IPV6_DST      12
+#define NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL 13
 #define NFP_FL_ACTION_OPCODE_SET_UDP           14
 #define NFP_FL_ACTION_OPCODE_SET_TCP           15
 #define NFP_FL_ACTION_OPCODE_PRE_LAG           16
@@ -83,6 +84,8 @@
 #define NFP_FL_PUSH_VLAN_CFI           BIT(12)
 #define NFP_FL_PUSH_VLAN_VID           GENMASK(11, 0)
 
+#define IPV6_FLOW_LABEL_MASK           cpu_to_be32(0x000fffff)
+
 /* LAG ports */
 #define NFP_FL_LAG_OUT                 0xC0DE0000
 
@@ -135,6 +138,17 @@ struct nfp_fl_set_ip4_ttl_tos {
        __be16 reserved;
 };
 
+struct nfp_fl_set_ipv6_tc_hl_fl {
+       struct nfp_fl_act_head head;
+       u8 ipv6_tc_mask;
+       u8 ipv6_hop_limit_mask;
+       __be16 reserved;
+       u8 ipv6_tc;
+       u8 ipv6_hop_limit;
+       __be32 ipv6_label_mask;
+       __be32 ipv6_label;
+};
+
 struct nfp_fl_set_ipv6_addr {
        struct nfp_fl_act_head head;
        __be16 reserved;