net: microchip: sparx5: Adding TC goto action and action checking
authorSteen Hegelund <steen.hegelund@microchip.com>
Wed, 9 Nov 2022 11:41:12 +0000 (12:41 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Nov 2022 10:39:12 +0000 (10:39 +0000)
Add support for a goto action and ensure that a HW offloaded TC flower
filter has a valid goto action and that pass and trap actions are not both
used in the same filter.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
drivers/net/ethernet/microchip/vcap/vcap_api.c
drivers/net/ethernet/microchip/vcap/vcap_api_client.h

index 13bc6bf..6cd29d3 100644 (file)
@@ -464,6 +464,60 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
        return err;
 }
 
+static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
+                                        struct flow_cls_offload *fco,
+                                        struct vcap_admin *admin)
+{
+       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 that last action is a goto */
+       if (last_actent->id != FLOW_ACTION_GOTO) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Last action must be 'goto'");
+               return -EINVAL;
+       }
+
+       /* Check if the goto chain is in the next lookup */
+       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;
+       }
+
+       /* 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;
+       }
+
+       return 0;
+}
+
 static int sparx5_tc_flower_replace(struct net_device *ndev,
                                    struct flow_cls_offload *fco,
                                    struct vcap_admin *admin)
@@ -475,16 +529,12 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
        struct vcap_rule *vrule;
        int err, idx;
 
-       frule = flow_cls_offload_flow_rule(fco);
-       if (!flow_action_has_entries(&frule->action)) {
-               NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
-               return -EINVAL;
-       }
+       vctrl = port->sparx5->vcap_ctrl;
 
-       if (!flow_action_basic_hw_stats_check(&frule->action, fco->common.extack))
-               return -EOPNOTSUPP;
+       err = sparx5_tc_flower_action_check(vctrl, fco, admin);
+       if (err)
+               return err;
 
-       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))
@@ -492,6 +542,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
 
        vrule->cookie = fco->cookie;
        sparx5_tc_use_dissectors(fco, admin, vrule);
+       frule = flow_cls_offload_flow_rule(fco);
        flow_action_for_each(idx, act, &frule->action) {
                switch (act->id) {
                case FLOW_ACTION_TRAP:
@@ -521,6 +572,9 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
                        if (err)
                                goto out;
                        break;
+               case FLOW_ACTION_GOTO:
+                       /* Links between VCAPs will be added later */
+                       break;
                default:
                        NL_SET_ERR_MSG_MOD(fco->common.extack,
                                           "Unsupported TC action");
index d5b62e4..0dd9637 100644 (file)
@@ -677,6 +677,42 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
 }
 EXPORT_SYMBOL_GPL(vcap_find_admin);
 
+/* 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)
+{
+       struct vcap_admin *admin, *next_admin;
+       int lookup, next_lookup;
+
+       /* The offset must be at least one lookup */
+       if (next_cid < cur_cid + VCAP_CID_LOOKUP_SIZE)
+               return false;
+
+       if (vcap_api_check(vctrl))
+               return false;
+
+       admin = vcap_find_admin(vctrl, cur_cid);
+       if (!admin)
+               return false;
+
+       /* If no VCAP contains the next chain, the next chain must be beyond
+        * the last chain in the current VCAP
+        */
+       next_admin = vcap_find_admin(vctrl, next_cid);
+       if (!next_admin)
+               return next_cid > admin->last_cid;
+
+       lookup = vcap_chain_id_to_lookup(admin, cur_cid);
+       next_lookup = vcap_chain_id_to_lookup(next_admin, next_cid);
+
+       /* Next lookup must be the following lookup */
+       if (admin == next_admin || admin->vtype == next_admin->vtype)
+               return next_lookup == lookup + 1;
+
+       /* Must be the first lookup in the next VCAP instance */
+       return next_lookup == 0;
+}
+EXPORT_SYMBOL_GPL(vcap_is_next_lookup);
+
 /* Check if there is room for a new rule */
 static int vcap_rule_space(struct vcap_admin *admin, int size)
 {
index 7d9a227..5cecb12 100644 (file)
@@ -193,6 +193,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);
+/* 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);
 
 /* Copy to host byte order */
 void vcap_netbytes_copy(u8 *dst, u8 *src, int count);