18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 58c2ecf20Sopenharmony_ci#include <linux/mpls.h> 68c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 78c2ecf20Sopenharmony_ci#include <net/tc_act/tc_csum.h> 88c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gact.h> 98c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mirred.h> 108c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mpls.h> 118c2ecf20Sopenharmony_ci#include <net/tc_act/tc_pedit.h> 128c2ecf20Sopenharmony_ci#include <net/tc_act/tc_vlan.h> 138c2ecf20Sopenharmony_ci#include <net/tc_act/tc_tunnel_key.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "cmsg.h" 168c2ecf20Sopenharmony_ci#include "main.h" 178c2ecf20Sopenharmony_ci#include "../nfp_net_repr.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* The kernel versions of TUNNEL_* are not ABI and therefore vulnerable 208c2ecf20Sopenharmony_ci * to change. Such changes will break our FW ABI. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#define NFP_FL_TUNNEL_CSUM cpu_to_be16(0x01) 238c2ecf20Sopenharmony_ci#define NFP_FL_TUNNEL_KEY cpu_to_be16(0x04) 248c2ecf20Sopenharmony_ci#define NFP_FL_TUNNEL_GENEVE_OPT cpu_to_be16(0x0800) 258c2ecf20Sopenharmony_ci#define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS (IP_TUNNEL_INFO_TX | \ 268c2ecf20Sopenharmony_ci IP_TUNNEL_INFO_IPV6) 278c2ecf20Sopenharmony_ci#define NFP_FL_SUPPORTED_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \ 288c2ecf20Sopenharmony_ci NFP_FL_TUNNEL_KEY | \ 298c2ecf20Sopenharmony_ci NFP_FL_TUNNEL_GENEVE_OPT) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int 328c2ecf20Sopenharmony_cinfp_fl_push_mpls(struct nfp_fl_push_mpls *push_mpls, 338c2ecf20Sopenharmony_ci const struct flow_action_entry *act, 348c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_push_mpls); 378c2ecf20Sopenharmony_ci u32 mpls_lse = 0; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci push_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_MPLS; 408c2ecf20Sopenharmony_ci push_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* BOS is optional in the TC action but required for offload. */ 438c2ecf20Sopenharmony_ci if (act->mpls_push.bos != ACT_MPLS_BOS_NOT_SET) { 448c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_push.bos << MPLS_LS_S_SHIFT; 458c2ecf20Sopenharmony_ci } else { 468c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: BOS field must explicitly be set for MPLS push"); 478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Leave MPLS TC as a default value of 0 if not explicitly set. */ 518c2ecf20Sopenharmony_ci if (act->mpls_push.tc != ACT_MPLS_TC_NOT_SET) 528c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_push.tc << MPLS_LS_TC_SHIFT; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Proto, label and TTL are enforced and verified for MPLS push. */ 558c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_push.label << MPLS_LS_LABEL_SHIFT; 568c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_push.ttl << MPLS_LS_TTL_SHIFT; 578c2ecf20Sopenharmony_ci push_mpls->ethtype = act->mpls_push.proto; 588c2ecf20Sopenharmony_ci push_mpls->lse = cpu_to_be32(mpls_lse); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void 648c2ecf20Sopenharmony_cinfp_fl_pop_mpls(struct nfp_fl_pop_mpls *pop_mpls, 658c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_pop_mpls); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci pop_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_POP_MPLS; 708c2ecf20Sopenharmony_ci pop_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ; 718c2ecf20Sopenharmony_ci pop_mpls->ethtype = act->mpls_pop.proto; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void 758c2ecf20Sopenharmony_cinfp_fl_set_mpls(struct nfp_fl_set_mpls *set_mpls, 768c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_set_mpls); 798c2ecf20Sopenharmony_ci u32 mpls_lse = 0, mpls_mask = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci set_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_SET_MPLS; 828c2ecf20Sopenharmony_ci set_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (act->mpls_mangle.label != ACT_MPLS_LABEL_NOT_SET) { 858c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_mangle.label << MPLS_LS_LABEL_SHIFT; 868c2ecf20Sopenharmony_ci mpls_mask |= MPLS_LS_LABEL_MASK; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci if (act->mpls_mangle.tc != ACT_MPLS_TC_NOT_SET) { 898c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_mangle.tc << MPLS_LS_TC_SHIFT; 908c2ecf20Sopenharmony_ci mpls_mask |= MPLS_LS_TC_MASK; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci if (act->mpls_mangle.bos != ACT_MPLS_BOS_NOT_SET) { 938c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_mangle.bos << MPLS_LS_S_SHIFT; 948c2ecf20Sopenharmony_ci mpls_mask |= MPLS_LS_S_MASK; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci if (act->mpls_mangle.ttl) { 978c2ecf20Sopenharmony_ci mpls_lse |= act->mpls_mangle.ttl << MPLS_LS_TTL_SHIFT; 988c2ecf20Sopenharmony_ci mpls_mask |= MPLS_LS_TTL_MASK; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci set_mpls->lse = cpu_to_be32(mpls_lse); 1028c2ecf20Sopenharmony_ci set_mpls->lse_mask = cpu_to_be32(mpls_mask); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_pop_vlan); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pop_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_POP_VLAN; 1108c2ecf20Sopenharmony_ci pop_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ; 1118c2ecf20Sopenharmony_ci pop_vlan->reserved = 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void 1158c2ecf20Sopenharmony_cinfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan, 1168c2ecf20Sopenharmony_ci const struct flow_action_entry *act) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_push_vlan); 1198c2ecf20Sopenharmony_ci u16 tmp_push_vlan_tci; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci push_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_VLAN; 1228c2ecf20Sopenharmony_ci push_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ; 1238c2ecf20Sopenharmony_ci push_vlan->reserved = 0; 1248c2ecf20Sopenharmony_ci push_vlan->vlan_tpid = act->vlan.proto; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci tmp_push_vlan_tci = 1278c2ecf20Sopenharmony_ci FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, act->vlan.prio) | 1288c2ecf20Sopenharmony_ci FIELD_PREP(NFP_FL_PUSH_VLAN_VID, act->vlan.vid); 1298c2ecf20Sopenharmony_ci push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int 1338c2ecf20Sopenharmony_cinfp_fl_pre_lag(struct nfp_app *app, const struct flow_action_entry *act, 1348c2ecf20Sopenharmony_ci struct nfp_fl_payload *nfp_flow, int act_len, 1358c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_pre_lag); 1388c2ecf20Sopenharmony_ci struct nfp_fl_pre_lag *pre_lag; 1398c2ecf20Sopenharmony_ci struct net_device *out_dev; 1408c2ecf20Sopenharmony_ci int err; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci out_dev = act->dev; 1438c2ecf20Sopenharmony_ci if (!out_dev || !netif_is_lag_master(out_dev)) 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (act_len + act_size > NFP_FL_MAX_A_SIZ) { 1478c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at LAG action"); 1488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Pre_lag action must be first on action list. 1528c2ecf20Sopenharmony_ci * If other actions already exist they need pushed forward. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (act_len) 1558c2ecf20Sopenharmony_ci memmove(nfp_flow->action_data + act_size, 1568c2ecf20Sopenharmony_ci nfp_flow->action_data, act_len); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci pre_lag = (struct nfp_fl_pre_lag *)nfp_flow->action_data; 1598c2ecf20Sopenharmony_ci err = nfp_flower_lag_populate_pre_action(app, out_dev, pre_lag, extack); 1608c2ecf20Sopenharmony_ci if (err) 1618c2ecf20Sopenharmony_ci return err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci pre_lag->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_LAG; 1648c2ecf20Sopenharmony_ci pre_lag->head.len_lw = act_size >> NFP_FL_LW_SIZ; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return act_size; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int 1728c2ecf20Sopenharmony_cinfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output, 1738c2ecf20Sopenharmony_ci const struct flow_action_entry *act, 1748c2ecf20Sopenharmony_ci struct nfp_fl_payload *nfp_flow, 1758c2ecf20Sopenharmony_ci bool last, struct net_device *in_dev, 1768c2ecf20Sopenharmony_ci enum nfp_flower_tun_type tun_type, int *tun_out_cnt, 1778c2ecf20Sopenharmony_ci bool pkt_host, struct netlink_ext_ack *extack) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_output); 1808c2ecf20Sopenharmony_ci struct nfp_flower_priv *priv = app->priv; 1818c2ecf20Sopenharmony_ci struct net_device *out_dev; 1828c2ecf20Sopenharmony_ci u16 tmp_flags; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT; 1858c2ecf20Sopenharmony_ci output->head.len_lw = act_size >> NFP_FL_LW_SIZ; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci out_dev = act->dev; 1888c2ecf20Sopenharmony_ci if (!out_dev) { 1898c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid egress interface for mirred action"); 1908c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (tun_type) { 1968c2ecf20Sopenharmony_ci /* Verify the egress netdev matches the tunnel type. */ 1978c2ecf20Sopenharmony_ci if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type)) { 1988c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface does not match the required tunnel type"); 1998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (*tun_out_cnt) { 2038c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot offload more than one tunnel mirred output per filter"); 2048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci (*tun_out_cnt)++; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci output->flags = cpu_to_be16(tmp_flags | 2098c2ecf20Sopenharmony_ci NFP_FL_OUT_FLAGS_USE_TUN); 2108c2ecf20Sopenharmony_ci output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type); 2118c2ecf20Sopenharmony_ci } else if (netif_is_lag_master(out_dev) && 2128c2ecf20Sopenharmony_ci priv->flower_en_feats & NFP_FL_ENABLE_LAG) { 2138c2ecf20Sopenharmony_ci int gid; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci output->flags = cpu_to_be16(tmp_flags); 2168c2ecf20Sopenharmony_ci gid = nfp_flower_lag_get_output_id(app, out_dev); 2178c2ecf20Sopenharmony_ci if (gid < 0) { 2188c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot find group id for LAG action"); 2198c2ecf20Sopenharmony_ci return gid; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci output->port = cpu_to_be32(NFP_FL_LAG_OUT | gid); 2228c2ecf20Sopenharmony_ci } else if (nfp_flower_internal_port_can_offload(app, out_dev)) { 2238c2ecf20Sopenharmony_ci if (!(priv->flower_ext_feats & NFP_FL_FEATS_PRE_TUN_RULES)) { 2248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules not supported in loaded firmware"); 2258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (nfp_flow->pre_tun_rule.dev || !pkt_host) { 2298c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules require single egress dev and ptype HOST action"); 2308c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci nfp_flow->pre_tun_rule.dev = out_dev; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci /* Set action output parameters. */ 2388c2ecf20Sopenharmony_ci output->flags = cpu_to_be16(tmp_flags); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (nfp_netdev_is_nfp_repr(in_dev)) { 2418c2ecf20Sopenharmony_ci /* Confirm ingress and egress are on same device. */ 2428c2ecf20Sopenharmony_ci if (!netdev_port_same_parent_id(in_dev, out_dev)) { 2438c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: ingress and egress interfaces are on different devices"); 2448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!nfp_netdev_is_nfp_repr(out_dev)) { 2498c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface is not an nfp port"); 2508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev)); 2548c2ecf20Sopenharmony_ci if (!output->port) { 2558c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid port id for egress interface"); 2568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci nfp_flow->meta.shortcut = output->port; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic bool 2658c2ecf20Sopenharmony_cinfp_flower_tun_is_gre(struct flow_cls_offload *flow, int start_idx) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct flow_action_entry *act = flow->rule->action.entries; 2688c2ecf20Sopenharmony_ci int num_act = flow->rule->action.num_entries; 2698c2ecf20Sopenharmony_ci int act_idx; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Preparse action list for next mirred or redirect action */ 2728c2ecf20Sopenharmony_ci for (act_idx = start_idx + 1; act_idx < num_act; act_idx++) 2738c2ecf20Sopenharmony_ci if (act[act_idx].id == FLOW_ACTION_REDIRECT || 2748c2ecf20Sopenharmony_ci act[act_idx].id == FLOW_ACTION_MIRRED) 2758c2ecf20Sopenharmony_ci return netif_is_gretap(act[act_idx].dev); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return false; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic enum nfp_flower_tun_type 2818c2ecf20Sopenharmony_cinfp_fl_get_tun_from_act(struct nfp_app *app, 2828c2ecf20Sopenharmony_ci struct flow_cls_offload *flow, 2838c2ecf20Sopenharmony_ci const struct flow_action_entry *act, int act_idx) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci const struct ip_tunnel_info *tun = act->tunnel; 2868c2ecf20Sopenharmony_ci struct nfp_flower_priv *priv = app->priv; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Determine the tunnel type based on the egress netdev 2898c2ecf20Sopenharmony_ci * in the mirred action for tunnels without l4. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (nfp_flower_tun_is_gre(flow, act_idx)) 2928c2ecf20Sopenharmony_ci return NFP_FL_TUNNEL_GRE; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci switch (tun->key.tp_dst) { 2958c2ecf20Sopenharmony_ci case htons(IANA_VXLAN_UDP_PORT): 2968c2ecf20Sopenharmony_ci return NFP_FL_TUNNEL_VXLAN; 2978c2ecf20Sopenharmony_ci case htons(GENEVE_UDP_PORT): 2988c2ecf20Sopenharmony_ci if (priv->flower_ext_feats & NFP_FL_FEATS_GENEVE) 2998c2ecf20Sopenharmony_ci return NFP_FL_TUNNEL_GENEVE; 3008c2ecf20Sopenharmony_ci fallthrough; 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci return NFP_FL_TUNNEL_NONE; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_pre_tunnel); 3098c2ecf20Sopenharmony_ci struct nfp_fl_pre_tunnel *pre_tun_act; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Pre_tunnel action must be first on action list. 3128c2ecf20Sopenharmony_ci * If other actions already exist they need to be pushed forward. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (act_len) 3158c2ecf20Sopenharmony_ci memmove(act_data + act_size, act_data, act_len); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci memset(pre_tun_act, 0, act_size); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci pre_tun_act->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_TUNNEL; 3228c2ecf20Sopenharmony_ci pre_tun_act->head.len_lw = act_size >> NFP_FL_LW_SIZ; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return pre_tun_act; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int 3288c2ecf20Sopenharmony_cinfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len, 3298c2ecf20Sopenharmony_ci const struct flow_action_entry *act, 3308c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct ip_tunnel_info *ip_tun = (struct ip_tunnel_info *)act->tunnel; 3338c2ecf20Sopenharmony_ci int opt_len, opt_cnt, act_start, tot_push_len; 3348c2ecf20Sopenharmony_ci u8 *src = ip_tunnel_info_opts(ip_tun); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* We need to populate the options in reverse order for HW. 3378c2ecf20Sopenharmony_ci * Therefore we go through the options, calculating the 3388c2ecf20Sopenharmony_ci * number of options and the total size, then we populate 3398c2ecf20Sopenharmony_ci * them in reverse order in the action list. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci opt_cnt = 0; 3428c2ecf20Sopenharmony_ci tot_push_len = 0; 3438c2ecf20Sopenharmony_ci opt_len = ip_tun->options_len; 3448c2ecf20Sopenharmony_ci while (opt_len > 0) { 3458c2ecf20Sopenharmony_ci struct geneve_opt *opt = (struct geneve_opt *)src; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci opt_cnt++; 3488c2ecf20Sopenharmony_ci if (opt_cnt > NFP_FL_MAX_GENEVE_OPT_CNT) { 3498c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed number of geneve options exceeded"); 3508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci tot_push_len += sizeof(struct nfp_fl_push_geneve) + 3548c2ecf20Sopenharmony_ci opt->length * 4; 3558c2ecf20Sopenharmony_ci if (tot_push_len > NFP_FL_MAX_GENEVE_OPT_ACT) { 3568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options"); 3578c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci opt_len -= sizeof(struct geneve_opt) + opt->length * 4; 3618c2ecf20Sopenharmony_ci src += sizeof(struct geneve_opt) + opt->length * 4; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (*list_len + tot_push_len > NFP_FL_MAX_A_SIZ) { 3658c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options"); 3668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci act_start = *list_len; 3708c2ecf20Sopenharmony_ci *list_len += tot_push_len; 3718c2ecf20Sopenharmony_ci src = ip_tunnel_info_opts(ip_tun); 3728c2ecf20Sopenharmony_ci while (opt_cnt) { 3738c2ecf20Sopenharmony_ci struct geneve_opt *opt = (struct geneve_opt *)src; 3748c2ecf20Sopenharmony_ci struct nfp_fl_push_geneve *push; 3758c2ecf20Sopenharmony_ci size_t act_size, len; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci opt_cnt--; 3788c2ecf20Sopenharmony_ci act_size = sizeof(struct nfp_fl_push_geneve) + opt->length * 4; 3798c2ecf20Sopenharmony_ci tot_push_len -= act_size; 3808c2ecf20Sopenharmony_ci len = act_start + tot_push_len; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci push = (struct nfp_fl_push_geneve *)&nfp_fl->action_data[len]; 3838c2ecf20Sopenharmony_ci push->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_GENEVE; 3848c2ecf20Sopenharmony_ci push->head.len_lw = act_size >> NFP_FL_LW_SIZ; 3858c2ecf20Sopenharmony_ci push->reserved = 0; 3868c2ecf20Sopenharmony_ci push->class = opt->opt_class; 3878c2ecf20Sopenharmony_ci push->type = opt->type; 3888c2ecf20Sopenharmony_ci push->length = opt->length; 3898c2ecf20Sopenharmony_ci memcpy(&push->opt_data, opt->opt_data, opt->length * 4); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci src += sizeof(struct geneve_opt) + opt->length * 4; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int 3988c2ecf20Sopenharmony_cinfp_fl_set_tun(struct nfp_app *app, struct nfp_fl_set_tun *set_tun, 3998c2ecf20Sopenharmony_ci const struct flow_action_entry *act, 4008c2ecf20Sopenharmony_ci struct nfp_fl_pre_tunnel *pre_tun, 4018c2ecf20Sopenharmony_ci enum nfp_flower_tun_type tun_type, 4028c2ecf20Sopenharmony_ci struct net_device *netdev, struct netlink_ext_ack *extack) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci const struct ip_tunnel_info *ip_tun = act->tunnel; 4058c2ecf20Sopenharmony_ci bool ipv6 = ip_tunnel_info_af(ip_tun) == AF_INET6; 4068c2ecf20Sopenharmony_ci size_t act_size = sizeof(struct nfp_fl_set_tun); 4078c2ecf20Sopenharmony_ci struct nfp_flower_priv *priv = app->priv; 4088c2ecf20Sopenharmony_ci u32 tmp_set_ip_tun_type_index = 0; 4098c2ecf20Sopenharmony_ci /* Currently support one pre-tunnel so index is always 0. */ 4108c2ecf20Sopenharmony_ci int pretun_idx = 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_IPV6) && ipv6) 4138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (ipv6 && !(priv->flower_ext_feats & NFP_FL_FEATS_IPV6_TUN)) 4168c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci BUILD_BUG_ON(NFP_FL_TUNNEL_CSUM != TUNNEL_CSUM || 4198c2ecf20Sopenharmony_ci NFP_FL_TUNNEL_KEY != TUNNEL_KEY || 4208c2ecf20Sopenharmony_ci NFP_FL_TUNNEL_GENEVE_OPT != TUNNEL_GENEVE_OPT); 4218c2ecf20Sopenharmony_ci if (ip_tun->options_len && 4228c2ecf20Sopenharmony_ci (tun_type != NFP_FL_TUNNEL_GENEVE || 4238c2ecf20Sopenharmony_ci !(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE_OPT))) { 4248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support geneve options offload"); 4258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_TUNNEL; 4298c2ecf20Sopenharmony_ci set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Set tunnel type and pre-tunnel index. */ 4328c2ecf20Sopenharmony_ci tmp_set_ip_tun_type_index |= 4338c2ecf20Sopenharmony_ci FIELD_PREP(NFP_FL_TUNNEL_TYPE, tun_type) | 4348c2ecf20Sopenharmony_ci FIELD_PREP(NFP_FL_PRE_TUN_INDEX, pretun_idx); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index); 4378c2ecf20Sopenharmony_ci set_tun->tun_id = ip_tun->key.tun_id; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (ip_tun->key.ttl) { 4408c2ecf20Sopenharmony_ci set_tun->ttl = ip_tun->key.ttl; 4418c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6 4428c2ecf20Sopenharmony_ci } else if (ipv6) { 4438c2ecf20Sopenharmony_ci struct net *net = dev_net(netdev); 4448c2ecf20Sopenharmony_ci struct flowi6 flow = {}; 4458c2ecf20Sopenharmony_ci struct dst_entry *dst; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci flow.daddr = ip_tun->key.u.ipv6.dst; 4488c2ecf20Sopenharmony_ci flow.flowi4_proto = IPPROTO_UDP; 4498c2ecf20Sopenharmony_ci dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &flow, NULL); 4508c2ecf20Sopenharmony_ci if (!IS_ERR(dst)) { 4518c2ecf20Sopenharmony_ci set_tun->ttl = ip6_dst_hoplimit(dst); 4528c2ecf20Sopenharmony_ci dst_release(dst); 4538c2ecf20Sopenharmony_ci } else { 4548c2ecf20Sopenharmony_ci set_tun->ttl = net->ipv6.devconf_all->hop_limit; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci#endif 4578c2ecf20Sopenharmony_ci } else { 4588c2ecf20Sopenharmony_ci struct net *net = dev_net(netdev); 4598c2ecf20Sopenharmony_ci struct flowi4 flow = {}; 4608c2ecf20Sopenharmony_ci struct rtable *rt; 4618c2ecf20Sopenharmony_ci int err; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Do a route lookup to determine ttl - if fails then use 4648c2ecf20Sopenharmony_ci * default. Note that CONFIG_INET is a requirement of 4658c2ecf20Sopenharmony_ci * CONFIG_NET_SWITCHDEV so must be defined here. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci flow.daddr = ip_tun->key.u.ipv4.dst; 4688c2ecf20Sopenharmony_ci flow.flowi4_proto = IPPROTO_UDP; 4698c2ecf20Sopenharmony_ci rt = ip_route_output_key(net, &flow); 4708c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(rt); 4718c2ecf20Sopenharmony_ci if (!err) { 4728c2ecf20Sopenharmony_ci set_tun->ttl = ip4_dst_hoplimit(&rt->dst); 4738c2ecf20Sopenharmony_ci ip_rt_put(rt); 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci set_tun->ttl = net->ipv4.sysctl_ip_default_ttl; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci set_tun->tos = ip_tun->key.tos; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (!(ip_tun->key.tun_flags & NFP_FL_TUNNEL_KEY) || 4828c2ecf20Sopenharmony_ci ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_UDP_TUN_FLAGS) { 4838c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support tunnel flag offload"); 4848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci set_tun->tun_flags = ip_tun->key.tun_flags; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (tun_type == NFP_FL_TUNNEL_GENEVE) { 4898c2ecf20Sopenharmony_ci set_tun->tun_proto = htons(ETH_P_TEB); 4908c2ecf20Sopenharmony_ci set_tun->tun_len = ip_tun->options_len / 4; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Complete pre_tunnel action. */ 4948c2ecf20Sopenharmony_ci if (ipv6) { 4958c2ecf20Sopenharmony_ci pre_tun->flags |= cpu_to_be16(NFP_FL_PRE_TUN_IPV6); 4968c2ecf20Sopenharmony_ci pre_tun->ipv6_dst = ip_tun->key.u.ipv6.dst; 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci u32 oldvalue = get_unaligned((u32 *)p_exact); 5078c2ecf20Sopenharmony_ci u32 oldmask = get_unaligned((u32 *)p_mask); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci value &= mask; 5108c2ecf20Sopenharmony_ci value |= oldvalue & ~mask; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci put_unaligned(oldmask | mask, (u32 *)p_mask); 5138c2ecf20Sopenharmony_ci put_unaligned(value, (u32 *)p_exact); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int 5178c2ecf20Sopenharmony_cinfp_fl_set_eth(const struct flow_action_entry *act, u32 off, 5188c2ecf20Sopenharmony_ci struct nfp_fl_set_eth *set_eth, struct netlink_ext_ack *extack) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci u32 exact, mask; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (off + 4 > ETH_ALEN * 2) { 5238c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action"); 5248c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mask = ~act->mangle.mask; 5288c2ecf20Sopenharmony_ci exact = act->mangle.val; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (exact & ~mask) { 5318c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action"); 5328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off], 5368c2ecf20Sopenharmony_ci &set_eth->eth_addr_mask[off]); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci set_eth->reserved = cpu_to_be16(0); 5398c2ecf20Sopenharmony_ci set_eth->head.jump_id = NFP_FL_ACTION_OPCODE_SET_ETHERNET; 5408c2ecf20Sopenharmony_ci set_eth->head.len_lw = sizeof(*set_eth) >> NFP_FL_LW_SIZ; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistruct ipv4_ttl_word { 5468c2ecf20Sopenharmony_ci __u8 ttl; 5478c2ecf20Sopenharmony_ci __u8 protocol; 5488c2ecf20Sopenharmony_ci __sum16 check; 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int 5528c2ecf20Sopenharmony_cinfp_fl_set_ip4(const struct flow_action_entry *act, u32 off, 5538c2ecf20Sopenharmony_ci struct nfp_fl_set_ip4_addrs *set_ip_addr, 5548c2ecf20Sopenharmony_ci struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos, 5558c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct ipv4_ttl_word *ttl_word_mask; 5588c2ecf20Sopenharmony_ci struct ipv4_ttl_word *ttl_word; 5598c2ecf20Sopenharmony_ci struct iphdr *tos_word_mask; 5608c2ecf20Sopenharmony_ci struct iphdr *tos_word; 5618c2ecf20Sopenharmony_ci __be32 exact, mask; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* We are expecting tcf_pedit to return a big endian value */ 5648c2ecf20Sopenharmony_ci mask = (__force __be32)~act->mangle.mask; 5658c2ecf20Sopenharmony_ci exact = (__force __be32)act->mangle.val; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (exact & ~mask) { 5688c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 action"); 5698c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci switch (off) { 5738c2ecf20Sopenharmony_ci case offsetof(struct iphdr, daddr): 5748c2ecf20Sopenharmony_ci set_ip_addr->ipv4_dst_mask |= mask; 5758c2ecf20Sopenharmony_ci set_ip_addr->ipv4_dst &= ~mask; 5768c2ecf20Sopenharmony_ci set_ip_addr->ipv4_dst |= exact & mask; 5778c2ecf20Sopenharmony_ci set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS; 5788c2ecf20Sopenharmony_ci set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> 5798c2ecf20Sopenharmony_ci NFP_FL_LW_SIZ; 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci case offsetof(struct iphdr, saddr): 5828c2ecf20Sopenharmony_ci set_ip_addr->ipv4_src_mask |= mask; 5838c2ecf20Sopenharmony_ci set_ip_addr->ipv4_src &= ~mask; 5848c2ecf20Sopenharmony_ci set_ip_addr->ipv4_src |= exact & mask; 5858c2ecf20Sopenharmony_ci set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS; 5868c2ecf20Sopenharmony_ci set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> 5878c2ecf20Sopenharmony_ci NFP_FL_LW_SIZ; 5888c2ecf20Sopenharmony_ci break; 5898c2ecf20Sopenharmony_ci case offsetof(struct iphdr, ttl): 5908c2ecf20Sopenharmony_ci ttl_word_mask = (struct ipv4_ttl_word *)&mask; 5918c2ecf20Sopenharmony_ci ttl_word = (struct ipv4_ttl_word *)&exact; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (ttl_word_mask->protocol || ttl_word_mask->check) { 5948c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 ttl action"); 5958c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_ttl_mask |= ttl_word_mask->ttl; 5998c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_ttl &= ~ttl_word_mask->ttl; 6008c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_ttl |= ttl_word->ttl & ttl_word_mask->ttl; 6018c2ecf20Sopenharmony_ci set_ip_ttl_tos->head.jump_id = 6028c2ecf20Sopenharmony_ci NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS; 6038c2ecf20Sopenharmony_ci set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >> 6048c2ecf20Sopenharmony_ci NFP_FL_LW_SIZ; 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci case round_down(offsetof(struct iphdr, tos), 4): 6078c2ecf20Sopenharmony_ci tos_word_mask = (struct iphdr *)&mask; 6088c2ecf20Sopenharmony_ci tos_word = (struct iphdr *)&exact; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (tos_word_mask->version || tos_word_mask->ihl || 6118c2ecf20Sopenharmony_ci tos_word_mask->tot_len) { 6128c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 tos action"); 6138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_tos_mask |= tos_word_mask->tos; 6178c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_tos &= ~tos_word_mask->tos; 6188c2ecf20Sopenharmony_ci set_ip_ttl_tos->ipv4_tos |= tos_word->tos & tos_word_mask->tos; 6198c2ecf20Sopenharmony_ci set_ip_ttl_tos->head.jump_id = 6208c2ecf20Sopenharmony_ci NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS; 6218c2ecf20Sopenharmony_ci set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >> 6228c2ecf20Sopenharmony_ci NFP_FL_LW_SIZ; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci default: 6258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv4 header"); 6268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void 6338c2ecf20Sopenharmony_cinfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask, 6348c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_addr *ip6) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci ip6->ipv6[word].mask |= mask; 6378c2ecf20Sopenharmony_ci ip6->ipv6[word].exact &= ~mask; 6388c2ecf20Sopenharmony_ci ip6->ipv6[word].exact |= exact & mask; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci ip6->reserved = cpu_to_be16(0); 6418c2ecf20Sopenharmony_ci ip6->head.jump_id = opcode_tag; 6428c2ecf20Sopenharmony_ci ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistruct ipv6_hop_limit_word { 6468c2ecf20Sopenharmony_ci __be16 payload_len; 6478c2ecf20Sopenharmony_ci u8 nexthdr; 6488c2ecf20Sopenharmony_ci u8 hop_limit; 6498c2ecf20Sopenharmony_ci}; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int 6528c2ecf20Sopenharmony_cinfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask, 6538c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl, 6548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct ipv6_hop_limit_word *fl_hl_mask; 6578c2ecf20Sopenharmony_ci struct ipv6_hop_limit_word *fl_hl; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci switch (off) { 6608c2ecf20Sopenharmony_ci case offsetof(struct ipv6hdr, payload_len): 6618c2ecf20Sopenharmony_ci fl_hl_mask = (struct ipv6_hop_limit_word *)&mask; 6628c2ecf20Sopenharmony_ci fl_hl = (struct ipv6_hop_limit_word *)&exact; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (fl_hl_mask->nexthdr || fl_hl_mask->payload_len) { 6658c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 hop limit action"); 6668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_hop_limit_mask |= fl_hl_mask->hop_limit; 6708c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_hop_limit &= ~fl_hl_mask->hop_limit; 6718c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_hop_limit |= fl_hl->hop_limit & 6728c2ecf20Sopenharmony_ci fl_hl_mask->hop_limit; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci case round_down(offsetof(struct ipv6hdr, flow_lbl), 4): 6758c2ecf20Sopenharmony_ci if (mask & ~IPV6_FLOW_LABEL_MASK || 6768c2ecf20Sopenharmony_ci exact & ~IPV6_FLOW_LABEL_MASK) { 6778c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 flow label action"); 6788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_label_mask |= mask; 6828c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_label &= ~mask; 6838c2ecf20Sopenharmony_ci ip_hl_fl->ipv6_label |= exact & mask; 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ip_hl_fl->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL; 6888c2ecf20Sopenharmony_ci ip_hl_fl->head.len_lw = sizeof(*ip_hl_fl) >> NFP_FL_LW_SIZ; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int 6948c2ecf20Sopenharmony_cinfp_fl_set_ip6(const struct flow_action_entry *act, u32 off, 6958c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_addr *ip_dst, 6968c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_addr *ip_src, 6978c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl, 6988c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci __be32 exact, mask; 7018c2ecf20Sopenharmony_ci int err = 0; 7028c2ecf20Sopenharmony_ci u8 word; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* We are expecting tcf_pedit to return a big endian value */ 7058c2ecf20Sopenharmony_ci mask = (__force __be32)~act->mangle.mask; 7068c2ecf20Sopenharmony_ci exact = (__force __be32)act->mangle.val; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (exact & ~mask) { 7098c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 action"); 7108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (off < offsetof(struct ipv6hdr, saddr)) { 7148c2ecf20Sopenharmony_ci err = nfp_fl_set_ip6_hop_limit_flow_label(off, exact, mask, 7158c2ecf20Sopenharmony_ci ip_hl_fl, extack); 7168c2ecf20Sopenharmony_ci } else if (off < offsetof(struct ipv6hdr, daddr)) { 7178c2ecf20Sopenharmony_ci word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact); 7188c2ecf20Sopenharmony_ci nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word, 7198c2ecf20Sopenharmony_ci exact, mask, ip_src); 7208c2ecf20Sopenharmony_ci } else if (off < offsetof(struct ipv6hdr, daddr) + 7218c2ecf20Sopenharmony_ci sizeof(struct in6_addr)) { 7228c2ecf20Sopenharmony_ci word = (off - offsetof(struct ipv6hdr, daddr)) / sizeof(exact); 7238c2ecf20Sopenharmony_ci nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, word, 7248c2ecf20Sopenharmony_ci exact, mask, ip_dst); 7258c2ecf20Sopenharmony_ci } else { 7268c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv6 header"); 7278c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int 7348c2ecf20Sopenharmony_cinfp_fl_set_tport(const struct flow_action_entry *act, u32 off, 7358c2ecf20Sopenharmony_ci struct nfp_fl_set_tport *set_tport, int opcode, 7368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci u32 exact, mask; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (off) { 7418c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of L4 header"); 7428c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci mask = ~act->mangle.mask; 7468c2ecf20Sopenharmony_ci exact = act->mangle.val; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (exact & ~mask) { 7498c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit L4 action"); 7508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val, 7548c2ecf20Sopenharmony_ci set_tport->tp_port_mask); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci set_tport->reserved = cpu_to_be16(0); 7578c2ecf20Sopenharmony_ci set_tport->head.jump_id = opcode; 7588c2ecf20Sopenharmony_ci set_tport->head.len_lw = sizeof(*set_tport) >> NFP_FL_LW_SIZ; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic u32 nfp_fl_csum_l4_to_flag(u8 ip_proto) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci switch (ip_proto) { 7668c2ecf20Sopenharmony_ci case 0: 7678c2ecf20Sopenharmony_ci /* Filter doesn't force proto match, 7688c2ecf20Sopenharmony_ci * both TCP and UDP will be updated if encountered 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci return TCA_CSUM_UPDATE_FLAG_TCP | TCA_CSUM_UPDATE_FLAG_UDP; 7718c2ecf20Sopenharmony_ci case IPPROTO_TCP: 7728c2ecf20Sopenharmony_ci return TCA_CSUM_UPDATE_FLAG_TCP; 7738c2ecf20Sopenharmony_ci case IPPROTO_UDP: 7748c2ecf20Sopenharmony_ci return TCA_CSUM_UPDATE_FLAG_UDP; 7758c2ecf20Sopenharmony_ci default: 7768c2ecf20Sopenharmony_ci /* All other protocols will be ignored by FW */ 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistruct nfp_flower_pedit_acts { 7828c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src; 7838c2ecf20Sopenharmony_ci struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl; 7848c2ecf20Sopenharmony_ci struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos; 7858c2ecf20Sopenharmony_ci struct nfp_fl_set_ip4_addrs set_ip_addr; 7868c2ecf20Sopenharmony_ci struct nfp_fl_set_tport set_tport; 7878c2ecf20Sopenharmony_ci struct nfp_fl_set_eth set_eth; 7888c2ecf20Sopenharmony_ci}; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int 7918c2ecf20Sopenharmony_cinfp_fl_commit_mangle(struct flow_cls_offload *flow, char *nfp_action, 7928c2ecf20Sopenharmony_ci int *a_len, struct nfp_flower_pedit_acts *set_act, 7938c2ecf20Sopenharmony_ci u32 *csum_updated) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct flow_rule *rule = flow_cls_offload_flow_rule(flow); 7968c2ecf20Sopenharmony_ci size_t act_size = 0; 7978c2ecf20Sopenharmony_ci u8 ip_proto = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 8008c2ecf20Sopenharmony_ci struct flow_match_basic match; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci flow_rule_match_basic(rule, &match); 8038c2ecf20Sopenharmony_ci ip_proto = match.key->ip_proto; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (set_act->set_eth.head.len_lw) { 8078c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_eth); 8088c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_eth, act_size); 8098c2ecf20Sopenharmony_ci *a_len += act_size; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (set_act->set_ip_ttl_tos.head.len_lw) { 8138c2ecf20Sopenharmony_ci nfp_action += act_size; 8148c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip_ttl_tos); 8158c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip_ttl_tos, act_size); 8168c2ecf20Sopenharmony_ci *a_len += act_size; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */ 8198c2ecf20Sopenharmony_ci *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR | 8208c2ecf20Sopenharmony_ci nfp_fl_csum_l4_to_flag(ip_proto); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (set_act->set_ip_addr.head.len_lw) { 8248c2ecf20Sopenharmony_ci nfp_action += act_size; 8258c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip_addr); 8268c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip_addr, act_size); 8278c2ecf20Sopenharmony_ci *a_len += act_size; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */ 8308c2ecf20Sopenharmony_ci *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR | 8318c2ecf20Sopenharmony_ci nfp_fl_csum_l4_to_flag(ip_proto); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (set_act->set_ip6_tc_hl_fl.head.len_lw) { 8358c2ecf20Sopenharmony_ci nfp_action += act_size; 8368c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip6_tc_hl_fl); 8378c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip6_tc_hl_fl, act_size); 8388c2ecf20Sopenharmony_ci *a_len += act_size; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* Hardware will automatically fix TCP/UDP checksum. */ 8418c2ecf20Sopenharmony_ci *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (set_act->set_ip6_dst.head.len_lw && 8458c2ecf20Sopenharmony_ci set_act->set_ip6_src.head.len_lw) { 8468c2ecf20Sopenharmony_ci /* TC compiles set src and dst IPv6 address as a single action, 8478c2ecf20Sopenharmony_ci * the hardware requires this to be 2 separate actions. 8488c2ecf20Sopenharmony_ci */ 8498c2ecf20Sopenharmony_ci nfp_action += act_size; 8508c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip6_src); 8518c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip6_src, act_size); 8528c2ecf20Sopenharmony_ci *a_len += act_size; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip6_dst); 8558c2ecf20Sopenharmony_ci memcpy(&nfp_action[sizeof(set_act->set_ip6_src)], 8568c2ecf20Sopenharmony_ci &set_act->set_ip6_dst, act_size); 8578c2ecf20Sopenharmony_ci *a_len += act_size; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* Hardware will automatically fix TCP/UDP checksum. */ 8608c2ecf20Sopenharmony_ci *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); 8618c2ecf20Sopenharmony_ci } else if (set_act->set_ip6_dst.head.len_lw) { 8628c2ecf20Sopenharmony_ci nfp_action += act_size; 8638c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip6_dst); 8648c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip6_dst, act_size); 8658c2ecf20Sopenharmony_ci *a_len += act_size; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Hardware will automatically fix TCP/UDP checksum. */ 8688c2ecf20Sopenharmony_ci *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); 8698c2ecf20Sopenharmony_ci } else if (set_act->set_ip6_src.head.len_lw) { 8708c2ecf20Sopenharmony_ci nfp_action += act_size; 8718c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_ip6_src); 8728c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_ip6_src, act_size); 8738c2ecf20Sopenharmony_ci *a_len += act_size; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* Hardware will automatically fix TCP/UDP checksum. */ 8768c2ecf20Sopenharmony_ci *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci if (set_act->set_tport.head.len_lw) { 8798c2ecf20Sopenharmony_ci nfp_action += act_size; 8808c2ecf20Sopenharmony_ci act_size = sizeof(set_act->set_tport); 8818c2ecf20Sopenharmony_ci memcpy(nfp_action, &set_act->set_tport, act_size); 8828c2ecf20Sopenharmony_ci *a_len += act_size; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Hardware will automatically fix TCP/UDP checksum. */ 8858c2ecf20Sopenharmony_ci *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic int 8928c2ecf20Sopenharmony_cinfp_fl_pedit(const struct flow_action_entry *act, 8938c2ecf20Sopenharmony_ci struct flow_cls_offload *flow, char *nfp_action, int *a_len, 8948c2ecf20Sopenharmony_ci u32 *csum_updated, struct nfp_flower_pedit_acts *set_act, 8958c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci enum flow_action_mangle_base htype; 8988c2ecf20Sopenharmony_ci u32 offset; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci htype = act->mangle.htype; 9018c2ecf20Sopenharmony_ci offset = act->mangle.offset; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci switch (htype) { 9048c2ecf20Sopenharmony_ci case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: 9058c2ecf20Sopenharmony_ci return nfp_fl_set_eth(act, offset, &set_act->set_eth, extack); 9068c2ecf20Sopenharmony_ci case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: 9078c2ecf20Sopenharmony_ci return nfp_fl_set_ip4(act, offset, &set_act->set_ip_addr, 9088c2ecf20Sopenharmony_ci &set_act->set_ip_ttl_tos, extack); 9098c2ecf20Sopenharmony_ci case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: 9108c2ecf20Sopenharmony_ci return nfp_fl_set_ip6(act, offset, &set_act->set_ip6_dst, 9118c2ecf20Sopenharmony_ci &set_act->set_ip6_src, 9128c2ecf20Sopenharmony_ci &set_act->set_ip6_tc_hl_fl, extack); 9138c2ecf20Sopenharmony_ci case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: 9148c2ecf20Sopenharmony_ci return nfp_fl_set_tport(act, offset, &set_act->set_tport, 9158c2ecf20Sopenharmony_ci NFP_FL_ACTION_OPCODE_SET_TCP, extack); 9168c2ecf20Sopenharmony_ci case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: 9178c2ecf20Sopenharmony_ci return nfp_fl_set_tport(act, offset, &set_act->set_tport, 9188c2ecf20Sopenharmony_ci NFP_FL_ACTION_OPCODE_SET_UDP, extack); 9198c2ecf20Sopenharmony_ci default: 9208c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported header"); 9218c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic int 9268c2ecf20Sopenharmony_cinfp_flower_output_action(struct nfp_app *app, 9278c2ecf20Sopenharmony_ci const struct flow_action_entry *act, 9288c2ecf20Sopenharmony_ci struct nfp_fl_payload *nfp_fl, int *a_len, 9298c2ecf20Sopenharmony_ci struct net_device *netdev, bool last, 9308c2ecf20Sopenharmony_ci enum nfp_flower_tun_type *tun_type, int *tun_out_cnt, 9318c2ecf20Sopenharmony_ci int *out_cnt, u32 *csum_updated, bool pkt_host, 9328c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct nfp_flower_priv *priv = app->priv; 9358c2ecf20Sopenharmony_ci struct nfp_fl_output *output; 9368c2ecf20Sopenharmony_ci int err, prelag_size; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* If csum_updated has not been reset by now, it means HW will 9398c2ecf20Sopenharmony_ci * incorrectly update csums when they are not requested. 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_ci if (*csum_updated) { 9428c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: set actions without updating checksums are not supported"); 9438c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) { 9478c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: mirred output increases action list size beyond the allowed maximum"); 9488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; 9528c2ecf20Sopenharmony_ci err = nfp_fl_output(app, output, act, nfp_fl, last, netdev, *tun_type, 9538c2ecf20Sopenharmony_ci tun_out_cnt, pkt_host, extack); 9548c2ecf20Sopenharmony_ci if (err) 9558c2ecf20Sopenharmony_ci return err; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_output); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (priv->flower_en_feats & NFP_FL_ENABLE_LAG) { 9608c2ecf20Sopenharmony_ci /* nfp_fl_pre_lag returns -err or size of prelag action added. 9618c2ecf20Sopenharmony_ci * This will be 0 if it is not egressing to a lag dev. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci prelag_size = nfp_fl_pre_lag(app, act, nfp_fl, *a_len, extack); 9648c2ecf20Sopenharmony_ci if (prelag_size < 0) { 9658c2ecf20Sopenharmony_ci return prelag_size; 9668c2ecf20Sopenharmony_ci } else if (prelag_size > 0 && (!last || *out_cnt)) { 9678c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: LAG action has to be last action in action list"); 9688c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci *a_len += prelag_size; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci (*out_cnt)++; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return 0; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic int 9798c2ecf20Sopenharmony_cinfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, 9808c2ecf20Sopenharmony_ci struct flow_cls_offload *flow, 9818c2ecf20Sopenharmony_ci struct nfp_fl_payload *nfp_fl, int *a_len, 9828c2ecf20Sopenharmony_ci struct net_device *netdev, 9838c2ecf20Sopenharmony_ci enum nfp_flower_tun_type *tun_type, int *tun_out_cnt, 9848c2ecf20Sopenharmony_ci int *out_cnt, u32 *csum_updated, 9858c2ecf20Sopenharmony_ci struct nfp_flower_pedit_acts *set_act, bool *pkt_host, 9868c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, int act_idx) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct nfp_fl_pre_tunnel *pre_tun; 9898c2ecf20Sopenharmony_ci struct nfp_fl_set_tun *set_tun; 9908c2ecf20Sopenharmony_ci struct nfp_fl_push_vlan *psh_v; 9918c2ecf20Sopenharmony_ci struct nfp_fl_push_mpls *psh_m; 9928c2ecf20Sopenharmony_ci struct nfp_fl_pop_vlan *pop_v; 9938c2ecf20Sopenharmony_ci struct nfp_fl_pop_mpls *pop_m; 9948c2ecf20Sopenharmony_ci struct nfp_fl_set_mpls *set_m; 9958c2ecf20Sopenharmony_ci int err; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci switch (act->id) { 9988c2ecf20Sopenharmony_ci case FLOW_ACTION_DROP: 9998c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP); 10008c2ecf20Sopenharmony_ci break; 10018c2ecf20Sopenharmony_ci case FLOW_ACTION_REDIRECT_INGRESS: 10028c2ecf20Sopenharmony_ci case FLOW_ACTION_REDIRECT: 10038c2ecf20Sopenharmony_ci err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev, 10048c2ecf20Sopenharmony_ci true, tun_type, tun_out_cnt, 10058c2ecf20Sopenharmony_ci out_cnt, csum_updated, *pkt_host, 10068c2ecf20Sopenharmony_ci extack); 10078c2ecf20Sopenharmony_ci if (err) 10088c2ecf20Sopenharmony_ci return err; 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci case FLOW_ACTION_MIRRED_INGRESS: 10118c2ecf20Sopenharmony_ci case FLOW_ACTION_MIRRED: 10128c2ecf20Sopenharmony_ci err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev, 10138c2ecf20Sopenharmony_ci false, tun_type, tun_out_cnt, 10148c2ecf20Sopenharmony_ci out_cnt, csum_updated, *pkt_host, 10158c2ecf20Sopenharmony_ci extack); 10168c2ecf20Sopenharmony_ci if (err) 10178c2ecf20Sopenharmony_ci return err; 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_POP: 10208c2ecf20Sopenharmony_ci if (*a_len + 10218c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) { 10228c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop vlan"); 10238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci pop_v = (struct nfp_fl_pop_vlan *)&nfp_fl->action_data[*a_len]; 10278c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_POPV); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci nfp_fl_pop_vlan(pop_v); 10308c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_pop_vlan); 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci case FLOW_ACTION_VLAN_PUSH: 10338c2ecf20Sopenharmony_ci if (*a_len + 10348c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ) { 10358c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push vlan"); 10368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len]; 10408c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci nfp_fl_push_vlan(psh_v, act); 10438c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_push_vlan); 10448c2ecf20Sopenharmony_ci break; 10458c2ecf20Sopenharmony_ci case FLOW_ACTION_TUNNEL_ENCAP: { 10468c2ecf20Sopenharmony_ci const struct ip_tunnel_info *ip_tun = act->tunnel; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci *tun_type = nfp_fl_get_tun_from_act(app, flow, act, act_idx); 10498c2ecf20Sopenharmony_ci if (*tun_type == NFP_FL_TUNNEL_NONE) { 10508c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel type in action list"); 10518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (ip_tun->mode & ~NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS) { 10558c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel flags in action list"); 10568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci /* Pre-tunnel action is required for tunnel encap. 10608c2ecf20Sopenharmony_ci * This checks for next hop entries on NFP. 10618c2ecf20Sopenharmony_ci * If none, the packet falls back before applying other actions. 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_ci if (*a_len + sizeof(struct nfp_fl_pre_tunnel) + 10648c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_set_tun) > NFP_FL_MAX_A_SIZ) { 10658c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at tunnel encap"); 10668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len); 10708c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 10718c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_pre_tunnel); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci err = nfp_fl_push_geneve_options(nfp_fl, a_len, act, extack); 10748c2ecf20Sopenharmony_ci if (err) 10758c2ecf20Sopenharmony_ci return err; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci set_tun = (void *)&nfp_fl->action_data[*a_len]; 10788c2ecf20Sopenharmony_ci err = nfp_fl_set_tun(app, set_tun, act, pre_tun, *tun_type, 10798c2ecf20Sopenharmony_ci netdev, extack); 10808c2ecf20Sopenharmony_ci if (err) 10818c2ecf20Sopenharmony_ci return err; 10828c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_set_tun); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci case FLOW_ACTION_TUNNEL_DECAP: 10868c2ecf20Sopenharmony_ci /* Tunnel decap is handled by default so accept action. */ 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci case FLOW_ACTION_MANGLE: 10898c2ecf20Sopenharmony_ci if (nfp_fl_pedit(act, flow, &nfp_fl->action_data[*a_len], 10908c2ecf20Sopenharmony_ci a_len, csum_updated, set_act, extack)) 10918c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10928c2ecf20Sopenharmony_ci break; 10938c2ecf20Sopenharmony_ci case FLOW_ACTION_CSUM: 10948c2ecf20Sopenharmony_ci /* csum action requests recalc of something we have not fixed */ 10958c2ecf20Sopenharmony_ci if (act->csum_flags & ~*csum_updated) { 10968c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported csum update action in action list"); 10978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci /* If we will correctly fix the csum we can remove it from the 11008c2ecf20Sopenharmony_ci * csum update list. Which will later be used to check support. 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_ci *csum_updated &= ~act->csum_flags; 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci case FLOW_ACTION_MPLS_PUSH: 11058c2ecf20Sopenharmony_ci if (*a_len + 11068c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_push_mpls) > NFP_FL_MAX_A_SIZ) { 11078c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push MPLS"); 11088c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci psh_m = (struct nfp_fl_push_mpls *)&nfp_fl->action_data[*a_len]; 11128c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci err = nfp_fl_push_mpls(psh_m, act, extack); 11158c2ecf20Sopenharmony_ci if (err) 11168c2ecf20Sopenharmony_ci return err; 11178c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_push_mpls); 11188c2ecf20Sopenharmony_ci break; 11198c2ecf20Sopenharmony_ci case FLOW_ACTION_MPLS_POP: 11208c2ecf20Sopenharmony_ci if (*a_len + 11218c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_pop_mpls) > NFP_FL_MAX_A_SIZ) { 11228c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop MPLS"); 11238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci pop_m = (struct nfp_fl_pop_mpls *)&nfp_fl->action_data[*a_len]; 11278c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci nfp_fl_pop_mpls(pop_m, act); 11308c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_pop_mpls); 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci case FLOW_ACTION_MPLS_MANGLE: 11338c2ecf20Sopenharmony_ci if (*a_len + 11348c2ecf20Sopenharmony_ci sizeof(struct nfp_fl_set_mpls) > NFP_FL_MAX_A_SIZ) { 11358c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at set MPLS"); 11368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci set_m = (struct nfp_fl_set_mpls *)&nfp_fl->action_data[*a_len]; 11408c2ecf20Sopenharmony_ci nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci nfp_fl_set_mpls(set_m, act); 11438c2ecf20Sopenharmony_ci *a_len += sizeof(struct nfp_fl_set_mpls); 11448c2ecf20Sopenharmony_ci break; 11458c2ecf20Sopenharmony_ci case FLOW_ACTION_PTYPE: 11468c2ecf20Sopenharmony_ci /* TC ptype skbedit sets PACKET_HOST for ingress redirect. */ 11478c2ecf20Sopenharmony_ci if (act->ptype != PACKET_HOST) 11488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci *pkt_host = true; 11518c2ecf20Sopenharmony_ci break; 11528c2ecf20Sopenharmony_ci default: 11538c2ecf20Sopenharmony_ci /* Currently we do not handle any other actions. */ 11548c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported action in action list"); 11558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci return 0; 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistatic bool nfp_fl_check_mangle_start(struct flow_action *flow_act, 11628c2ecf20Sopenharmony_ci int current_act_idx) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct flow_action_entry current_act; 11658c2ecf20Sopenharmony_ci struct flow_action_entry prev_act; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci current_act = flow_act->entries[current_act_idx]; 11688c2ecf20Sopenharmony_ci if (current_act.id != FLOW_ACTION_MANGLE) 11698c2ecf20Sopenharmony_ci return false; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (current_act_idx == 0) 11728c2ecf20Sopenharmony_ci return true; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci prev_act = flow_act->entries[current_act_idx - 1]; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return prev_act.id != FLOW_ACTION_MANGLE; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic bool nfp_fl_check_mangle_end(struct flow_action *flow_act, 11808c2ecf20Sopenharmony_ci int current_act_idx) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct flow_action_entry current_act; 11838c2ecf20Sopenharmony_ci struct flow_action_entry next_act; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci current_act = flow_act->entries[current_act_idx]; 11868c2ecf20Sopenharmony_ci if (current_act.id != FLOW_ACTION_MANGLE) 11878c2ecf20Sopenharmony_ci return false; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (current_act_idx == flow_act->num_entries) 11908c2ecf20Sopenharmony_ci return true; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci next_act = flow_act->entries[current_act_idx + 1]; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return next_act.id != FLOW_ACTION_MANGLE; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ciint nfp_flower_compile_action(struct nfp_app *app, 11988c2ecf20Sopenharmony_ci struct flow_cls_offload *flow, 11998c2ecf20Sopenharmony_ci struct net_device *netdev, 12008c2ecf20Sopenharmony_ci struct nfp_fl_payload *nfp_flow, 12018c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci int act_len, act_cnt, err, tun_out_cnt, out_cnt, i; 12048c2ecf20Sopenharmony_ci struct nfp_flower_pedit_acts set_act; 12058c2ecf20Sopenharmony_ci enum nfp_flower_tun_type tun_type; 12068c2ecf20Sopenharmony_ci struct flow_action_entry *act; 12078c2ecf20Sopenharmony_ci bool pkt_host = false; 12088c2ecf20Sopenharmony_ci u32 csum_updated = 0; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (!flow_action_hw_stats_check(&flow->rule->action, extack, 12118c2ecf20Sopenharmony_ci FLOW_ACTION_HW_STATS_DELAYED_BIT)) 12128c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ); 12158c2ecf20Sopenharmony_ci nfp_flow->meta.act_len = 0; 12168c2ecf20Sopenharmony_ci tun_type = NFP_FL_TUNNEL_NONE; 12178c2ecf20Sopenharmony_ci act_len = 0; 12188c2ecf20Sopenharmony_ci act_cnt = 0; 12198c2ecf20Sopenharmony_ci tun_out_cnt = 0; 12208c2ecf20Sopenharmony_ci out_cnt = 0; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci flow_action_for_each(i, act, &flow->rule->action) { 12238c2ecf20Sopenharmony_ci if (nfp_fl_check_mangle_start(&flow->rule->action, i)) 12248c2ecf20Sopenharmony_ci memset(&set_act, 0, sizeof(set_act)); 12258c2ecf20Sopenharmony_ci err = nfp_flower_loop_action(app, act, flow, nfp_flow, &act_len, 12268c2ecf20Sopenharmony_ci netdev, &tun_type, &tun_out_cnt, 12278c2ecf20Sopenharmony_ci &out_cnt, &csum_updated, 12288c2ecf20Sopenharmony_ci &set_act, &pkt_host, extack, i); 12298c2ecf20Sopenharmony_ci if (err) 12308c2ecf20Sopenharmony_ci return err; 12318c2ecf20Sopenharmony_ci act_cnt++; 12328c2ecf20Sopenharmony_ci if (nfp_fl_check_mangle_end(&flow->rule->action, i)) 12338c2ecf20Sopenharmony_ci nfp_fl_commit_mangle(flow, 12348c2ecf20Sopenharmony_ci &nfp_flow->action_data[act_len], 12358c2ecf20Sopenharmony_ci &act_len, &set_act, &csum_updated); 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* We optimise when the action list is small, this can unfortunately 12398c2ecf20Sopenharmony_ci * not happen once we have more than one action in the action list. 12408c2ecf20Sopenharmony_ci */ 12418c2ecf20Sopenharmony_ci if (act_cnt > 1) 12428c2ecf20Sopenharmony_ci nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci nfp_flow->meta.act_len = act_len; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 1248