Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/microchip/sparx5/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 40 kB image not shown  

Quelle  sparx5_tc_flower.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/* Microchip VCAP API
 *
 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
 */


#include <net/tc_act/tc_gate.h>
#include <net/tcp.h>

#include "sparx5_tc.h"
#include "vcap_api.h"
#include "vcap_api_client.h"
#include "vcap_tc.h"
#include "sparx5_main.h"
#include "sparx5_vcap_impl.h"

#define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */

/* Collect keysets and type ids for multiple rules per size */
struct sparx5_wildcard_rule {
 bool selected;
 u8 value;
 u8 mask;
 enum vcap_keyfield_set keyset;
};

struct sparx5_multiple_rules {
 struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE];
};

struct sparx5_tc_flower_template {
 struct list_head list; /* for insertion in the list of templates */
 int cid; /* chain id */
 enum vcap_keyfield_set orig; /* keyset used before the template */
 enum vcap_keyfield_set keyset; /* new keyset used by template */
 u16 l3_proto; /* protocol specified in the template */
};

/* SparX-5 VCAP fragment types:
 * 0 = no fragment, 1 = initial fragment,
 * 2 = suspicious fragment, 3 = valid follow-up fragment
 */

enum {                   /* key / mask */
 FRAG_NOT   = 0x03, /* 0 / 3 */
 FRAG_SOME  = 0x11, /* 1 / 1 */
 FRAG_FIRST = 0x13, /* 1 / 3 */
 FRAG_LATER = 0x33, /* 3 / 3 */
 FRAG_INVAL = 0xff, /* invalid */
};

/* Flower fragment flag to VCAP fragment type mapping */
static const u8 sparx5_vcap_frag_map[4][4] = {    /* is_frag */
 { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_FIRST }, /* 0/0 */
 { FRAG_NOT,   FRAG_NOT,   FRAG_INVAL, FRAG_INVAL }, /* 0/1 */
 { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_INVAL }, /* 1/0 */
 { FRAG_SOME,  FRAG_LATER, FRAG_INVAL, FRAG_FIRST }  /* 1/1 */
 /* 0/0       0/1   1/0       1/1 <-- first_frag */
};

static int
sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st)
{
 int err = 0;

 switch (st->tpid) {
 case ETH_P_8021Q:
  err = vcap_rule_add_key_u32(st->vrule,
         VCAP_KF_8021Q_TPID,
         SPX5_TPID_SEL_8100, ~0);
  break;
 case ETH_P_8021AD:
  err = vcap_rule_add_key_u32(st->vrule,
         VCAP_KF_8021Q_TPID,
         SPX5_TPID_SEL_88A8, ~0);
  break;
 default:
  NL_SET_ERR_MSG_MOD(st->fco->common.extack,
       "Invalid vlan proto");
  err = -EINVAL;
  break;
 }
 return err;
}

static int
sparx5_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
{
 struct flow_match_basic mt;
 int err = 0;

 flow_rule_match_basic(st->frule, &mt);

 if (mt.mask->n_proto) {
  st->l3_proto = be16_to_cpu(mt.key->n_proto);
  if (!sparx5_vcap_is_known_etype(st->admin, st->l3_proto)) {
   err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
          st->l3_proto, ~0);
   if (err)
    goto out;
  } else if (st->l3_proto == ETH_P_IP) {
   err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
          VCAP_BIT_1);
   if (err)
    goto out;
  } else if (st->l3_proto == ETH_P_IPV6) {
   err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
          VCAP_BIT_0);
   if (err)
    goto out;
   if (st->admin->vtype == VCAP_TYPE_IS0) {
    err = vcap_rule_add_key_bit(st->vrule,
           VCAP_KF_IP_SNAP_IS,
           VCAP_BIT_1);
    if (err)
     goto out;
   }
  }
 }

 if (mt.mask->ip_proto) {
  st->l4_proto = mt.key->ip_proto;
  if (st->l4_proto == IPPROTO_TCP) {
   err = vcap_rule_add_key_bit(st->vrule,
          VCAP_KF_TCP_IS,
          VCAP_BIT_1);
   if (err)
    goto out;
  } else if (st->l4_proto == IPPROTO_UDP) {
   err = vcap_rule_add_key_bit(st->vrule,
          VCAP_KF_TCP_IS,
          VCAP_BIT_0);
   if (err)
    goto out;
   if (st->admin->vtype == VCAP_TYPE_IS0) {
    err = vcap_rule_add_key_bit(st->vrule,
           VCAP_KF_TCP_UDP_IS,
           VCAP_BIT_1);
    if (err)
     goto out;
   }
  } else {
   err = vcap_rule_add_key_u32(st->vrule,
          VCAP_KF_L3_IP_PROTO,
          st->l4_proto, ~0);
   if (err)
    goto out;
  }
 }

 st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_BASIC);

 return err;

out:
 NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
 return err;
}

static int
sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
{
 struct netlink_ext_ack *extack = st->fco->common.extack;
 struct flow_match_control mt;
 u32 value, mask;
 int err = 0;

 flow_rule_match_control(st->frule, &mt);

 if (mt.mask->flags & (FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG)) {
  u8 is_frag_key = !!(mt.key->flags & FLOW_DIS_IS_FRAGMENT);
  u8 is_frag_mask = !!(mt.mask->flags & FLOW_DIS_IS_FRAGMENT);
  u8 is_frag_idx = (is_frag_key << 1) | is_frag_mask;

  u8 first_frag_key = !!(mt.key->flags & FLOW_DIS_FIRST_FRAG);
  u8 first_frag_mask = !!(mt.mask->flags & FLOW_DIS_FIRST_FRAG);
  u8 first_frag_idx = (first_frag_key << 1) | first_frag_mask;

  /* Lookup verdict based on the 2 + 2 input bits */
  u8 vdt = sparx5_vcap_frag_map[is_frag_idx][first_frag_idx];

  if (vdt == FRAG_INVAL) {
   NL_SET_ERR_MSG_MOD(extack,
        "Match on invalid fragment flag combination");
   return -EINVAL;
  }

  /* Extract VCAP fragment key and mask from verdict */
  value = (vdt >> 4) & 0x3;
  mask = vdt & 0x3;

  err = vcap_rule_add_key_u32(st->vrule,
         VCAP_KF_L3_FRAGMENT_TYPE,
         value, mask);
  if (err) {
   NL_SET_ERR_MSG_MOD(extack, "ip_frag parse error");
   return err;
  }
 }

 if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT |
          FLOW_DIS_FIRST_FRAG,
          mt.mask->flags, extack))
  return -EOPNOTSUPP;

 st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL);

 return err;
}

static int
sparx5_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st)
{
 if (st->admin->vtype != VCAP_TYPE_IS0) {
  NL_SET_ERR_MSG_MOD(st->fco->common.extack,
       "cvlan not supported in this VCAP");
  return -EINVAL;
 }

 return vcap_tc_flower_handler_cvlan_usage(st);
}

static int
sparx5_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
{
 enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS;
 enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS;
 int err;

 if (st->admin->vtype == VCAP_TYPE_IS0) {
  vid_key = VCAP_KF_8021Q_VID0;
  pcp_key = VCAP_KF_8021Q_PCP0;
 }

 err = vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key);
 if (err)
  return err;

 if (st->admin->vtype == VCAP_TYPE_ES0 && st->tpid)
  err = sparx5_tc_flower_es0_tpid(st);

 return err;
}

static int (*sparx5_tc_flower_usage_handlers[])(struct vcap_tc_flower_parse_usage *st) = {
 [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
 [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
 [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
 [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage,
 [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
 [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
 [FLOW_DISSECTOR_KEY_CVLAN] = sparx5_tc_flower_handler_cvlan_usage,
 [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
 [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
 [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
 [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
};

static int sparx5_tc_use_dissectors(struct vcap_tc_flower_parse_usage *st,
        struct vcap_admin *admin,
        struct vcap_rule *vrule)
{
 int idx, err = 0;

 for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) {
  if (!flow_rule_match_key(st->frule, idx))
   continue;
  if (!sparx5_tc_flower_usage_handlers[idx])
   continue;
  err = sparx5_tc_flower_usage_handlers[idx](st);
  if (err)
   return err;
 }

 if (st->frule->match.dissector->used_keys ^ st->used_keys) {
  NL_SET_ERR_MSG_MOD(st->fco->common.extack,
       "Unsupported match item");
  return -ENOENT;
 }

 return err;
}

static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
      struct net_device *ndev,
      struct flow_cls_offload *fco,
      bool ingress)
{
 struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
 struct flow_action_entry *actent, *last_actent = NULL;
 struct flow_action *act = &rule->action;
 u64 action_mask = 0;
 int idx;

 if (!flow_action_has_entries(act)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
  return -EINVAL;
 }

 if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
  return -EOPNOTSUPP;

 flow_action_for_each(idx, actent, act) {
  if (action_mask & BIT(actent->id)) {
   NL_SET_ERR_MSG_MOD(fco->common.extack,
        "More actions of the same type");
   return -EINVAL;
  }
  action_mask |= BIT(actent->id);
  last_actent = actent; /* Save last action for later check */
 }

 /* Check if last action is a goto
 * The last chain/lookup does not need to have a goto action
 */

 if (last_actent->id == FLOW_ACTION_GOTO) {
  /* Check if the destination chain is in one of the VCAPs */
  if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
      last_actent->chain_index)) {
   NL_SET_ERR_MSG_MOD(fco->common.extack,
        "Invalid goto chain");
   return -EINVAL;
  }
 } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,
           ingress)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Last action must be 'goto'");
  return -EINVAL;
 }

 /* Catch unsupported combinations of actions */
 if (action_mask & BIT(FLOW_ACTION_TRAP) &&
     action_mask & BIT(FLOW_ACTION_ACCEPT)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Cannot combine pass and trap action");
  return -EOPNOTSUPP;
 }

 if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
     action_mask & BIT(FLOW_ACTION_VLAN_POP)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Cannot combine vlan push and pop action");
  return -EOPNOTSUPP;
 }

 if (action_mask & BIT(FLOW_ACTION_VLAN_PUSH) &&
     action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Cannot combine vlan push and modify action");
  return -EOPNOTSUPP;
 }

 if (action_mask & BIT(FLOW_ACTION_VLAN_POP) &&
     action_mask & BIT(FLOW_ACTION_VLAN_MANGLE)) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Cannot combine vlan pop and modify action");
  return -EOPNOTSUPP;
 }

 return 0;
}

/* Add a rule counter action */
static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
          struct vcap_rule *vrule)
{
 int err;

 switch (admin->vtype) {
 case VCAP_TYPE_IS0:
  break;
 case VCAP_TYPE_ES0:
  err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX,
            vrule->id);
  if (err)
   return err;
  vcap_rule_set_counter_id(vrule, vrule->id);
  break;
 case VCAP_TYPE_IS2:
 case VCAP_TYPE_ES2:
  err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID,
            vrule->id);
  if (err)
   return err;
  vcap_rule_set_counter_id(vrule, vrule->id);
  break;
 default:
  pr_err("%s:%d: vcap type: %d not supported\n",
         __func__, __LINE__, admin->vtype);
  break;
 }
 return 0;
}

/* Collect all port keysets and apply the first of them, possibly wildcarded */
static int sparx5_tc_select_protocol_keyset(struct net_device *ndev,
         struct vcap_rule *vrule,
         struct vcap_admin *admin,
         u16 l3_proto,
         struct sparx5_multiple_rules *multi)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct vcap_keyset_list portkeysetlist = {};
 enum vcap_keyfield_set portkeysets[10] = {};
 struct vcap_keyset_list matches = {};
 enum vcap_keyfield_set keysets[10];
 int idx, jdx, err = 0, count = 0;
 struct sparx5_wildcard_rule *mru;
 const struct vcap_set *kinfo;
 struct vcap_control *vctrl;

 vctrl = port->sparx5->vcap_ctrl;

 /* Find the keysets that the rule can use */
 matches.keysets = keysets;
 matches.max = ARRAY_SIZE(keysets);
 if (!vcap_rule_find_keysets(vrule, &matches))
  return -EINVAL;

 /* Find the keysets that the port configuration supports */
 portkeysetlist.max = ARRAY_SIZE(portkeysets);
 portkeysetlist.keysets = portkeysets;
 err = sparx5_vcap_get_port_keyset(ndev,
       admin, vrule->vcap_chain_id,
       l3_proto,
       &portkeysetlist);
 if (err)
  return err;

 /* Find the intersection of the two sets of keyset */
 for (idx = 0; idx < portkeysetlist.cnt; ++idx) {
  kinfo = vcap_keyfieldset(vctrl, admin->vtype,
      portkeysetlist.keysets[idx]);
  if (!kinfo)
   continue;

  /* Find a port keyset that matches the required keys
 * If there are multiple keysets then compose a type id mask
 */

  for (jdx = 0; jdx < matches.cnt; ++jdx) {
   if (portkeysetlist.keysets[idx] != matches.keysets[jdx])
    continue;

   mru = &multi->rule[kinfo->sw_per_item];
   if (!mru->selected) {
    mru->selected = true;
    mru->keyset = portkeysetlist.keysets[idx];
    mru->value = kinfo->type_id;
   }
   mru->value &= kinfo->type_id;
   mru->mask |= kinfo->type_id;
   ++count;
  }
 }
 if (count == 0)
  return -EPROTO;

 if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt)
  return -ENOENT;

 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
  mru = &multi->rule[idx];
  if (!mru->selected)
   continue;

  /* Align the mask to the combined value */
  mru->mask ^= mru->value;
 }

 /* Set the chosen keyset on the rule and set a wildcarded type if there
 * are more than one keyset
 */

 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
  mru = &multi->rule[idx];
  if (!mru->selected)
   continue;

  vcap_set_rule_set_keyset(vrule, mru->keyset);
  if (count > 1)
   /* Some keysets do not have a type field */
   vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE,
           mru->value,
           ~mru->mask);
  mru->selected = false/* mark as done */
  break/* Stop here and add more rules later */
 }
 return err;
}

static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl,
       struct flow_cls_offload *fco,
       struct vcap_rule *erule,
       struct vcap_admin *admin,
       struct sparx5_wildcard_rule *rule)
{
 enum vcap_key_field keylist[] = {
  VCAP_KF_IF_IGR_PORT_MASK,
  VCAP_KF_IF_IGR_PORT_MASK_SEL,
  VCAP_KF_IF_IGR_PORT_MASK_RNG,
  VCAP_KF_LOOKUP_FIRST_IS,
  VCAP_KF_TYPE,
 };
 struct vcap_rule *vrule;
 int err;

 /* Add an extra rule with a special user and the new keyset */
 erule->user = VCAP_USER_TC_EXTRA;
 vrule = vcap_copy_rule(erule);
 if (IS_ERR(vrule))
  return PTR_ERR(vrule);

 /* Link the new rule to the existing rule with the cookie */
 vrule->cookie = erule->cookie;
 vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true);
 err = vcap_set_rule_set_keyset(vrule, rule->keyset);
 if (err) {
  pr_err("%s:%d: could not set keyset %s in rule: %u\n",
         __func__, __LINE__,
         vcap_keyset_name(vctrl, rule->keyset),
         vrule->id);
  goto out;
 }

 /* Some keysets do not have a type field, so ignore return value */
 vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask);

 err = vcap_set_rule_set_actionset(vrule, erule->actionset);
 if (err)
  goto out;

 err = sparx5_tc_add_rule_counter(admin, vrule);
 if (err)
  goto out;

 err = vcap_val_rule(vrule, ETH_P_ALL);
 if (err) {
  pr_err("%s:%d: could not validate rule: %u\n",
         __func__, __LINE__, vrule->id);
  vcap_set_tc_exterr(fco, vrule);
  goto out;
 }
 err = vcap_add_rule(vrule);
 if (err) {
  pr_err("%s:%d: could not add rule: %u\n",
         __func__, __LINE__, vrule->id);
  goto out;
 }
out:
 vcap_free_rule(vrule);
 return err;
}

static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl,
      struct flow_cls_offload *fco,
      struct vcap_rule *erule,
      struct vcap_admin *admin,
      struct sparx5_multiple_rules *multi)
{
 int idx, err = 0;

 for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) {
  if (!multi->rule[idx].selected)
   continue;

  err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin,
           &multi->rule[idx]);
  if (err)
   break;
 }
 return err;
}

/* Add the actionset that is the default for the VCAP type */
static int sparx5_tc_set_actionset(struct vcap_admin *admin,
       struct vcap_rule *vrule)
{
 enum vcap_actionfield_set aset;
 int err = 0;

 switch (admin->vtype) {
 case VCAP_TYPE_IS0:
  aset = VCAP_AFS_CLASSIFICATION;
  break;
 case VCAP_TYPE_IS2:
  aset = VCAP_AFS_BASE_TYPE;
  break;
 case VCAP_TYPE_ES0:
  aset = VCAP_AFS_ES0;
  break;
 case VCAP_TYPE_ES2:
  aset = VCAP_AFS_BASE_TYPE;
  break;
 default:
  pr_err("%s:%d: %s\n", __func__, __LINE__, "Invalid VCAP type");
  return -EINVAL;
 }
 /* Do not overwrite any current actionset */
 if (vrule->actionset == VCAP_AFS_NO_VALUE)
  err = vcap_set_rule_set_actionset(vrule, aset);
 return err;
}

/* Add the VCAP key to match on for a rule target value */
static int sparx5_tc_add_rule_link_target(struct vcap_admin *admin,
       struct vcap_rule *vrule,
       int target_cid)
{
 int link_val = target_cid % VCAP_CID_LOOKUP_SIZE;
 int err;

 if (!link_val)
  return 0;

 switch (admin->vtype) {
 case VCAP_TYPE_IS0:
  /* Add NXT_IDX key for chaining rules between IS0 instances */
  err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL,
         1, /* enable */
         ~0);
  if (err)
   return err;
  return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX,
          link_val, /* target */
          ~0);
 case VCAP_TYPE_IS2:
  /* Add PAG key for chaining rules from IS0 */
  return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG,
          link_val, /* target */
          ~0);
 case VCAP_TYPE_ES0:
 case VCAP_TYPE_ES2:
  /* Add ISDX key for chaining rules from IS0 */
  return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, link_val,
          ~0);
 default:
  break;
 }
 return 0;
}

/* Add the VCAP action that adds a target value to a rule */
static int sparx5_tc_add_rule_link(struct vcap_control *vctrl,
       struct vcap_admin *admin,
       struct vcap_rule *vrule,
       int from_cid, int to_cid)
{
 struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid);
 int diff, err = 0;

 if (!to_admin) {
  pr_err("%s:%d: unsupported chain direction: %d\n",
         __func__, __LINE__, to_cid);
  return -EINVAL;
 }

 diff = vcap_chain_offset(vctrl, from_cid, to_cid);
 if (!diff)
  return 0;

 if (admin->vtype == VCAP_TYPE_IS0 &&
     to_admin->vtype == VCAP_TYPE_IS0) {
  /* Between IS0 instances the G_IDX value is used */
  err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX, diff);
  if (err)
   goto out;
  err = vcap_rule_add_action_u32(vrule, VCAP_AF_NXT_IDX_CTRL,
            1); /* Replace */
  if (err)
   goto out;
 } else if (admin->vtype == VCAP_TYPE_IS0 &&
     to_admin->vtype == VCAP_TYPE_IS2) {
  /* Between IS0 and IS2 the PAG value is used */
  err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff);
  if (err)
   goto out;
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_PAG_OVERRIDE_MASK,
            0xff);
  if (err)
   goto out;
 } else if (admin->vtype == VCAP_TYPE_IS0 &&
     (to_admin->vtype == VCAP_TYPE_ES0 ||
      to_admin->vtype == VCAP_TYPE_ES2)) {
  /* Between IS0 and ES0/ES2 the ISDX value is used */
  err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL,
            diff);
  if (err)
   goto out;
  err = vcap_rule_add_action_bit(vrule,
            VCAP_AF_ISDX_ADD_REPLACE_SEL,
            VCAP_BIT_1);
  if (err)
   goto out;
 } else {
  pr_err("%s:%d: unsupported chain destination: %d\n",
         __func__, __LINE__, to_cid);
  err = -EOPNOTSUPP;
 }
out:
 return err;
}

static int sparx5_tc_flower_parse_act_gate(struct sparx5_psfp_sg *sg,
        struct flow_action_entry *act,
        struct netlink_ext_ack *extack)
{
 int i;

 if (act->gate.prio < -1 || act->gate.prio > SPX5_PSFP_SG_MAX_IPV) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid gate priority");
  return -EINVAL;
 }

 if (act->gate.cycletime < SPX5_PSFP_SG_MIN_CYCLE_TIME_NS ||
     act->gate.cycletime > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletime");
  return -EINVAL;
 }

 if (act->gate.cycletimeext > SPX5_PSFP_SG_MAX_CYCLE_TIME_NS) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid gate cycletimeext");
  return -EINVAL;
 }

 if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries");
  return -EINVAL;
 }

 sg->gate_state = true;
 sg->ipv = act->gate.prio;
 sg->num_entries = act->gate.num_entries;
 sg->cycletime = act->gate.cycletime;
 sg->cycletimeext = act->gate.cycletimeext;

 for (i = 0; i < sg->num_entries; i++) {
  sg->gce[i].gate_state = !!act->gate.entries[i].gate_state;
  sg->gce[i].interval = act->gate.entries[i].interval;
  sg->gce[i].ipv = act->gate.entries[i].ipv;
  sg->gce[i].maxoctets = act->gate.entries[i].maxoctets;
 }

 return 0;
}

static int sparx5_tc_flower_parse_act_police(struct sparx5_policer *pol,
          struct flow_action_entry *act,
          struct netlink_ext_ack *extack)
{
 pol->type = SPX5_POL_SERVICE;
 pol->rate = div_u64(act->police.rate_bytes_ps, 1000) * 8;
 pol->burst = act->police.burst;
 pol->idx = act->hw_index;

 /* rate is now in kbit */
 if (pol->rate > DIV_ROUND_UP(SPX5_SDLB_GROUP_RATE_MAX, 1000)) {
  NL_SET_ERR_MSG_MOD(extack, "Maximum rate exceeded");
  return -EINVAL;
 }

 if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
  NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop");
  return -EOPNOTSUPP;
 }

 if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
     act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
  NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok");
  return -EOPNOTSUPP;
 }

 return 0;
}

static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5,
           struct vcap_rule *vrule, int sg_idx,
           int pol_idx, struct sparx5_psfp_sg *sg,
           struct sparx5_psfp_fm *fm,
           struct sparx5_psfp_sf *sf)
{
 u32 psfp_sfid = 0, psfp_fmid = 0, psfp_sgid = 0;
 int ret;

 /* Must always have a stream gate - max sdu (filter option) is evaluated
 * after frames have passed the gate, so in case of only a policer, we
 * allocate a stream gate that is always open.
 */

 if (sg_idx < 0) {
  /* Always-open stream gate is always the last */
  sg_idx = sparx5_pool_idx_to_id(sparx5->data->consts->n_gates -
            1);
  sg->ipv = 0; /* Disabled */
  sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
  sg->num_entries = 1;
  sg->gate_state = 1; /* Open */
  sg->gate_enabled = 1;
  sg->gce[0].gate_state = 1;
  sg->gce[0].interval = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
  sg->gce[0].ipv = 0;
  sg->gce[0].maxoctets = 0; /* Disabled */
 }

 ret = sparx5_psfp_sg_add(sparx5, sg_idx, sg, &psfp_sgid);
 if (ret < 0)
  return ret;

 if (pol_idx >= 0) {
  /* Add new flow-meter */
  ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid);
  if (ret < 0)
   return ret;
 }

 /* Map stream filter to stream gate */
 sf->sgid = psfp_sgid;

 /* Add new stream-filter and map it to a steam gate */
 ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid);
 if (ret < 0)
  return ret;

 /* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */
 sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid);

 ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL,
           VCAP_BIT_1);
 if (ret)
  return ret;

 ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid);
 if (ret)
  return ret;

 return 0;
}

/* Handle the action trap for a VCAP rule */
static int sparx5_tc_action_trap(struct vcap_admin *admin,
     struct vcap_rule *vrule,
     struct flow_cls_offload *fco)
{
 int err = 0;

 switch (admin->vtype) {
 case VCAP_TYPE_IS2:
  err = vcap_rule_add_action_bit(vrule,
            VCAP_AF_CPU_COPY_ENA,
            VCAP_BIT_1);
  if (err)
   break;
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_CPU_QUEUE_NUM, 0);
  if (err)
   break;
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_MASK_MODE,
            SPX5_PMM_REPLACE_ALL);
  break;
 case VCAP_TYPE_ES0:
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_FWD_SEL,
            SPX5_FWSEL_REDIRECT_TO_LOOPBACK);
  break;
 case VCAP_TYPE_ES2:
  err = vcap_rule_add_action_bit(vrule,
            VCAP_AF_CPU_COPY_ENA,
            VCAP_BIT_1);
  if (err)
   break;
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_CPU_QUEUE_NUM, 0);
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Trap action not supported in this VCAP");
  err = -EOPNOTSUPP;
  break;
 }
 return err;
}

static int sparx5_tc_action_vlan_pop(struct vcap_admin *admin,
         struct vcap_rule *vrule,
         struct flow_cls_offload *fco,
         u16 tpid)
{
 int err = 0;

 switch (admin->vtype) {
 case VCAP_TYPE_ES0:
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "VLAN pop action not supported in this VCAP");
  return -EOPNOTSUPP;
 }

 switch (tpid) {
 case ETH_P_8021Q:
 case ETH_P_8021AD:
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_PUSH_OUTER_TAG,
            SPX5_OTAG_UNTAG);
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Invalid vlan proto");
  err = -EINVAL;
 }
 return err;
}

static int sparx5_tc_action_vlan_modify(struct vcap_admin *admin,
     struct vcap_rule *vrule,
     struct flow_cls_offload *fco,
     struct flow_action_entry *act,
     u16 tpid)
{
 int err = 0;

 switch (admin->vtype) {
 case VCAP_TYPE_ES0:
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_PUSH_OUTER_TAG,
            SPX5_OTAG_TAG_A);
  if (err)
   return err;
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "VLAN modify action not supported in this VCAP");
  return -EOPNOTSUPP;
 }

 switch (tpid) {
 case ETH_P_8021Q:
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_TAG_A_TPID_SEL,
            SPX5_TPID_A_8100);
  break;
 case ETH_P_8021AD:
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_TAG_A_TPID_SEL,
            SPX5_TPID_A_88A8);
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Invalid vlan proto");
  err = -EINVAL;
 }
 if (err)
  return err;

 err = vcap_rule_add_action_u32(vrule,
           VCAP_AF_TAG_A_VID_SEL,
           SPX5_VID_A_VAL);
 if (err)
  return err;

 err = vcap_rule_add_action_u32(vrule,
           VCAP_AF_VID_A_VAL,
           act->vlan.vid);
 if (err)
  return err;

 err = vcap_rule_add_action_u32(vrule,
           VCAP_AF_TAG_A_PCP_SEL,
           SPX5_PCP_A_VAL);
 if (err)
  return err;

 err = vcap_rule_add_action_u32(vrule,
           VCAP_AF_PCP_A_VAL,
           act->vlan.prio);
 if (err)
  return err;

 return vcap_rule_add_action_u32(vrule,
     VCAP_AF_TAG_A_DEI_SEL,
     SPX5_DEI_A_CLASSIFIED);
}

static int sparx5_tc_action_vlan_push(struct vcap_admin *admin,
          struct vcap_rule *vrule,
          struct flow_cls_offload *fco,
          struct flow_action_entry *act,
          u16 tpid)
{
 u16 act_tpid = be16_to_cpu(act->vlan.proto);
 int err = 0;

 switch (admin->vtype) {
 case VCAP_TYPE_ES0:
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "VLAN push action not supported in this VCAP");
  return -EOPNOTSUPP;
 }

 if (tpid == ETH_P_8021AD) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Cannot push on double tagged frames");
  return -EOPNOTSUPP;
 }

 err = sparx5_tc_action_vlan_modify(admin, vrule, fco, act, act_tpid);
 if (err)
  return err;

 switch (act_tpid) {
 case ETH_P_8021Q:
  break;
 case ETH_P_8021AD:
  /* Push classified tag as inner tag */
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_PUSH_INNER_TAG,
            SPX5_ITAG_PUSH_B_TAG);
  if (err)
   break;
  err = vcap_rule_add_action_u32(vrule,
            VCAP_AF_TAG_B_TPID_SEL,
            SPX5_TPID_B_CLASSIFIED);
  break;
 default:
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Invalid vlan proto");
  err = -EINVAL;
 }
 return err;
}

static void sparx5_tc_flower_set_port_mask(struct vcap_u72_action *ports,
        struct net_device *ndev)
{
 struct sparx5_port *port = netdev_priv(ndev);
 int byidx = port->portno / BITS_PER_BYTE;
 int biidx = port->portno % BITS_PER_BYTE;

 ports->value[byidx] |= BIT(biidx);
}

static int sparx5_tc_action_mirred(struct vcap_admin *admin,
       struct vcap_rule *vrule,
       struct flow_cls_offload *fco,
       struct flow_action_entry *act)
{
 struct vcap_u72_action ports = {0};
 int err;

 if (admin->vtype != VCAP_TYPE_IS0 && admin->vtype != VCAP_TYPE_IS2) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Mirror action not supported in this VCAP");
  return -EOPNOTSUPP;
 }

 err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
           SPX5_PMM_OR_DSTMASK);
 if (err)
  return err;

 sparx5_tc_flower_set_port_mask(&ports, act->dev);

 return vcap_rule_add_action_u72(vrule, VCAP_AF_PORT_MASK, &ports);
}

static int sparx5_tc_action_redirect(struct vcap_admin *admin,
         struct vcap_rule *vrule,
         struct flow_cls_offload *fco,
         struct flow_action_entry *act)
{
 struct vcap_u72_action ports = {0};
 int err;

 if (admin->vtype != VCAP_TYPE_IS0 && admin->vtype != VCAP_TYPE_IS2) {
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Redirect action not supported in this VCAP");
  return -EOPNOTSUPP;
 }

 err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
           SPX5_PMM_REPLACE_ALL);
 if (err)
  return err;

 sparx5_tc_flower_set_port_mask(&ports, act->dev);

 return vcap_rule_add_action_u72(vrule, VCAP_AF_PORT_MASK, &ports);
}

/* Remove rule keys that may prevent templates from matching a keyset */
static void sparx5_tc_flower_simplify_rule(struct vcap_admin *admin,
        struct vcap_rule *vrule,
        u16 l3_proto)
{
 switch (admin->vtype) {
 case VCAP_TYPE_IS0:
  vcap_rule_rem_key(vrule, VCAP_KF_ETYPE);
  switch (l3_proto) {
  case ETH_P_IP:
   break;
  case ETH_P_IPV6:
   vcap_rule_rem_key(vrule, VCAP_KF_IP_SNAP_IS);
   break;
  default:
   break;
  }
  break;
 case VCAP_TYPE_ES2:
  switch (l3_proto) {
  case ETH_P_IP:
   if (vrule->keyset == VCAP_KFS_IP4_OTHER)
    vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);
   break;
  case ETH_P_IPV6:
   if (vrule->keyset == VCAP_KFS_IP6_STD)
    vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS);
   vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);
   break;
  default:
   break;
  }
  break;
 case VCAP_TYPE_IS2:
  switch (l3_proto) {
  case ETH_P_IP:
  case ETH_P_IPV6:
   vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS);
   break;
  default:
   break;
  }
  break;
 default:
  break;
 }
}

static bool sparx5_tc_flower_use_template(struct net_device *ndev,
       struct flow_cls_offload *fco,
       struct vcap_admin *admin,
       struct vcap_rule *vrule)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct sparx5_tc_flower_template *ftp;

 list_for_each_entry(ftp, &port->tc_templates, list) {
  if (ftp->cid != fco->common.chain_index)
   continue;

  vcap_set_rule_set_keyset(vrule, ftp->keyset);
  sparx5_tc_flower_simplify_rule(admin, vrule, ftp->l3_proto);
  return true;
 }
 return false;
}

static int sparx5_tc_flower_replace(struct net_device *ndev,
        struct flow_cls_offload *fco,
        struct vcap_admin *admin,
        bool ingress)
{
 struct sparx5_psfp_sf sf = { .max_sdu = SPX5_PSFP_SF_MAX_SDU };
 struct netlink_ext_ack *extack = fco->common.extack;
 int err, idx, tc_sg_idx = -1, tc_pol_idx = -1;
 struct vcap_tc_flower_parse_usage state = {
  .fco = fco,
  .l3_proto = ETH_P_ALL,
  .admin = admin,
 };
 struct sparx5_port *port = netdev_priv(ndev);
 struct sparx5_multiple_rules multi = {};
 struct sparx5 *sparx5 = port->sparx5;
 struct sparx5_psfp_sg sg = { 0 };
 struct sparx5_psfp_fm fm = { 0 };
 struct flow_action_entry *act;
 struct vcap_control *vctrl;
 struct flow_rule *frule;
 struct vcap_rule *vrule;

 vctrl = port->sparx5->vcap_ctrl;

 err = sparx5_tc_flower_action_check(vctrl, ndev, fco, ingress);
 if (err)
  return err;

 vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC,
    fco->common.prio, 0);
 if (IS_ERR(vrule))
  return PTR_ERR(vrule);

 vrule->cookie = fco->cookie;

 state.vrule = vrule;
 state.frule = flow_cls_offload_flow_rule(fco);
 err = sparx5_tc_use_dissectors(&state, admin, vrule);
 if (err)
  goto out;

 err = sparx5_tc_add_rule_counter(admin, vrule);
 if (err)
  goto out;

 err = sparx5_tc_add_rule_link_target(admin, vrule,
          fco->common.chain_index);
 if (err)
  goto out;

 frule = flow_cls_offload_flow_rule(fco);
 flow_action_for_each(idx, act, &frule->action) {
  switch (act->id) {
  case FLOW_ACTION_GATE: {
   err = sparx5_tc_flower_parse_act_gate(&sg, act, extack);
   if (err < 0)
    goto out;

   tc_sg_idx = act->hw_index;

   break;
  }
  case FLOW_ACTION_POLICE: {
   err = sparx5_tc_flower_parse_act_police(&fm.pol, act,
        extack);
   if (err < 0)
    goto out;

   tc_pol_idx = fm.pol.idx;
   sf.max_sdu = act->police.mtu;

   break;
  }
  case FLOW_ACTION_TRAP:
   err = sparx5_tc_action_trap(admin, vrule, fco);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_MIRRED:
   err = sparx5_tc_action_mirred(admin, vrule, fco, act);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_REDIRECT:
   err = sparx5_tc_action_redirect(admin, vrule, fco, act);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_ACCEPT:
   err = sparx5_tc_set_actionset(admin, vrule);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_GOTO:
   err = sparx5_tc_set_actionset(admin, vrule);
   if (err)
    goto out;
   sparx5_tc_add_rule_link(vctrl, admin, vrule,
      fco->common.chain_index,
      act->chain_index);
   break;
  case FLOW_ACTION_VLAN_POP:
   err = sparx5_tc_action_vlan_pop(admin, vrule, fco,
       state.tpid);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_VLAN_PUSH:
   err = sparx5_tc_action_vlan_push(admin, vrule, fco,
        act, state.tpid);
   if (err)
    goto out;
   break;
  case FLOW_ACTION_VLAN_MANGLE:
   err = sparx5_tc_action_vlan_modify(admin, vrule, fco,
          act, state.tpid);
   if (err)
    goto out;
   break;
  default:
   NL_SET_ERR_MSG_MOD(fco->common.extack,
        "Unsupported TC action");
   err = -EOPNOTSUPP;
   goto out;
  }
 }

 /* Setup PSFP */
 if (tc_sg_idx >= 0 || tc_pol_idx >= 0) {
  if (!sparx5_has_feature(sparx5, SPX5_FEATURE_PSFP)) {
   err = -EOPNOTSUPP;
   goto out;
  }

  err = sparx5_tc_flower_psfp_setup(sparx5, vrule, tc_sg_idx,
        tc_pol_idx, &sg, &fm, &sf);
  if (err)
   goto out;
 }

 if (!sparx5_tc_flower_use_template(ndev, fco, admin, vrule)) {
  err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin,
             state.l3_proto, &multi);
  if (err) {
   NL_SET_ERR_MSG_MOD(fco->common.extack,
        "No matching port keyset for filter protocol and keys");
   goto out;
  }
 }

 /* provide the l3 protocol to guide the keyset selection */
 err = vcap_val_rule(vrule, state.l3_proto);
 if (err) {
  vcap_set_tc_exterr(fco, vrule);
  goto out;
 }
 err = vcap_add_rule(vrule);
 if (err)
  NL_SET_ERR_MSG_MOD(fco->common.extack,
       "Could not add the filter");

 if (state.l3_proto == ETH_P_ALL)
  err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin,
          &multi);

out:
 vcap_free_rule(vrule);
 return err;
}

static void sparx5_tc_free_psfp_resources(struct sparx5 *sparx5,
       struct vcap_rule *vrule)
{
 struct vcap_client_actionfield *afield;
 u32 isdx, sfid, sgid, fmid;

 /* Check if VCAP_AF_ISDX_VAL action is set for this rule - and if
 * it is used for stream and/or flow-meter classification.
 */

 afield = vcap_find_actionfield(vrule, VCAP_AF_ISDX_VAL);
 if (!afield)
  return;

 isdx = afield->data.u32.value;
 sfid = sparx5_psfp_isdx_get_sf(sparx5, isdx);

 if (!sfid)
  return;

 fmid = sparx5_psfp_isdx_get_fm(sparx5, isdx);
 sgid = sparx5_psfp_sf_get_sg(sparx5, sfid);

 if (fmid && sparx5_psfp_fm_del(sparx5, fmid) < 0)
  pr_err("%s:%d Could not delete invalid fmid: %d", __func__,
         __LINE__, fmid);

 if (sgid && sparx5_psfp_sg_del(sparx5, sgid) < 0)
  pr_err("%s:%d Could not delete invalid sgid: %d", __func__,
         __LINE__, sgid);

 if (sparx5_psfp_sf_del(sparx5, sfid) < 0)
  pr_err("%s:%d Could not delete invalid sfid: %d", __func__,
         __LINE__, sfid);

 sparx5_isdx_conf_set(sparx5, isdx, 0, 0);
}

static int sparx5_tc_free_rule_resources(struct net_device *ndev,
      struct vcap_control *vctrl,
      int rule_id)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct sparx5 *sparx5 = port->sparx5;
 struct vcap_rule *vrule;
 int ret = 0;

 vrule = vcap_get_rule(vctrl, rule_id);
 if (IS_ERR(vrule))
  return -EINVAL;

 sparx5_tc_free_psfp_resources(sparx5, vrule);

 vcap_free_rule(vrule);
 return ret;
}

static int sparx5_tc_flower_destroy(struct net_device *ndev,
        struct flow_cls_offload *fco,
        struct vcap_admin *admin)
{
 struct sparx5_port *port = netdev_priv(ndev);
 int err = -ENOENT, count = 0, rule_id;
 struct vcap_control *vctrl;

 vctrl = port->sparx5->vcap_ctrl;
 while (true) {
  rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie);
  if (rule_id <= 0)
   break;
  if (count == 0) {
   /* Resources are attached to the first rule of
 * a set of rules. Only works if the rules are
 * in the correct order.
 */

   err = sparx5_tc_free_rule_resources(ndev, vctrl,
           rule_id);
   if (err)
    pr_err("%s:%d: could not free resources %d\n",
           __func__, __LINE__, rule_id);
  }
  err = vcap_del_rule(vctrl, ndev, rule_id);
  if (err) {
   pr_err("%s:%d: could not delete rule %d\n",
          __func__, __LINE__, rule_id);
   break;
  }
 }
 return err;
}

static int sparx5_tc_flower_stats(struct net_device *ndev,
      struct flow_cls_offload *fco,
      struct vcap_admin *admin)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct vcap_counter ctr = {};
 struct vcap_control *vctrl;
 ulong lastused = 0;
 int err;

 vctrl = port->sparx5->vcap_ctrl;
 err = vcap_get_rule_count_by_cookie(vctrl, &ctr, fco->cookie);
 if (err)
  return err;
 flow_stats_update(&fco->stats, 0x0, ctr.value, 0, lastused,
     FLOW_ACTION_HW_STATS_IMMEDIATE);
 return err;
}

static int sparx5_tc_flower_template_create(struct net_device *ndev,
         struct flow_cls_offload *fco,
         struct vcap_admin *admin)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct vcap_tc_flower_parse_usage state = {
  .fco = fco,
  .l3_proto = ETH_P_ALL,
  .admin = admin,
 };
 struct sparx5_tc_flower_template *ftp;
 struct vcap_keyset_list kslist = {};
 enum vcap_keyfield_set keysets[10];
 struct vcap_control *vctrl;
 struct vcap_rule *vrule;
 int count, err;

 if (admin->vtype == VCAP_TYPE_ES0) {
  pr_err("%s:%d: %s\n", __func__, __LINE__,
         "VCAP does not support templates");
  return -EINVAL;
 }

 count = vcap_admin_rule_count(admin, fco->common.chain_index);
 if (count > 0) {
  pr_err("%s:%d: %s\n", __func__, __LINE__,
         "Filters are already present");
  return -EBUSY;
 }

 ftp = kzalloc(sizeof(*ftp), GFP_KERNEL);
 if (!ftp)
  return -ENOMEM;

 ftp->cid = fco->common.chain_index;
 ftp->orig = VCAP_KFS_NO_VALUE;
 ftp->keyset = VCAP_KFS_NO_VALUE;

 vctrl = port->sparx5->vcap_ctrl;
 vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index,
    VCAP_USER_TC, fco->common.prio, 0);
 if (IS_ERR(vrule)) {
  err = PTR_ERR(vrule);
  goto err_rule;
 }

 state.vrule = vrule;
 state.frule = flow_cls_offload_flow_rule(fco);
 err = sparx5_tc_use_dissectors(&state, admin, vrule);
 if (err) {
  pr_err("%s:%d: key error: %d\n", __func__, __LINE__, err);
  goto out;
 }

 ftp->l3_proto = state.l3_proto;

 sparx5_tc_flower_simplify_rule(admin, vrule, state.l3_proto);

 /* Find the keysets that the rule can use */
 kslist.keysets = keysets;
 kslist.max = ARRAY_SIZE(keysets);
 if (!vcap_rule_find_keysets(vrule, &kslist)) {
  pr_err("%s:%d: %s\n", __func__, __LINE__,
         "Could not find a suitable keyset");
  err = -ENOENT;
  goto out;
 }

 ftp->keyset = vcap_select_min_rule_keyset(vctrl, admin->vtype, &kslist);
 kslist.cnt = 0;
 sparx5_vcap_set_port_keyset(ndev, admin, fco->common.chain_index,
        state.l3_proto,
        ftp->keyset,
        &kslist);

 if (kslist.cnt > 0)
  ftp->orig = kslist.keysets[0];

 /* Store new template */
 list_add_tail(&ftp->list, &port->tc_templates);
 vcap_free_rule(vrule);
 return 0;

out:
 vcap_free_rule(vrule);
err_rule:
 kfree(ftp);
 return err;
}

static int sparx5_tc_flower_template_destroy(struct net_device *ndev,
          struct flow_cls_offload *fco,
          struct vcap_admin *admin)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct sparx5_tc_flower_template *ftp, *tmp;
 int err = -ENOENT;

 /* Rules using the template are removed by the tc framework */
 list_for_each_entry_safe(ftp, tmp, &port->tc_templates, list) {
  if (ftp->cid != fco->common.chain_index)
   continue;

  sparx5_vcap_set_port_keyset(ndev, admin,
         fco->common.chain_index,
         ftp->l3_proto, ftp->orig,
         NULL);
  list_del(&ftp->list);
  kfree(ftp);
  break;
 }
 return err;
}

int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
       bool ingress)
{
 struct sparx5_port *port = netdev_priv(ndev);
 struct vcap_control *vctrl;
 struct vcap_admin *admin;
 int err = -EINVAL;

 /* Get vcap instance from the chain id */
 vctrl = port->sparx5->vcap_ctrl;
 admin = vcap_find_admin(vctrl, fco->common.chain_index);
 if (!admin) {
  NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain");
  return err;
 }

 switch (fco->command) {
 case FLOW_CLS_REPLACE:
  return sparx5_tc_flower_replace(ndev, fco, admin, ingress);
 case FLOW_CLS_DESTROY:
  return sparx5_tc_flower_destroy(ndev, fco, admin);
 case FLOW_CLS_STATS:
  return sparx5_tc_flower_stats(ndev, fco, admin);
 case FLOW_CLS_TMPLT_CREATE:
  return sparx5_tc_flower_template_create(ndev, fco, admin);
 case FLOW_CLS_TMPLT_DESTROY:
  return sparx5_tc_flower_template_destroy(ndev, fco, admin);
 default:
  return -EOPNOTSUPP;
 }
}

Messung V0.5
C=91 H=89 G=89

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.