linux/drivers/net/ethernet/netronome/nfp/flower/action.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Netronome Systems, Inc.
   3 *
   4 * This software is dual licensed under the GNU General License Version 2,
   5 * June 1991 as shown in the file COPYING in the top-level directory of this
   6 * source tree or the BSD 2-Clause License provided below.  You have the
   7 * option to license this software under the complete terms of either license.
   8 *
   9 * The BSD 2-Clause License:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      1. Redistributions of source code must retain the above
  16 *         copyright notice, this list of conditions and the following
  17 *         disclaimer.
  18 *
  19 *      2. Redistributions in binary form must reproduce the above
  20 *         copyright notice, this list of conditions and the following
  21 *         disclaimer in the documentation and/or other materials
  22 *         provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include <linux/bitfield.h>
  35#include <net/pkt_cls.h>
  36#include <net/switchdev.h>
  37#include <net/tc_act/tc_gact.h>
  38#include <net/tc_act/tc_mirred.h>
  39#include <net/tc_act/tc_pedit.h>
  40#include <net/tc_act/tc_vlan.h>
  41#include <net/tc_act/tc_tunnel_key.h>
  42
  43#include "cmsg.h"
  44#include "main.h"
  45#include "../nfp_net_repr.h"
  46
  47static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan)
  48{
  49        size_t act_size = sizeof(struct nfp_fl_pop_vlan);
  50
  51        pop_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_POP_VLAN;
  52        pop_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
  53        pop_vlan->reserved = 0;
  54}
  55
  56static void
  57nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
  58                 const struct tc_action *action)
  59{
  60        size_t act_size = sizeof(struct nfp_fl_push_vlan);
  61        u16 tmp_push_vlan_tci;
  62
  63        push_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_VLAN;
  64        push_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
  65        push_vlan->reserved = 0;
  66        push_vlan->vlan_tpid = tcf_vlan_push_proto(action);
  67
  68        tmp_push_vlan_tci =
  69                FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, tcf_vlan_push_prio(action)) |
  70                FIELD_PREP(NFP_FL_PUSH_VLAN_VID, tcf_vlan_push_vid(action)) |
  71                NFP_FL_PUSH_VLAN_CFI;
  72        push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci);
  73}
  74
  75static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev,
  76                                         enum nfp_flower_tun_type tun_type)
  77{
  78        if (!out_dev->rtnl_link_ops)
  79                return false;
  80
  81        if (!strcmp(out_dev->rtnl_link_ops->kind, "vxlan"))
  82                return tun_type == NFP_FL_TUNNEL_VXLAN;
  83
  84        if (!strcmp(out_dev->rtnl_link_ops->kind, "geneve"))
  85                return tun_type == NFP_FL_TUNNEL_GENEVE;
  86
  87        return false;
  88}
  89
  90static int
  91nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action,
  92              struct nfp_fl_payload *nfp_flow, bool last,
  93              struct net_device *in_dev, enum nfp_flower_tun_type tun_type,
  94              int *tun_out_cnt)
  95{
  96        size_t act_size = sizeof(struct nfp_fl_output);
  97        struct net_device *out_dev;
  98        u16 tmp_flags;
  99
 100        output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT;
 101        output->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 102
 103        out_dev = tcf_mirred_dev(action);
 104        if (!out_dev)
 105                return -EOPNOTSUPP;
 106
 107        tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0;
 108
 109        if (tun_type) {
 110                /* Verify the egress netdev matches the tunnel type. */
 111                if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type))
 112                        return -EOPNOTSUPP;
 113
 114                if (*tun_out_cnt)
 115                        return -EOPNOTSUPP;
 116                (*tun_out_cnt)++;
 117
 118                output->flags = cpu_to_be16(tmp_flags |
 119                                            NFP_FL_OUT_FLAGS_USE_TUN);
 120                output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
 121        } else {
 122                /* Set action output parameters. */
 123                output->flags = cpu_to_be16(tmp_flags);
 124
 125                /* Only offload if egress ports are on the same device as the
 126                 * ingress port.
 127                 */
 128                if (!switchdev_port_same_parent_id(in_dev, out_dev))
 129                        return -EOPNOTSUPP;
 130                if (!nfp_netdev_is_nfp_repr(out_dev))
 131                        return -EOPNOTSUPP;
 132
 133                output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
 134                if (!output->port)
 135                        return -EOPNOTSUPP;
 136        }
 137        nfp_flow->meta.shortcut = output->port;
 138
 139        return 0;
 140}
 141
 142static enum nfp_flower_tun_type
 143nfp_fl_get_tun_from_act_l4_port(struct nfp_app *app,
 144                                const struct tc_action *action)
 145{
 146        struct ip_tunnel_info *tun = tcf_tunnel_info(action);
 147        struct nfp_flower_priv *priv = app->priv;
 148
 149        switch (tun->key.tp_dst) {
 150        case htons(NFP_FL_VXLAN_PORT):
 151                return NFP_FL_TUNNEL_VXLAN;
 152        case htons(NFP_FL_GENEVE_PORT):
 153                if (priv->flower_ext_feats & NFP_FL_FEATS_GENEVE)
 154                        return NFP_FL_TUNNEL_GENEVE;
 155                /* FALLTHROUGH */
 156        default:
 157                return NFP_FL_TUNNEL_NONE;
 158        }
 159}
 160
 161static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
 162{
 163        size_t act_size = sizeof(struct nfp_fl_pre_tunnel);
 164        struct nfp_fl_pre_tunnel *pre_tun_act;
 165
 166        /* Pre_tunnel action must be first on action list.
 167         * If other actions already exist they need pushed forward.
 168         */
 169        if (act_len)
 170                memmove(act_data + act_size, act_data, act_len);
 171
 172        pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data;
 173
 174        memset(pre_tun_act, 0, act_size);
 175
 176        pre_tun_act->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_TUNNEL;
 177        pre_tun_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 178
 179        return pre_tun_act;
 180}
 181
 182static int
 183nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun,
 184                        const struct tc_action *action,
 185                        struct nfp_fl_pre_tunnel *pre_tun,
 186                        enum nfp_flower_tun_type tun_type,
 187                        struct net_device *netdev)
 188{
 189        size_t act_size = sizeof(struct nfp_fl_set_ipv4_udp_tun);
 190        struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action);
 191        u32 tmp_set_ip_tun_type_index = 0;
 192        /* Currently support one pre-tunnel so index is always 0. */
 193        int pretun_idx = 0;
 194        struct net *net;
 195
 196        if (ip_tun->options_len)
 197                return -EOPNOTSUPP;
 198
 199        net = dev_net(netdev);
 200
 201        set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL;
 202        set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 203
 204        /* Set tunnel type and pre-tunnel index. */
 205        tmp_set_ip_tun_type_index |=
 206                FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, tun_type) |
 207                FIELD_PREP(NFP_FL_IPV4_PRE_TUN_INDEX, pretun_idx);
 208
 209        set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index);
 210        set_tun->tun_id = ip_tun->key.tun_id;
 211        set_tun->ttl = net->ipv4_sysctl_ip_default_ttl;
 212
 213        /* Complete pre_tunnel action. */
 214        pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst;
 215
 216        return 0;
 217}
 218
 219static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask)
 220{
 221        u32 oldvalue = get_unaligned((u32 *)p_exact);
 222        u32 oldmask = get_unaligned((u32 *)p_mask);
 223
 224        value &= mask;
 225        value |= oldvalue & ~mask;
 226
 227        put_unaligned(oldmask | mask, (u32 *)p_mask);
 228        put_unaligned(value, (u32 *)p_exact);
 229}
 230
 231static int
 232nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
 233               struct nfp_fl_set_eth *set_eth)
 234{
 235        u32 exact, mask;
 236
 237        if (off + 4 > ETH_ALEN * 2)
 238                return -EOPNOTSUPP;
 239
 240        mask = ~tcf_pedit_mask(action, idx);
 241        exact = tcf_pedit_val(action, idx);
 242
 243        if (exact & ~mask)
 244                return -EOPNOTSUPP;
 245
 246        nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off],
 247                            &set_eth->eth_addr_mask[off]);
 248
 249        set_eth->reserved = cpu_to_be16(0);
 250        set_eth->head.jump_id = NFP_FL_ACTION_OPCODE_SET_ETHERNET;
 251        set_eth->head.len_lw = sizeof(*set_eth) >> NFP_FL_LW_SIZ;
 252
 253        return 0;
 254}
 255
 256static int
 257nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
 258               struct nfp_fl_set_ip4_addrs *set_ip_addr)
 259{
 260        __be32 exact, mask;
 261
 262        /* We are expecting tcf_pedit to return a big endian value */
 263        mask = (__force __be32)~tcf_pedit_mask(action, idx);
 264        exact = (__force __be32)tcf_pedit_val(action, idx);
 265
 266        if (exact & ~mask)
 267                return -EOPNOTSUPP;
 268
 269        switch (off) {
 270        case offsetof(struct iphdr, daddr):
 271                set_ip_addr->ipv4_dst_mask = mask;
 272                set_ip_addr->ipv4_dst = exact;
 273                break;
 274        case offsetof(struct iphdr, saddr):
 275                set_ip_addr->ipv4_src_mask = mask;
 276                set_ip_addr->ipv4_src = exact;
 277                break;
 278        default:
 279                return -EOPNOTSUPP;
 280        }
 281
 282        set_ip_addr->reserved = cpu_to_be16(0);
 283        set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
 284        set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> NFP_FL_LW_SIZ;
 285
 286        return 0;
 287}
 288
 289static void
 290nfp_fl_set_ip6_helper(int opcode_tag, int idx, __be32 exact, __be32 mask,
 291                      struct nfp_fl_set_ipv6_addr *ip6)
 292{
 293        ip6->ipv6[idx % 4].mask = mask;
 294        ip6->ipv6[idx % 4].exact = exact;
 295
 296        ip6->reserved = cpu_to_be16(0);
 297        ip6->head.jump_id = opcode_tag;
 298        ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
 299}
 300
 301static int
 302nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
 303               struct nfp_fl_set_ipv6_addr *ip_dst,
 304               struct nfp_fl_set_ipv6_addr *ip_src)
 305{
 306        __be32 exact, mask;
 307
 308        /* We are expecting tcf_pedit to return a big endian value */
 309        mask = (__force __be32)~tcf_pedit_mask(action, idx);
 310        exact = (__force __be32)tcf_pedit_val(action, idx);
 311
 312        if (exact & ~mask)
 313                return -EOPNOTSUPP;
 314
 315        if (off < offsetof(struct ipv6hdr, saddr))
 316                return -EOPNOTSUPP;
 317        else if (off < offsetof(struct ipv6hdr, daddr))
 318                nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, idx,
 319                                      exact, mask, ip_src);
 320        else if (off < offsetof(struct ipv6hdr, daddr) +
 321                       sizeof(struct in6_addr))
 322                nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, idx,
 323                                      exact, mask, ip_dst);
 324        else
 325                return -EOPNOTSUPP;
 326
 327        return 0;
 328}
 329
 330static int
 331nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off,
 332                 struct nfp_fl_set_tport *set_tport, int opcode)
 333{
 334        u32 exact, mask;
 335
 336        if (off)
 337                return -EOPNOTSUPP;
 338
 339        mask = ~tcf_pedit_mask(action, idx);
 340        exact = tcf_pedit_val(action, idx);
 341
 342        if (exact & ~mask)
 343                return -EOPNOTSUPP;
 344
 345        nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val,
 346                            set_tport->tp_port_mask);
 347
 348        set_tport->reserved = cpu_to_be16(0);
 349        set_tport->head.jump_id = opcode;
 350        set_tport->head.len_lw = sizeof(*set_tport) >> NFP_FL_LW_SIZ;
 351
 352        return 0;
 353}
 354
 355static int
 356nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
 357{
 358        struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
 359        struct nfp_fl_set_ip4_addrs set_ip_addr;
 360        struct nfp_fl_set_tport set_tport;
 361        struct nfp_fl_set_eth set_eth;
 362        enum pedit_header_type htype;
 363        int idx, nkeys, err;
 364        size_t act_size;
 365        u32 offset, cmd;
 366
 367        memset(&set_ip6_dst, 0, sizeof(set_ip6_dst));
 368        memset(&set_ip6_src, 0, sizeof(set_ip6_src));
 369        memset(&set_ip_addr, 0, sizeof(set_ip_addr));
 370        memset(&set_tport, 0, sizeof(set_tport));
 371        memset(&set_eth, 0, sizeof(set_eth));
 372        nkeys = tcf_pedit_nkeys(action);
 373
 374        for (idx = 0; idx < nkeys; idx++) {
 375                cmd = tcf_pedit_cmd(action, idx);
 376                htype = tcf_pedit_htype(action, idx);
 377                offset = tcf_pedit_offset(action, idx);
 378
 379                if (cmd != TCA_PEDIT_KEY_EX_CMD_SET)
 380                        return -EOPNOTSUPP;
 381
 382                switch (htype) {
 383                case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
 384                        err = nfp_fl_set_eth(action, idx, offset, &set_eth);
 385                        break;
 386                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
 387                        err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr);
 388                        break;
 389                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
 390                        err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
 391                                             &set_ip6_src);
 392                        break;
 393                case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
 394                        err = nfp_fl_set_tport(action, idx, offset, &set_tport,
 395                                               NFP_FL_ACTION_OPCODE_SET_TCP);
 396                        break;
 397                case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
 398                        err = nfp_fl_set_tport(action, idx, offset, &set_tport,
 399                                               NFP_FL_ACTION_OPCODE_SET_UDP);
 400                        break;
 401                default:
 402                        return -EOPNOTSUPP;
 403                }
 404                if (err)
 405                        return err;
 406        }
 407
 408        if (set_eth.head.len_lw) {
 409                act_size = sizeof(set_eth);
 410                memcpy(nfp_action, &set_eth, act_size);
 411                *a_len += act_size;
 412        } else if (set_ip_addr.head.len_lw) {
 413                act_size = sizeof(set_ip_addr);
 414                memcpy(nfp_action, &set_ip_addr, act_size);
 415                *a_len += act_size;
 416        } else if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) {
 417                /* TC compiles set src and dst IPv6 address as a single action,
 418                 * the hardware requires this to be 2 separate actions.
 419                 */
 420                act_size = sizeof(set_ip6_src);
 421                memcpy(nfp_action, &set_ip6_src, act_size);
 422                *a_len += act_size;
 423
 424                act_size = sizeof(set_ip6_dst);
 425                memcpy(&nfp_action[sizeof(set_ip6_src)], &set_ip6_dst,
 426                       act_size);
 427                *a_len += act_size;
 428        } else if (set_ip6_dst.head.len_lw) {
 429                act_size = sizeof(set_ip6_dst);
 430                memcpy(nfp_action, &set_ip6_dst, act_size);
 431                *a_len += act_size;
 432        } else if (set_ip6_src.head.len_lw) {
 433                act_size = sizeof(set_ip6_src);
 434                memcpy(nfp_action, &set_ip6_src, act_size);
 435                *a_len += act_size;
 436        } else if (set_tport.head.len_lw) {
 437                act_size = sizeof(set_tport);
 438                memcpy(nfp_action, &set_tport, act_size);
 439                *a_len += act_size;
 440        }
 441
 442        return 0;
 443}
 444
 445static int
 446nfp_flower_loop_action(const struct tc_action *a,
 447                       struct nfp_fl_payload *nfp_fl, int *a_len,
 448                       struct net_device *netdev,
 449                       enum nfp_flower_tun_type *tun_type, int *tun_out_cnt)
 450{
 451        struct nfp_fl_set_ipv4_udp_tun *set_tun;
 452        struct nfp_fl_pre_tunnel *pre_tun;
 453        struct nfp_fl_push_vlan *psh_v;
 454        struct nfp_fl_pop_vlan *pop_v;
 455        struct nfp_fl_output *output;
 456        int err;
 457
 458        if (is_tcf_gact_shot(a)) {
 459                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP);
 460        } else if (is_tcf_mirred_egress_redirect(a)) {
 461                if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ)
 462                        return -EOPNOTSUPP;
 463
 464                output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
 465                err = nfp_fl_output(output, a, nfp_fl, true, netdev, *tun_type,
 466                                    tun_out_cnt);
 467                if (err)
 468                        return err;
 469
 470                *a_len += sizeof(struct nfp_fl_output);
 471        } else if (is_tcf_mirred_egress_mirror(a)) {
 472                if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ)
 473                        return -EOPNOTSUPP;
 474
 475                output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
 476                err = nfp_fl_output(output, a, nfp_fl, false, netdev, *tun_type,
 477                                    tun_out_cnt);
 478                if (err)
 479                        return err;
 480
 481                *a_len += sizeof(struct nfp_fl_output);
 482        } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
 483                if (*a_len + sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ)
 484                        return -EOPNOTSUPP;
 485
 486                pop_v = (struct nfp_fl_pop_vlan *)&nfp_fl->action_data[*a_len];
 487                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_POPV);
 488
 489                nfp_fl_pop_vlan(pop_v);
 490                *a_len += sizeof(struct nfp_fl_pop_vlan);
 491        } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
 492                if (*a_len + sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ)
 493                        return -EOPNOTSUPP;
 494
 495                psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len];
 496                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
 497
 498                nfp_fl_push_vlan(psh_v, a);
 499                *a_len += sizeof(struct nfp_fl_push_vlan);
 500        } else if (is_tcf_tunnel_set(a)) {
 501                struct nfp_repr *repr = netdev_priv(netdev);
 502                *tun_type = nfp_fl_get_tun_from_act_l4_port(repr->app, a);
 503                if (*tun_type == NFP_FL_TUNNEL_NONE)
 504                        return -EOPNOTSUPP;
 505
 506                /* Pre-tunnel action is required for tunnel encap.
 507                 * This checks for next hop entries on NFP.
 508                 * If none, the packet falls back before applying other actions.
 509                 */
 510                if (*a_len + sizeof(struct nfp_fl_pre_tunnel) +
 511                    sizeof(struct nfp_fl_set_ipv4_udp_tun) > NFP_FL_MAX_A_SIZ)
 512                        return -EOPNOTSUPP;
 513
 514                pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len);
 515                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
 516                *a_len += sizeof(struct nfp_fl_pre_tunnel);
 517
 518                set_tun = (void *)&nfp_fl->action_data[*a_len];
 519                err = nfp_fl_set_ipv4_udp_tun(set_tun, a, pre_tun, *tun_type,
 520                                              netdev);
 521                if (err)
 522                        return err;
 523                *a_len += sizeof(struct nfp_fl_set_ipv4_udp_tun);
 524        } else if (is_tcf_tunnel_release(a)) {
 525                /* Tunnel decap is handled by default so accept action. */
 526                return 0;
 527        } else if (is_tcf_pedit(a)) {
 528                if (nfp_fl_pedit(a, &nfp_fl->action_data[*a_len], a_len))
 529                        return -EOPNOTSUPP;
 530        } else {
 531                /* Currently we do not handle any other actions. */
 532                return -EOPNOTSUPP;
 533        }
 534
 535        return 0;
 536}
 537
 538int nfp_flower_compile_action(struct tc_cls_flower_offload *flow,
 539                              struct net_device *netdev,
 540                              struct nfp_fl_payload *nfp_flow)
 541{
 542        int act_len, act_cnt, err, tun_out_cnt;
 543        enum nfp_flower_tun_type tun_type;
 544        const struct tc_action *a;
 545        LIST_HEAD(actions);
 546
 547        memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ);
 548        nfp_flow->meta.act_len = 0;
 549        tun_type = NFP_FL_TUNNEL_NONE;
 550        act_len = 0;
 551        act_cnt = 0;
 552        tun_out_cnt = 0;
 553
 554        tcf_exts_to_list(flow->exts, &actions);
 555        list_for_each_entry(a, &actions, list) {
 556                err = nfp_flower_loop_action(a, nfp_flow, &act_len, netdev,
 557                                             &tun_type, &tun_out_cnt);
 558                if (err)
 559                        return err;
 560                act_cnt++;
 561        }
 562
 563        /* We optimise when the action list is small, this can unfortunately
 564         * not happen once we have more than one action in the action list.
 565         */
 566        if (act_cnt > 1)
 567                nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
 568
 569        nfp_flow->meta.act_len = act_len;
 570
 571        return 0;
 572}
 573