@@ -850,6 +850,84 @@ static int sparx5_tc_set_actionset(struct vcap_admin *admin,
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);
+ 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;
+
+ diff = vcap_chain_offset(vctrl, from_cid, to_cid);
+ if (!(to_admin && diff > 0)) {
+ pr_err("%s:%d: unsupported chain direction: %d\n",
+ __func__, __LINE__, to_cid);
+ return -EINVAL;
+ }
+ 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 {
+ pr_err("%s:%d: unsupported chain destination: %d\n",
+ __func__, __LINE__, to_cid);
+ err = -EOPNOTSUPP;
+ }
+out:
+ return err;
+}
+
static int sparx5_tc_flower_replace(struct net_device *ndev,
struct flow_cls_offload *fco,
struct vcap_admin *admin)
@@ -885,10 +963,21 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
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_TRAP:
+ if (admin->vtype != VCAP_TYPE_IS2) {
+ NL_SET_ERR_MSG_MOD(fco->common.extack,
+ "Trap action not supported in this VCAP");
+ err = -EOPNOTSUPP;
+ goto out;
+ }
err = vcap_rule_add_action_bit(vrule,
VCAP_AF_CPU_COPY_ENA,
VCAP_BIT_1);
@@ -917,7 +1006,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
err = sparx5_tc_set_actionset(admin, vrule);
if (err)
goto out;
- /* Links between VCAPs will be added later */
+ sparx5_tc_add_rule_link(vctrl, admin, vrule,
+ fco->common.chain_index,
+ act->chain_index);
break;
default:
NL_SET_ERR_MSG_MOD(fco->common.extack,
@@ -1603,6 +1603,40 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
}
EXPORT_SYMBOL_GPL(vcap_find_admin);
+/* Is this the last admin instance ordered by chain id */
+static bool vcap_admin_is_last(struct vcap_control *vctrl,
+ struct vcap_admin *admin)
+{
+ struct vcap_admin *iter, *last = NULL;
+ int max_cid = 0;
+
+ list_for_each_entry(iter, &vctrl->list, list) {
+ if (iter->first_cid > max_cid) {
+ last = iter;
+ max_cid = iter->first_cid;
+ }
+ }
+ if (!last)
+ return false;
+
+ return admin == last;
+}
+
+/* Calculate the value used for chaining VCAP rules */
+int vcap_chain_offset(struct vcap_control *vctrl, int from_cid, int to_cid)
+{
+ int diff = to_cid - from_cid;
+
+ if (diff < 0) /* Wrong direction */
+ return diff;
+ to_cid %= VCAP_CID_LOOKUP_SIZE;
+ if (to_cid == 0) /* Destination aligned to a lookup == no chaining */
+ return 0;
+ diff %= VCAP_CID_LOOKUP_SIZE; /* Limit to a value within a lookup */
+ return diff;
+}
+EXPORT_SYMBOL_GPL(vcap_chain_offset);
+
/* Is the next chain id in one of the following lookups
* For now this does not support filters linked to other filters using
* keys and actions. That will be added later.
@@ -2826,6 +2860,7 @@ static int vcap_enable_rule(struct vcap_rule_internal *ri)
static int vcap_enable_rules(struct vcap_control *vctrl,
struct net_device *ndev, int chain)
{
+ int next_chain = chain + VCAP_CID_LOOKUP_SIZE;
struct vcap_rule_internal *ri;
struct vcap_admin *admin;
int err = 0;
@@ -2837,8 +2872,11 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
/* Found the admin, now find the offloadable rules */
mutex_lock(&admin->lock);
list_for_each_entry(ri, &admin->rules, list) {
- if (ri->data.vcap_chain_id != chain)
+ /* Is the rule in the lookup defined by the chain */
+ if (!(ri->data.vcap_chain_id >= chain &&
+ ri->data.vcap_chain_id < next_chain)) {
continue;
+ }
if (ri->ndev != ndev)
continue;
@@ -3055,6 +3093,9 @@ bool vcap_is_last_chain(struct vcap_control *vctrl, int cid)
if (!admin)
return false;
+ if (!vcap_admin_is_last(vctrl, admin))
+ return false;
+
/* This must be the last lookup in this VCAP type */
lookup = vcap_chain_id_to_lookup(admin, cid);
return lookup == admin->lookups - 1;
@@ -217,6 +217,8 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
enum vcap_key_field key);
/* Find a rule id with a provided cookie */
int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie);
+/* Calculate the value used for chaining VCAP rules */
+int vcap_chain_offset(struct vcap_control *vctrl, int from_cid, int to_cid);
/* Is the next chain id in the following lookup, possible in another VCAP */
bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid);
/* Is this chain id the last lookup of all VCAPs */