nfp: flower: add ipv4 set ttl and tos offload
authorPieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Wed, 7 Nov 2018 01:07:28 +0000 (17:07 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 Nov 2018 19:45:21 +0000 (11:45 -0800)
Add ipv4 set ttl and tos action offload. Since pedit sets headers per 4
byte word, we need to ensure that setting either version, ihl, protocol,
total length or checksum does not get offloaded.

Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Reviewed-by: John Hurley <john.hurley@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 244dc261006e7a021f57650deb2eb2e2c77c6c64..b79b924ef56de30d91b3f02eaceb0e314741648f 100644 (file)
@@ -384,10 +384,21 @@ nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
        return 0;
 }
 
+struct ipv4_ttl_word {
+       __u8    ttl;
+       __u8    protocol;
+       __sum16 check;
+};
+
 static int
 nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
-              struct nfp_fl_set_ip4_addrs *set_ip_addr)
+              struct nfp_fl_set_ip4_addrs *set_ip_addr,
+              struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos)
 {
+       struct ipv4_ttl_word *ttl_word_mask;
+       struct ipv4_ttl_word *ttl_word;
+       struct iphdr *tos_word_mask;
+       struct iphdr *tos_word;
        __be32 exact, mask;
 
        /* We are expecting tcf_pedit to return a big endian value */
@@ -402,20 +413,53 @@ nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
                set_ip_addr->ipv4_dst_mask |= mask;
                set_ip_addr->ipv4_dst &= ~mask;
                set_ip_addr->ipv4_dst |= exact & mask;
+               set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
+               set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
+                                          NFP_FL_LW_SIZ;
                break;
        case offsetof(struct iphdr, saddr):
                set_ip_addr->ipv4_src_mask |= mask;
                set_ip_addr->ipv4_src &= ~mask;
                set_ip_addr->ipv4_src |= exact & mask;
+               set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
+               set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
+                                          NFP_FL_LW_SIZ;
+               break;
+       case offsetof(struct iphdr, ttl):
+               ttl_word_mask = (struct ipv4_ttl_word *)&mask;
+               ttl_word = (struct ipv4_ttl_word *)&exact;
+
+               if (ttl_word_mask->protocol || ttl_word_mask->check)
+                       return -EOPNOTSUPP;
+
+               set_ip_ttl_tos->ipv4_ttl_mask |= ttl_word_mask->ttl;
+               set_ip_ttl_tos->ipv4_ttl &= ~ttl_word_mask->ttl;
+               set_ip_ttl_tos->ipv4_ttl |= ttl_word->ttl & ttl_word_mask->ttl;
+               set_ip_ttl_tos->head.jump_id =
+                       NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
+               set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
+                                             NFP_FL_LW_SIZ;
+               break;
+       case round_down(offsetof(struct iphdr, tos), 4):
+               tos_word_mask = (struct iphdr *)&mask;
+               tos_word = (struct iphdr *)&exact;
+
+               if (tos_word_mask->version || tos_word_mask->ihl ||
+                   tos_word_mask->tot_len)
+                       return -EOPNOTSUPP;
+
+               set_ip_ttl_tos->ipv4_tos_mask |= tos_word_mask->tos;
+               set_ip_ttl_tos->ipv4_tos &= ~tos_word_mask->tos;
+               set_ip_ttl_tos->ipv4_tos |= tos_word->tos & tos_word_mask->tos;
+               set_ip_ttl_tos->head.jump_id =
+                       NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
+               set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
+                                             NFP_FL_LW_SIZ;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       set_ip_addr->reserved = cpu_to_be16(0);
-       set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
-       set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> NFP_FL_LW_SIZ;
-
        return 0;
 }
 
@@ -513,6 +557,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_ip4_ttl_tos set_ip_ttl_tos;
        struct nfp_fl_set_ip4_addrs set_ip_addr;
        struct nfp_fl_set_tport set_tport;
        struct nfp_fl_set_eth set_eth;
@@ -522,6 +567,7 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
        u32 offset, cmd;
        u8 ip_proto = 0;
 
+       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));
        memset(&set_ip_addr, 0, sizeof(set_ip_addr));
@@ -542,7 +588,8 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                        err = nfp_fl_set_eth(action, idx, offset, &set_eth);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
-                       err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr);
+                       err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr,
+                                            &set_ip_ttl_tos);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
                        err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
@@ -577,6 +624,16 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                memcpy(nfp_action, &set_eth, act_size);
                *a_len += act_size;
        }
+       if (set_ip_ttl_tos.head.len_lw) {
+               nfp_action += act_size;
+               act_size = sizeof(set_ip_ttl_tos);
+               memcpy(nfp_action, &set_ip_ttl_tos, act_size);
+               *a_len += act_size;
+
+               /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
+               *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
+                               nfp_fl_csum_l4_to_flag(ip_proto);
+       }
        if (set_ip_addr.head.len_lw) {
                nfp_action += act_size;
                act_size = sizeof(set_ip_addr);
index 29d673aa52779a786901806f5ce235c81fbf727d..a00f45b5e16c0b052b7b088a23f71ce005b544fe 100644 (file)
@@ -65,6 +65,7 @@
 #define NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL   6
 #define NFP_FL_ACTION_OPCODE_SET_ETHERNET      7
 #define NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS    9
+#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_UDP           14
@@ -125,6 +126,15 @@ struct nfp_fl_set_ip4_addrs {
        __be32 ipv4_dst;
 };
 
+struct nfp_fl_set_ip4_ttl_tos {
+       struct nfp_fl_act_head head;
+       u8 ipv4_ttl_mask;
+       u8 ipv4_tos_mask;
+       u8 ipv4_ttl;
+       u8 ipv4_tos;
+       __be16 reserved;
+};
+
 struct nfp_fl_set_ipv6_addr {
        struct nfp_fl_act_head head;
        __be16 reserved;