2022-10-19 12:52:26

by Steen Hegelund

[permalink] [raw]
Subject: [PATCH net-next v2 0/9] Add support for Sparx5 IS2 VCAP

This provides initial support for the Sparx5 VCAP functionality via the 'tc'
traffic control userspace tool and its flower filter.

Version History:
================
v2 Made the KUNIT test model a superset of the real model to fix a
kernel robot build error.

v1 Initial version

Overview:
=========

The supported flower filter keys and actions are:

- source and destination MAC address keys
- trap action
- pass action

The supported Sparx5 VCAPs are: IS2 (see below for more info)

The VCAP (Versatile Content-Aware Processor) feature is essentially a TCAM with
rules consisting of:

- Programmable key fields
- Programmable action fields
- A counter (which may be only one bit wide)

Besides this each VCAP has:

- A number of independent lookups
- A keyset configuration typically per port per lookup

VCAPs are used in many of the TSN features such as PSFP, PTP, FRER as well as
the general shaping, policing and access control, so it is an important building
block for these advanced features.

Functionality:
==============

When a frame is passed to a VCAP the VCAP will generate a set of keys (keyset)
based on the traffic type. If there is a rule created with this keyset in the
VCAP and the values of the keys matches the values in the keyset of the frame,
the rule is said to match and the actions in the rule will be executed and the
rule counter will be incremented. No more rules will be examined in this VCAP
lookup.

If there is no match in the current lookup the frame will be matched against the
next lookup (some VCAPs do the processing of the lookups in parallel).

The Sparx5 SoC has 6 different VCAP types:

- IS0: Ingress Stage 0 (AKA CLM) mostly handles classification
- IS2: Ingress Stage 2 mostly handles access control
- IP6PFX: IPv6 prefix: Provides tables for IPV6 address management
- LPM: Longest Path Match for IP guarding and routing
- ES0: Egress Stage 0 is mostly used for CPU copying and multicast handling
- ES2: Egress Stage 2 is known as the rewriter and mostly updates tags


Design:
=======

The VCAP implementation provides switchcore independent handling of rules
and supports:

- Creating and deleting rules
- Updating and getting rules

The platform specific API implementation as well as the platform specific model
of the VCAP instances are attached to the VCAP API and a client can then
access rules via the API in a platform independent way, with the
limitations that each VCAP has in terms of is supported keys and actions.

The VCAP model is generated from information delivered by the designers if the
VCAP hardware.

Here is an illustration of this:

+------------------+ +------------------+
| TC flower filter | | PTP client |
| for Sparx5 | | for Sparx5 |
+-------------\----+ +---------/--------+
\ /
\ /
\ /
\ /
\ /
+----v--------v----+
| VCAP API |
+---------|--------+
|
|
|
|
+---------v--------+
| VCAP control |
| instance |
+----/--------|----+
/ |
/ |
/ |
/ |
+--------------v---+ +----v-------------+
| Sparx5 VCAP | | Sparx5 VCAP API |
| model | | Implementation |
+------------------+ +---------|--------+
|
|
|
|
+---------v--------+
| Sparx5 VCAP HW |
+------------------+

Delivery:
=========

For now only the IS2 is supported but later the IS0, ES0 and ES2 will be added.
There are currently no plans to support the IP6PFX and the LPM VCAPs.

The IS2 VCAP has 4 lookups and they are accessible with a TC chain id:

- chain 8000000: IS2 Lookup 0
- chain 8100000: IS2 Lookup 1
- chain 8200000: IS2 Lookup 2
- chain 8300000: IS2 Lookup 3

These lookups are executed in parallel by the IS2 VCAP but the actions are
executed in series (the datasheet explains what happens if actions overlap).

The functionality of TC flower as well as TC matchall filters will be expanded
in later submissions as well as the number of VCAPs supported.

This is current plan:

- add support for more TC flower filter keys and extend the Sparx5 port keyset
configuration
- support for TC protocol all
- debugfs support for inspecting rules
- TC flower filter statistics
- Sparx5 IS0 VCAP support and more TC keys and actions to support this
- add TC policer and drop action support (depends on the Sparx5 QoS support
upstreamed separately)
- Sparx5 ES0 VCAP support and more TC actions to support this
- TC flower template support
- TC matchall filter support for mirroring and policing ports
- TC flower filter mirror action support
- Sparx5 ES2 VCAP support


The LAN966x switchcore will also be updated to use the VCAP API as well as
future Microchip switches.
The LAN966x has 3 VCAPS (IS1, IS2 and ES0) and a slightly different keyset and
actionset portfolio than Sparx5.

Steen Hegelund (9):
net: microchip: sparx5: Adding initial VCAP API support
net: microchip: sparx5: Adding IS2 VCAP model to VCAP API
net: microchip: sparx5: Adding IS2 VCAP register interface
net: microchip: sparx5: Adding initial tc flower support for VCAP API
net: microchip: sparx5: Adding port keyset config and callback
interface
net: microchip: sparx5: Adding basic rule management in VCAP API
net: microchip: sparx5: Writing rules to the IS2 VCAP
net: microchip: sparx5: Adding KUNIT test VCAP model
net: microchip: sparx5: Adding KUNIT test for the VCAP API

MAINTAINERS | 1 +
drivers/net/ethernet/microchip/Kconfig | 1 +
drivers/net/ethernet/microchip/Makefile | 1 +
drivers/net/ethernet/microchip/sparx5/Kconfig | 1 +
.../net/ethernet/microchip/sparx5/Makefile | 8 +-
.../ethernet/microchip/sparx5/sparx5_main.c | 9 +
.../ethernet/microchip/sparx5/sparx5_main.h | 6 +
.../microchip/sparx5/sparx5_main_regs.h | 460 +-
.../net/ethernet/microchip/sparx5/sparx5_tc.c | 46 +
.../net/ethernet/microchip/sparx5/sparx5_tc.h | 14 +
.../microchip/sparx5/sparx5_tc_flower.c | 256 +
.../microchip/sparx5/sparx5_vcap_ag_api.c | 1351 ++++
.../microchip/sparx5/sparx5_vcap_ag_api.h | 18 +
.../microchip/sparx5/sparx5_vcap_impl.c | 527 ++
.../microchip/sparx5/sparx5_vcap_impl.h | 20 +
drivers/net/ethernet/microchip/vcap/Kconfig | 52 +
drivers/net/ethernet/microchip/vcap/Makefile | 9 +
.../net/ethernet/microchip/vcap/vcap_ag_api.h | 326 +
.../microchip/vcap/vcap_ag_api_kunit.h | 643 ++
.../net/ethernet/microchip/vcap/vcap_api.c | 1142 ++++
.../net/ethernet/microchip/vcap/vcap_api.h | 272 +
.../ethernet/microchip/vcap/vcap_api_client.h | 195 +
.../ethernet/microchip/vcap/vcap_api_kunit.c | 933 +++
.../microchip/vcap/vcap_model_kunit.c | 5570 +++++++++++++++++
.../microchip/vcap/vcap_model_kunit.h | 10 +
25 files changed, 11867 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c
create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h
create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h
create mode 100644 drivers/net/ethernet/microchip/vcap/Kconfig
create mode 100644 drivers/net/ethernet/microchip/vcap/Makefile
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api.h
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_ag_api_kunit.h
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api.c
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api.h
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_client.h
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c
create mode 100644 drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h

--
2.38.1


2022-10-19 12:53:10

by Steen Hegelund

[permalink] [raw]
Subject: [PATCH net-next v2 6/9] net: microchip: sparx5: Adding basic rule management in VCAP API

This provides most of the rule handling needed to add a new rule to a VCAP.
To add a rule a client must follow these steps:

1) Allocate a new rule (provide an id or get one automatically assigned)
2) Add keys to the rule
3) Add actions to the rule
4) Optionally set a keyset on the rule
5) Optionally set an actionset on the rule
6) Validate the rule (this will add keyset and actionset if not specified
in the previous steps)
7) Add the rule (if the validation was successful)
8) Free the rule instance (a copy has been added to the VCAP)

The validation step will fail if there are no keysets with the requested
keys, or there are no actionsets with the requested actions.
The validation will also fail if the keyset is not configured for the port
for the requested protocol).

Signed-off-by: Steen Hegelund <[email protected]>
---
.../microchip/sparx5/sparx5_vcap_impl.c | 1 +
.../net/ethernet/microchip/vcap/vcap_api.c | 320 +++++++++++++++++-
.../ethernet/microchip/vcap/vcap_api_client.h | 3 +
3 files changed, 316 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
index dbd2c2c4d346..50153264179e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
@@ -519,6 +519,7 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5)

list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) {
sparx5_vcap_port_key_deselection(sparx5, admin);
+ vcap_del_rules(ctrl, admin);
list_del(&admin->list);
sparx5_vcap_admin_free(admin);
}
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index d929d2d00b6c..7f5ec072681c 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -18,9 +18,22 @@ struct vcap_rule_internal {
struct vcap_admin *admin; /* vcap hw instance */
struct net_device *ndev; /* the interface that the rule applies to */
struct vcap_control *vctrl; /* the client control */
+ u32 sort_key; /* defines the position in the VCAP */
+ int keyset_sw; /* subwords in a keyset */
+ int actionset_sw; /* subwords in an actionset */
+ int keyset_sw_regs; /* registers in a subword in an keyset */
+ int actionset_sw_regs; /* registers in a subword in an actionset */
+ int size; /* the size of the rule: max(entry, action) */
u32 addr; /* address in the VCAP at insertion */
};

+/* Moving a rule in the VCAP address space */
+struct vcap_rule_move {
+ int addr; /* address to move */
+ int offset; /* change in address */
+ int count; /* blocksize of addresses to move */
+};
+
/* Return the list of keyfields for the keyset */
static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl,
enum vcap_type vt,
@@ -32,12 +45,82 @@ static const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl,
return vctrl->vcaps[vt].keyfield_set_map[keyset];
}

+/* Return the keyset information for the keyset */
+static const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl,
+ enum vcap_type vt,
+ enum vcap_keyfield_set keyset)
+{
+ const struct vcap_set *kset;
+
+ /* Check that the keyset exists in the vcap keyset list */
+ if (keyset >= vctrl->vcaps[vt].keyfield_set_size)
+ return NULL;
+ kset = &vctrl->vcaps[vt].keyfield_set[keyset];
+ if (kset->sw_per_item == 0 || kset->sw_per_item > vctrl->vcaps[vt].sw_count)
+ return NULL;
+ return kset;
+}
+
+static const struct vcap_set *
+vcap_actionfieldset(struct vcap_control *vctrl,
+ enum vcap_type vt, enum vcap_actionfield_set actionset)
+{
+ const struct vcap_set *aset;
+
+ /* Check that the actionset exists in the vcap actionset list */
+ if (actionset >= vctrl->vcaps[vt].actionfield_set_size)
+ return NULL;
+ aset = &vctrl->vcaps[vt].actionfield_set[actionset];
+ if (aset->sw_per_item == 0 || aset->sw_per_item > vctrl->vcaps[vt].sw_count)
+ return NULL;
+ return aset;
+}
+
+static int vcap_encode_rule(struct vcap_rule_internal *ri)
+{
+ /* Encoding of keyset and actionsets will be added later */
+ return 0;
+}
+
+static int vcap_api_check(struct vcap_control *ctrl)
+{
+ if (!ctrl) {
+ pr_err("%s:%d: vcap control is missing\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!ctrl->ops || !ctrl->ops->validate_keyset ||
+ !ctrl->ops->add_default_fields || !ctrl->ops->cache_erase ||
+ !ctrl->ops->cache_write || !ctrl->ops->cache_read ||
+ !ctrl->ops->init || !ctrl->ops->update || !ctrl->ops->move ||
+ !ctrl->ops->port_info) {
+ pr_err("%s:%d: client operations are missing\n",
+ __func__, __LINE__);
+ return -ENOENT;
+ }
+ return 0;
+}
+
+static void vcap_erase_cache(struct vcap_rule_internal *ri)
+{
+ ri->vctrl->ops->cache_erase(ri->admin);
+}
+
/* Update the keyset for the rule */
int vcap_set_rule_set_keyset(struct vcap_rule *rule,
enum vcap_keyfield_set keyset)
{
- /* This will be expanded with more information later */
- rule->keyset = keyset;
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ const struct vcap_set *kset;
+ int sw_width;
+
+ kset = vcap_keyfieldset(ri->vctrl, ri->admin->vtype, keyset);
+ /* Check that the keyset is valid */
+ if (!kset)
+ return -EINVAL;
+ ri->keyset_sw = kset->sw_per_item;
+ sw_width = ri->vctrl->vcaps[ri->admin->vtype].sw_width;
+ ri->keyset_sw_regs = DIV_ROUND_UP(sw_width, 32);
+ ri->data.keyset = keyset;
return 0;
}
EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset);
@@ -46,8 +129,18 @@ EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset);
int vcap_set_rule_set_actionset(struct vcap_rule *rule,
enum vcap_actionfield_set actionset)
{
- /* This will be expanded with more information later */
- rule->actionset = actionset;
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ const struct vcap_set *aset;
+ int act_width;
+
+ aset = vcap_actionfieldset(ri->vctrl, ri->admin->vtype, actionset);
+ /* Check that the actionset is valid */
+ if (!aset)
+ return -EINVAL;
+ ri->actionset_sw = aset->sw_per_item;
+ act_width = ri->vctrl->vcaps[ri->admin->vtype].act_width;
+ ri->actionset_sw_regs = DIV_ROUND_UP(act_width, 32);
+ ri->data.actionset = actionset;
return 0;
}
EXPORT_SYMBOL_GPL(vcap_set_rule_set_actionset);
@@ -82,11 +175,59 @@ 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 */
+static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri)
+{
+ struct vcap_rule_internal *duprule;
+
+ /* Allocate the client part */
+ duprule = kzalloc(sizeof(*duprule), GFP_KERNEL);
+ if (!duprule)
+ return ERR_PTR(-ENOMEM);
+ *duprule = *ri;
+ /* Not inserted in the VCAP */
+ INIT_LIST_HEAD(&duprule->list);
+ /* No elements in these lists */
+ INIT_LIST_HEAD(&duprule->data.keyfields);
+ INIT_LIST_HEAD(&duprule->data.actionfields);
+ return duprule;
+}
+
+/* Write VCAP cache content to the VCAP HW instance */
+static int vcap_write_rule(struct vcap_rule_internal *ri)
+{
+ struct vcap_admin *admin = ri->admin;
+ int sw_idx, ent_idx = 0, act_idx = 0;
+ u32 addr = ri->addr;
+
+ if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) {
+ pr_err("%s:%d: rule is empty\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ /* Use the values in the streams to write the VCAP cache */
+ for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) {
+ ri->vctrl->ops->cache_write(ri->ndev, admin,
+ VCAP_SEL_ENTRY, ent_idx,
+ ri->keyset_sw_regs);
+ ri->vctrl->ops->cache_write(ri->ndev, admin,
+ VCAP_SEL_ACTION, act_idx,
+ ri->actionset_sw_regs);
+ ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE,
+ VCAP_SEL_ALL, addr);
+ ent_idx += ri->keyset_sw_regs;
+ act_idx += ri->actionset_sw_regs;
+ }
+ return 0;
+}
+
/* Lookup a vcap instance using chain id */
struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
{
struct vcap_admin *admin;

+ if (vcap_api_check(vctrl))
+ return NULL;
+
list_for_each_entry(admin, &vctrl->list, list) {
if (cid >= admin->first_cid && cid <= admin->last_cid)
return admin;
@@ -95,12 +236,62 @@ struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
}
EXPORT_SYMBOL_GPL(vcap_find_admin);

+/* Check if there is room for a new rule */
+static int vcap_rule_space(struct vcap_admin *admin, int size)
+{
+ if (admin->last_used_addr - size < admin->first_valid_addr) {
+ pr_err("%s:%d: No room for rule size: %u, %u\n",
+ __func__, __LINE__, size, admin->first_valid_addr);
+ return -ENOSPC;
+ }
+ return 0;
+}
+
+/* Add the keyset typefield to the list of rule keyfields */
+static int vcap_add_type_keyfield(struct vcap_rule *rule)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ enum vcap_keyfield_set keyset = rule->keyset;
+ enum vcap_type vt = ri->admin->vtype;
+ const struct vcap_field *fields;
+ const struct vcap_set *kset;
+ int ret = -EINVAL;
+
+ kset = vcap_keyfieldset(ri->vctrl, vt, keyset);
+ if (!kset)
+ return ret;
+ if (kset->type_id == (u8)-1) /* No type field is needed */
+ return 0;
+
+ fields = vcap_keyfields(ri->vctrl, vt, keyset);
+ if (!fields)
+ return -EINVAL;
+ if (fields[VCAP_KF_TYPE].width > 1) {
+ ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE,
+ kset->type_id, 0xff);
+ } else {
+ if (kset->type_id)
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE,
+ VCAP_BIT_1);
+ else
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE,
+ VCAP_BIT_0);
+ }
+ return 0;
+}
+
/* Validate a rule with respect to available port keys */
int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
{
struct vcap_rule_internal *ri = to_intrule(rule);
+ enum vcap_keyfield_set keysets[10];
+ struct vcap_keyset_list kslist;
+ int ret;

/* This validation will be much expanded later */
+ ret = vcap_api_check(ri->vctrl);
+ if (ret)
+ return ret;
if (!ri->admin) {
ri->data.exterr = VCAP_ERR_NO_ADMIN;
return -EINVAL;
@@ -113,14 +304,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
return -EINVAL;
}
+ /* prepare for keyset validation */
+ keysets[0] = ri->data.keyset;
+ kslist.keysets = keysets;
+ kslist.cnt = 1;
+ /* Pick a keyset that is supported in the port lookups */
+ ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist,
+ l3_proto);
+ if (ret < 0) {
+ pr_err("%s:%d: keyset validation failed: %d\n",
+ __func__, __LINE__, ret);
+ ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH;
+ return ret;
+ }
if (ri->data.actionset == VCAP_AFS_NO_VALUE) {
ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH;
return -EINVAL;
}
- return 0;
+ vcap_add_type_keyfield(rule);
+ /* Add default fields to this rule */
+ ri->vctrl->ops->add_default_fields(ri->ndev, ri->admin, rule);
+
+ /* Rule size is the maximum of the entry and action subword count */
+ ri->size = max(ri->keyset_sw, ri->actionset_sw);
+
+ /* Finally check if there is room for the rule in the VCAP */
+ return vcap_rule_space(ri->admin, ri->size);
}
EXPORT_SYMBOL_GPL(vcap_val_rule);

+/* calculate the address of the next rule after this (lower address and prio) */
+static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri)
+{
+ return ((addr - ri->size) / ri->size) * ri->size;
+}
+
/* Assign a unique rule id and autogenerate one if id == 0 */
static u32 vcap_set_rule_id(struct vcap_rule_internal *ri)
{
@@ -141,11 +359,60 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri)
return ri->data.id;
}

+static int vcap_insert_rule(struct vcap_rule_internal *ri,
+ struct vcap_rule_move *move)
+{
+ struct vcap_admin *admin = ri->admin;
+ struct vcap_rule_internal *duprule;
+
+ /* Only support appending rules for now */
+ 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);
+ if (IS_ERR(duprule))
+ return PTR_ERR(duprule);
+ list_add_tail(&duprule->list, &admin->rules);
+ return 0;
+}
+
+static void vcap_move_rules(struct vcap_rule_internal *ri,
+ struct vcap_rule_move *move)
+{
+ ri->vctrl->ops->move(ri->ndev, ri->admin, move->addr,
+ move->offset, move->count);
+}
+
/* Encode and write a validated rule to the VCAP */
int vcap_add_rule(struct vcap_rule *rule)
{
- /* This will later handling the encode and writing of the rule */
- return 0;
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ struct vcap_rule_move move = {0};
+ int ret;
+
+ ret = vcap_api_check(ri->vctrl);
+ if (ret)
+ return ret;
+ /* Insert the new rule in the list of vcap rules */
+ ret = vcap_insert_rule(ri, &move);
+ if (ret < 0) {
+ pr_err("%s:%d: could not insert rule in vcap list: %d\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+ if (move.count > 0)
+ vcap_move_rules(ri, &move);
+ ret = vcap_encode_rule(ri);
+ if (ret) {
+ pr_err("%s:%d: rule encoding error: %d\n", __func__, __LINE__, ret);
+ goto out;
+ }
+
+ ret = vcap_write_rule(ri);
+ if (ret)
+ pr_err("%s:%d: rule write error: %d\n", __func__, __LINE__, ret);
+out:
+ return ret;
}
EXPORT_SYMBOL_GPL(vcap_add_rule);

@@ -157,6 +424,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
{
struct vcap_rule_internal *ri;
struct vcap_admin *admin;
+ int maxsize;

if (!ndev)
return ERR_PTR(-ENODEV);
@@ -164,6 +432,16 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
admin = vcap_find_admin(vctrl, vcap_chain_id);
if (!admin)
return ERR_PTR(-ENOENT);
+ /* Sanity check that this VCAP is supported on this platform */
+ if (vctrl->vcaps[admin->vtype].rows == 0)
+ return ERR_PTR(-EINVAL);
+ /* Check if a rule with this id already exists */
+ if (vcap_lookup_rule(vctrl, id))
+ return ERR_PTR(-EEXIST);
+ /* Check if there is room for the rule in the block(s) of the VCAP */
+ maxsize = vctrl->vcaps[admin->vtype].sw_count; /* worst case rule size */
+ if (vcap_rule_space(admin, maxsize))
+ return ERR_PTR(-ENOSPC);
/* Create a container for the rule and return it */
ri = kzalloc(sizeof(*ri), GFP_KERNEL);
if (!ri)
@@ -182,6 +460,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
ri->vctrl = vctrl; /* refer to the client */
if (vcap_set_rule_id(ri) == 0)
goto out_free;
+ vcap_erase_cache(ri);
return (struct vcap_rule *)ri;

out_free:
@@ -216,16 +495,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
{
struct vcap_rule_internal *ri, *elem;
struct vcap_admin *admin;
+ int err;

/* This will later also handle rule moving */
if (!ndev)
return -ENODEV;
+ err = vcap_api_check(vctrl);
+ if (err)
+ return err;
/* Look for the rule id in all vcaps */
ri = vcap_lookup_rule(vctrl, id);
if (!ri)
return -EINVAL;
admin = ri->admin;
list_del(&ri->list);
+
+ /* delete the rule in the cache */
+ vctrl->ops->init(ndev, admin, ri->addr, ri->size);
if (list_empty(&admin->rules)) {
admin->last_used_addr = admin->last_valid_addr;
} else {
@@ -238,11 +524,29 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
}
EXPORT_SYMBOL_GPL(vcap_del_rule);

+/* Delete all rules in the VCAP instance */
+int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
+{
+ struct vcap_rule_internal *ri, *next_ri;
+ int ret = vcap_api_check(vctrl);
+
+ if (ret)
+ return ret;
+ 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);
+ }
+ admin->last_used_addr = admin->last_valid_addr;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vcap_del_rules);
+
/* Find information on a key field in a rule */
const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
enum vcap_key_field key)
{
- struct vcap_rule_internal *ri = (struct vcap_rule_internal *)rule;
+ struct vcap_rule_internal *ri = to_intrule(rule);
enum vcap_keyfield_set keyset = rule->keyset;
enum vcap_type vt = ri->admin->vtype;
const struct vcap_field *fields;
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
index b0a2eae81dbe..a8d5072cd6e2 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
@@ -189,4 +189,7 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
/* Find a rule id with a provided cookie */
int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie);

+/* Cleanup a VCAP instance */
+int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin);
+
#endif /* __VCAP_API_CLIENT__ */
--
2.38.1

2022-10-20 08:00:15

by Casper Andersson

[permalink] [raw]
Subject: Re: [PATCH net-next v2 6/9] net: microchip: sparx5: Adding basic rule management in VCAP API

On 2022-10-19 13:42, Steen Hegelund wrote:
> +/* Write VCAP cache content to the VCAP HW instance */
> +static int vcap_write_rule(struct vcap_rule_internal *ri)
> +{
> + struct vcap_admin *admin = ri->admin;
> + int sw_idx, ent_idx = 0, act_idx = 0;
> + u32 addr = ri->addr;
> +
> + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) {
> + pr_err("%s:%d: rule is empty\n", __func__, __LINE__);
> + return -EINVAL;
> + }
> + /* Use the values in the streams to write the VCAP cache */
> + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) {
> + ri->vctrl->ops->cache_write(ri->ndev, admin,
> + VCAP_SEL_ENTRY, ent_idx,
> + ri->keyset_sw_regs);
> + ri->vctrl->ops->cache_write(ri->ndev, admin,
> + VCAP_SEL_ACTION, act_idx,
> + ri->actionset_sw_regs);
> + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE,
> + VCAP_SEL_ALL, addr);

Arguments not aligned with opening parenthesis.

> /* Validate a rule with respect to available port keys */
> int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
> {
> struct vcap_rule_internal *ri = to_intrule(rule);
> + enum vcap_keyfield_set keysets[10];
> + struct vcap_keyset_list kslist;
> + int ret;
>
> /* This validation will be much expanded later */
> + ret = vcap_api_check(ri->vctrl);
> + if (ret)
> + return ret;
> if (!ri->admin) {
> ri->data.exterr = VCAP_ERR_NO_ADMIN;
> return -EINVAL;
> @@ -113,14 +304,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
> ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
> return -EINVAL;
> }
> + /* prepare for keyset validation */
> + keysets[0] = ri->data.keyset;
> + kslist.keysets = keysets;
> + kslist.cnt = 1;
> + /* Pick a keyset that is supported in the port lookups */
> + ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist,
> + l3_proto);
> + if (ret < 0) {
> + pr_err("%s:%d: keyset validation failed: %d\n",
> + __func__, __LINE__, ret);
> + ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH;
> + return ret;
> + }
> if (ri->data.actionset == VCAP_AFS_NO_VALUE) {
> ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH;
> return -EINVAL;
> }
> - return 0;
> + vcap_add_type_keyfield(rule);
> + /* Add default fields to this rule */
> + ri->vctrl->ops->add_default_fields(ri->ndev, ri->admin, rule);
> +
> + /* Rule size is the maximum of the entry and action subword count */
> + ri->size = max(ri->keyset_sw, ri->actionset_sw);
> +
> + /* Finally check if there is room for the rule in the VCAP */
> + return vcap_rule_space(ri->admin, ri->size);
> }
> EXPORT_SYMBOL_GPL(vcap_val_rule);

Validating a rule also modifies it. I think validation and modification
should generally be kept apart. But it looks like it might be hard with
the current design since you need to add the fields to then check the
space it takes, and the rule sizes can depend on the hardware.

Tested on Microchip PCB135 switch.

Tested-by: Casper Andersson <[email protected]>
Reviewed-by: Casper Andersson <[email protected]>

2022-10-20 09:50:31

by Steen Hegelund

[permalink] [raw]
Subject: Re: [PATCH net-next v2 6/9] net: microchip: sparx5: Adding basic rule management in VCAP API

Hi Casper,

On Thu, 2022-10-20 at 09:41 +0200, Casper Andersson wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> On 2022-10-19 13:42, Steen Hegelund wrote:
> > +/* Write VCAP cache content to the VCAP HW instance */
> > +static int vcap_write_rule(struct vcap_rule_internal *ri)
> > +{
> > +     struct vcap_admin *admin = ri->admin;
> > +     int sw_idx, ent_idx = 0, act_idx = 0;
> > +     u32 addr = ri->addr;
> > +
> > +     if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) {
> > +             pr_err("%s:%d: rule is empty\n", __func__, __LINE__);
> > +             return -EINVAL;
> > +     }
> > +     /* Use the values in the streams to write the VCAP cache */
> > +     for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) {
> > +             ri->vctrl->ops->cache_write(ri->ndev, admin,
> > +                                     VCAP_SEL_ENTRY, ent_idx,
> > +                                     ri->keyset_sw_regs);
> > +             ri->vctrl->ops->cache_write(ri->ndev, admin,
> > +                                     VCAP_SEL_ACTION, act_idx,
> > +                                     ri->actionset_sw_regs);
> > +             ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE,
> > +                                VCAP_SEL_ALL, addr);
>
> Arguments not aligned with opening parenthesis.

I will fix that.

>
> >  /* Validate a rule with respect to available port keys */
> >  int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
> >  {
> >       struct vcap_rule_internal *ri = to_intrule(rule);
> > +     enum vcap_keyfield_set keysets[10];
> > +     struct vcap_keyset_list kslist;
> > +     int ret;
> >
> >       /* This validation will be much expanded later */
> > +     ret = vcap_api_check(ri->vctrl);
> > +     if (ret)
> > +             return ret;
> >       if (!ri->admin) {
> >               ri->data.exterr = VCAP_ERR_NO_ADMIN;
> >               return -EINVAL;
> > @@ -113,14 +304,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
> >               ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
> >               return -EINVAL;
> >       }
> > +     /* prepare for keyset validation */
> > +     keysets[0] = ri->data.keyset;
> > +     kslist.keysets = keysets;
> > +     kslist.cnt = 1;
> > +     /* Pick a keyset that is supported in the port lookups */
> > +     ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist,
> > +                                           l3_proto);
> > +     if (ret < 0) {
> > +             pr_err("%s:%d: keyset validation failed: %d\n",
> > +                    __func__, __LINE__, ret);
> > +             ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH;
> > +             return ret;
> > +     }
> >       if (ri->data.actionset == VCAP_AFS_NO_VALUE) {
> >               ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH;
> >               return -EINVAL;
> >       }
> > -     return 0;
> > +     vcap_add_type_keyfield(rule);
> > +     /* Add default fields to this rule */
> > +     ri->vctrl->ops->add_default_fields(ri->ndev, ri->admin, rule);
> > +
> > +     /* Rule size is the maximum of the entry and action subword count */
> > +     ri->size = max(ri->keyset_sw, ri->actionset_sw);
> > +
> > +     /* Finally check if there is room for the rule in the VCAP */
> > +     return vcap_rule_space(ri->admin, ri->size);
> >  }
> >  EXPORT_SYMBOL_GPL(vcap_val_rule);
>
> Validating a rule also modifies it. I think validation and modification
> should generally be kept apart. But it looks like it might be hard with
> the current design since you need to add the fields to then check the
> space it takes, and the rule sizes can depend on the hardware.

Hmm. You got a point. I just wanted to keep the number of API calls down a bit, so the validation
also ensures that the rule has a keyset type field and any other default fields that is needed in
the particular VCAP and setting the keyset defines the size of the rule, so it all needs to fit
together in the end.
For now I would like to keep this as just one validation call.

>
> Tested on Microchip PCB135 switch.
>
> Tested-by: Casper Andersson <[email protected]>
> Reviewed-by: Casper Andersson <[email protected]>
>

BR
Steen