net: microchip: vcap api: Add a storage state to a VCAP rule
authorSteen Hegelund <steen.hegelund@microchip.com>
Sat, 14 Jan 2023 13:42:41 +0000 (14:42 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Jan 2023 13:45:17 +0000 (13:45 +0000)
This allows a VCAP rule to be in one of 3 states:

- permanently stored in the VCAP HW (for rules that must always be present)
- enabled (stored in HW) when the corresponding lookup has been enabled
- disabled (stored in SW) when the lookup is disabled

This way important VCAP rules can be added even before the user enables the
VCAP lookups using a TC matchall filter.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/vcap/vcap_api.c
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c
drivers/net/ethernet/microchip/vcap/vcap_api_private.h

index 2cc6e94..9226968 100644 (file)
@@ -950,9 +950,12 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie)
 }
 EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie);
 
-/* Make a shallow copy of the rule without the fields */
-struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri)
+/* Make a copy of the rule, shallow or full */
+static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri,
+                                               bool full)
 {
+       struct vcap_client_actionfield *caf, *newcaf;
+       struct vcap_client_keyfield *ckf, *newckf;
        struct vcap_rule_internal *duprule;
 
        /* Allocate the client part */
@@ -965,6 +968,27 @@ struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri)
        /* No elements in these lists */
        INIT_LIST_HEAD(&duprule->data.keyfields);
        INIT_LIST_HEAD(&duprule->data.actionfields);
+
+       /* A full rule copy includes keys and actions */
+       if (!full)
+               return duprule;
+
+       list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) {
+               newckf = kzalloc(sizeof(*newckf), GFP_KERNEL);
+               if (!newckf)
+                       return ERR_PTR(-ENOMEM);
+               memcpy(newckf, ckf, sizeof(*newckf));
+               list_add_tail(&newckf->ctrl.list, &duprule->data.keyfields);
+       }
+
+       list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) {
+               newcaf = kzalloc(sizeof(*newcaf), GFP_KERNEL);
+               if (!newcaf)
+                       return ERR_PTR(-ENOMEM);
+               memcpy(newcaf, caf, sizeof(*newcaf));
+               list_add_tail(&newcaf->ctrl.list, &duprule->data.actionfields);
+       }
+
        return duprule;
 }
 
@@ -1877,8 +1901,8 @@ static int vcap_insert_rule(struct vcap_rule_internal *ri,
                ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri);
                admin->last_used_addr = ri->addr;
 
-               /* Add a shallow copy of the rule to the VCAP list */
-               duprule = vcap_dup_rule(ri);
+               /* Add a copy of the rule to the VCAP list */
+               duprule = vcap_dup_rule(ri, ri->state == VCAP_RS_DISABLED);
                if (IS_ERR(duprule))
                        return PTR_ERR(duprule);
 
@@ -1891,8 +1915,8 @@ static int vcap_insert_rule(struct vcap_rule_internal *ri,
        ri->addr = vcap_next_rule_addr(addr, ri);
        addr = ri->addr;
 
-       /* Add a shallow copy of the rule to the VCAP list */
-       duprule = vcap_dup_rule(ri);
+       /* Add a copy of the rule to the VCAP list */
+       duprule = vcap_dup_rule(ri, ri->state == VCAP_RS_DISABLED);
        if (IS_ERR(duprule))
                return PTR_ERR(duprule);
 
@@ -1939,6 +1963,72 @@ static bool vcap_is_chain_used(struct vcap_control *vctrl,
        return false;
 }
 
+/* Fetch the next chain in the enabled list for the port */
+static int vcap_get_next_chain(struct vcap_control *vctrl,
+                              struct net_device *ndev,
+                              int dst_cid)
+{
+       struct vcap_enabled_port *eport;
+       struct vcap_admin *admin;
+
+       list_for_each_entry(admin, &vctrl->list, list) {
+               list_for_each_entry(eport, &admin->enabled, list) {
+                       if (eport->ndev != ndev)
+                               continue;
+                       if (eport->src_cid == dst_cid)
+                               return eport->dst_cid;
+               }
+       }
+
+       return 0;
+}
+
+static bool vcap_path_exist(struct vcap_control *vctrl, struct net_device *ndev,
+                           int dst_cid)
+{
+       struct vcap_enabled_port *eport, *elem;
+       struct vcap_admin *admin;
+       int tmp;
+
+       /* Find first entry that starts from chain 0*/
+       list_for_each_entry(admin, &vctrl->list, list) {
+               list_for_each_entry(elem, &admin->enabled, list) {
+                       if (elem->src_cid == 0 && elem->ndev == ndev) {
+                               eport = elem;
+                               break;
+                       }
+               }
+               if (eport)
+                       break;
+       }
+
+       if (!eport)
+               return false;
+
+       tmp = eport->dst_cid;
+       while (tmp != dst_cid && tmp != 0)
+               tmp = vcap_get_next_chain(vctrl, ndev, tmp);
+
+       return !!tmp;
+}
+
+/* Internal clients can always store their rules in HW
+ * External clients can store their rules if the chain is enabled all
+ * the way from chain 0, otherwise the rule will be cached until
+ * the chain is enabled.
+ */
+static void vcap_rule_set_state(struct vcap_rule_internal *ri)
+{
+       if (ri->data.user <= VCAP_USER_QOS)
+               ri->state = VCAP_RS_PERMANENT;
+       else if (vcap_path_exist(ri->vctrl, ri->ndev, ri->data.vcap_chain_id))
+               ri->state = VCAP_RS_ENABLED;
+       else
+               ri->state = VCAP_RS_DISABLED;
+       /* For now always store directly in HW */
+       ri->state = VCAP_RS_PERMANENT;
+}
+
 /* Encode and write a validated rule to the VCAP */
 int vcap_add_rule(struct vcap_rule *rule)
 {
@@ -1952,6 +2042,8 @@ int vcap_add_rule(struct vcap_rule *rule)
                return ret;
        /* Insert the new rule in the list of vcap rules */
        mutex_lock(&ri->admin->lock);
+
+       vcap_rule_set_state(ri);
        ret = vcap_insert_rule(ri, &move);
        if (ret < 0) {
                pr_err("%s:%d: could not insert rule in vcap list: %d\n",
@@ -1960,6 +2052,13 @@ int vcap_add_rule(struct vcap_rule *rule)
        }
        if (move.count > 0)
                vcap_move_rules(ri, &move);
+
+       if (ri->state == VCAP_RS_DISABLED) {
+               /* Erase the rule area */
+               ri->vctrl->ops->init(ri->ndev, ri->admin, ri->addr, ri->size);
+               goto out;
+       }
+
        vcap_erase_cache(ri);
        ret = vcap_encode_rule(ri);
        if (ret) {
@@ -2071,9 +2170,13 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id)
        if (!elem)
                return NULL;
        mutex_lock(&elem->admin->lock);
-       ri = vcap_dup_rule(elem);
+       ri = vcap_dup_rule(elem, elem->state == VCAP_RS_DISABLED);
        if (IS_ERR(ri))
                goto unlock;
+
+       if (ri->state == VCAP_RS_DISABLED)
+               goto unlock;
+
        err = vcap_read_rule(ri);
        if (err) {
                ri = ERR_PTR(err);
@@ -2111,6 +2214,11 @@ int vcap_mod_rule(struct vcap_rule *rule)
                return -ENOENT;
 
        mutex_lock(&ri->admin->lock);
+
+       vcap_rule_set_state(ri);
+       if (ri->state == VCAP_RS_DISABLED)
+               goto out;
+
        /* Encode the bitstreams to the VCAP cache */
        vcap_erase_cache(ri);
        err = vcap_encode_rule(ri);
@@ -2203,7 +2311,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
        mutex_lock(&admin->lock);
        list_del(&ri->list);
        vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap);
-       kfree(ri);
+       vcap_free_rule(&ri->data);
        mutex_unlock(&admin->lock);
 
        /* Update the last used address, set to default when no rules */
@@ -2232,7 +2340,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
        list_for_each_entry_safe(ri, next_ri, &admin->rules, list) {
                vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size);
                list_del(&ri->list);
-               kfree(ri);
+               vcap_free_rule(&ri->data);
        }
        admin->last_used_addr = admin->last_valid_addr;
 
index e0b2062..d6a09ce 100644 (file)
@@ -152,37 +152,45 @@ vcap_debugfs_show_rule_actionfield(struct vcap_control *vctrl,
        out->prf(out->dst, "\n");
 }
 
-static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri,
-                                        struct vcap_output_print *out)
+static int vcap_debugfs_show_keysets(struct vcap_rule_internal *ri,
+                                    struct vcap_output_print *out)
 {
-       struct vcap_control *vctrl = ri->vctrl;
        struct vcap_admin *admin = ri->admin;
        enum vcap_keyfield_set keysets[10];
-       const struct vcap_field *keyfield;
-       enum vcap_type vt = admin->vtype;
-       struct vcap_client_keyfield *ckf;
        struct vcap_keyset_list matches;
-       u32 *maskstream;
-       u32 *keystream;
-       int res;
+       int err;
 
-       keystream = admin->cache.keystream;
-       maskstream = admin->cache.maskstream;
        matches.keysets = keysets;
        matches.cnt = 0;
        matches.max = ARRAY_SIZE(keysets);
-       res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream,
+
+       err = vcap_find_keystream_keysets(ri->vctrl, admin->vtype,
+                                         admin->cache.keystream,
+                                         admin->cache.maskstream,
                                          false, 0, &matches);
-       if (res < 0) {
+       if (err) {
                pr_err("%s:%d: could not find valid keysets: %d\n",
-                      __func__, __LINE__, res);
-               return -EINVAL;
+                      __func__, __LINE__, err);
+               return err;
        }
+
        out->prf(out->dst, "  keysets:");
        for (int idx = 0; idx < matches.cnt; ++idx)
                out->prf(out->dst, " %s",
-                        vcap_keyset_name(vctrl, matches.keysets[idx]));
+                        vcap_keyset_name(ri->vctrl, matches.keysets[idx]));
        out->prf(out->dst, "\n");
+       return 0;
+}
+
+static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri,
+                                        struct vcap_output_print *out)
+{
+       struct vcap_control *vctrl = ri->vctrl;
+       struct vcap_admin *admin = ri->admin;
+       const struct vcap_field *keyfield;
+       struct vcap_client_keyfield *ckf;
+
+       vcap_debugfs_show_keysets(ri, out);
        out->prf(out->dst, "  keyset_sw: %d\n", ri->keyset_sw);
        out->prf(out->dst, "  keyset_sw_regs: %d\n", ri->keyset_sw_regs);
 
@@ -233,6 +241,18 @@ static void vcap_show_admin_rule(struct vcap_control *vctrl,
        out->prf(out->dst, "  chain_id: %d\n", ri->data.vcap_chain_id);
        out->prf(out->dst, "  user: %d\n", ri->data.user);
        out->prf(out->dst, "  priority: %d\n", ri->data.priority);
+       out->prf(out->dst, "  state: ");
+       switch (ri->state) {
+       case VCAP_RS_PERMANENT:
+               out->prf(out->dst, "permanent\n");
+               break;
+       case VCAP_RS_DISABLED:
+               out->prf(out->dst, "disabled\n");
+               break;
+       case VCAP_RS_ENABLED:
+               out->prf(out->dst, "enabled\n");
+               break;
+       }
        vcap_debugfs_show_rule_keyset(ri, out);
        vcap_debugfs_show_rule_actionset(ri, out);
 }
index bef0b28..9211cb4 100644 (file)
@@ -445,6 +445,7 @@ static const char * const test_admin_expect[] = {
        "  chain_id: 0\n",
        "  user: 0\n",
        "  priority: 0\n",
+       "  state: permanent\n",
        "  keysets: VCAP_KFS_MAC_ETYPE\n",
        "  keyset_sw: 6\n",
        "  keyset_sw_regs: 2\n",
index 4fd21da..ce35af9 100644 (file)
 
 #define to_intrule(rule) container_of((rule), struct vcap_rule_internal, data)
 
+enum vcap_rule_state {
+       VCAP_RS_PERMANENT, /* the rule is always stored in HW */
+       VCAP_RS_ENABLED, /* enabled in HW but can be disabled */
+       VCAP_RS_DISABLED, /* disabled (stored in SW) and can be enabled */
+};
+
 /* Private VCAP API rule data */
 struct vcap_rule_internal {
        struct vcap_rule data; /* provided by the client */
@@ -29,6 +35,7 @@ struct vcap_rule_internal {
        u32 addr; /* address in the VCAP at insertion */
        u32 counter_id; /* counter id (if a dedicated counter is available) */
        struct vcap_counter counter; /* last read counter value */
+       enum vcap_rule_state state;  /* rule storage state */
 };
 
 /* Bit iterator for the VCAP cache streams */
@@ -43,8 +50,6 @@ struct vcap_stream_iter {
 
 /* Check that the control has a valid set of callbacks */
 int vcap_api_check(struct vcap_control *ctrl);
-/* Make a shallow copy of the rule without the fields */
-struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri);
 /* Erase the VCAP cache area used or encoding and decoding */
 void vcap_erase_cache(struct vcap_rule_internal *ri);