summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/microchip/vcap
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2022-11-11 10:39:12 +0000
committerDavid S. Miller <davem@davemloft.net>2022-11-11 10:39:12 +0000
commitf53e14328ec08bda94a95d50e3eb0abb72963efc (patch)
tree355996ac77dc883806491ad5bd755ea47bb9a7b8 /drivers/net/ethernet/microchip/vcap
parent573c38533c0d7f7a8964530c2c606eb691ba28ec (diff)
parentc956b9b318d9036701c471dd458f9ed31defc629 (diff)
downloadlinux-f53e14328ec08bda94a95d50e3eb0abb72963efc.tar.gz
linux-f53e14328ec08bda94a95d50e3eb0abb72963efc.tar.bz2
linux-f53e14328ec08bda94a95d50e3eb0abb72963efc.zip
Merge branch 'sparx5-TC-key'
Steen Hegelund says: ==================== Extend TC key support for Sparx5 IS2 VCAP This provides extended tc flower filter key support for the Sparx5 VCAP functionality. It builds on top of the initial IS2 VCAP support found in this series: https://lore.kernel.org/all/20221020130904.1215072-1-steen.hegelund@microchip.com/ Overview: ========= The added flower filter key (dissector) support is this: - ipv4_addr (sip and dip) - ipv6_addr (sip and dip) - control (IPv4 fragments) - portnum (tcp and udp port numbers) - basic (L3 and L4 protocol) - vlan (outer vlan tag info) - tcp (tcp flags) - ip (tos field) The IS2 VCAP supports classified VLAN information which amounts to the outer VLAN info in case of multiple tags. Functionality: ============== Before frames can match IS2 VCAP rules with e.g an IPv4 source address, the IS2 VCAPs keyset configuration must include keyset that contains a IPv4 source address and this must be configured for the lookup/port/traffic-type that you want to match on. The Sparx5 IS2 VCAP has the following traffic types: - Non-Ethernet frames - IPv4 Unicast frames - IPv4 Multicast frames - IPv6 Unicast frames - IPv6 Multicast frames - ARP frames So to cover IPv4 traffic the two IPv4 categories must be configured with a keyset that contains IPv4 address information such as the VCAP_KFS_IP4_TCP_UDP keyset. The IPv4 and IPv6 traffic types are configured with useful default keysets, in later series we will use the tc template functionality when we want to change these defaults. The flower filter must contain a goto action as its last action and the chain id must specify the chain id of the next lookup in a VCAP or a destination outside the VCAP ranges. To activate the VCAP lookups on a port you must add a TC matchall filter on the port containing a single goto action that points to the chain id of the first lookup in the IS2 VCAP. From then on frames arriving on this port will be matched against the rules in the IS2 VCAP lookups. Removing the matchall filter will deactivate the IS2 lookups, but will leave the VCAP rules in the memory of the VCAP instance, and from then in frames will no longer be matched against the rules the in IS2 VCAP. If the matchall rule is added back again the IS2 rules will be active once more. Delivery: ========= This is current plan for delivering the full VCAP feature set of Sparx5: - TC flower filter statistics and rule order by size and priority - debugfs support for inspecting rules - support for TC protocol all - Sparx5 IS0 VCAP support - add TC policer and drop action support (depends on the Sparx5 QoS support upstreamed separately) - Sparx5 ES0 VCAP support - TC flower template support - TC matchall filter support for mirroring and policing ports - TC flower filter mirror action support - Sparx5 ES2 VCAP support Version History: ================ v6 Rebased on the latest next-next master branch. No other implementation changes. v5 Add support for a TC matchall filter with a single goto action which will activate the lookups of the VCAP. Removing this filter will deactivate the VCAP lookups again. v4 Add support for TC flower filter goto action and a check of the actions: check action combinations and the goto chain id. v3 Add some more details to the explanation in the commit message about support for MAC_ETYPE keysets and "protocol all" as well as the classified VLAN information. This is done to help testing the feature. No implementation changes in this version. v2 Split one of the KUNIT tests into 3 tests to fix a kernel robot build warning. v1 Initial version ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/microchip/vcap')
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.c424
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.h6
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_client.h21
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c592
4 files changed, 1030 insertions, 13 deletions
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index d255bc7deae7..b6ab6bae28c0 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -44,6 +44,13 @@ struct vcap_stream_iter {
const struct vcap_typegroup *tg; /* current typegroup */
};
+/* Stores the filter cookie that enabled the port */
+struct vcap_enabled_port {
+ struct list_head list; /* for insertion in enabled ports list */
+ struct net_device *ndev; /* the enabled port */
+ unsigned long cookie; /* filter that enabled the port */
+};
+
static void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width,
const struct vcap_typegroup *tg, u32 offset)
{
@@ -516,7 +523,7 @@ static int vcap_api_check(struct vcap_control *ctrl)
!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) {
+ !ctrl->ops->port_info || !ctrl->ops->enable) {
pr_err("%s:%d: client operations are missing\n",
__func__, __LINE__);
return -ENOENT;
@@ -644,6 +651,23 @@ static int vcap_write_rule(struct vcap_rule_internal *ri)
return 0;
}
+/* Convert a chain id to a VCAP lookup index */
+int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid)
+{
+ int lookup_first = admin->vinst * admin->lookups_per_instance;
+ int lookup_last = lookup_first + admin->lookups_per_instance;
+ int cid_next = admin->first_cid + VCAP_CID_LOOKUP_SIZE;
+ int cid = admin->first_cid;
+ int lookup;
+
+ for (lookup = lookup_first; lookup < lookup_last; ++lookup,
+ cid += VCAP_CID_LOOKUP_SIZE, cid_next += VCAP_CID_LOOKUP_SIZE)
+ if (cur_cid >= cid && cur_cid < cid_next)
+ return lookup;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vcap_chain_id_to_lookup);
+
/* Lookup a vcap instance using chain id */
struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid)
{
@@ -660,6 +684,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)
{
@@ -704,15 +764,122 @@ static int vcap_add_type_keyfield(struct vcap_rule *rule)
return 0;
}
+/* Add a keyset to a keyset list */
+bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist,
+ enum vcap_keyfield_set keyset)
+{
+ int idx;
+
+ if (keysetlist->cnt < keysetlist->max) {
+ /* Avoid duplicates */
+ for (idx = 0; idx < keysetlist->cnt; ++idx)
+ if (keysetlist->keysets[idx] == keyset)
+ return keysetlist->cnt < keysetlist->max;
+ keysetlist->keysets[keysetlist->cnt++] = keyset;
+ }
+ return keysetlist->cnt < keysetlist->max;
+}
+EXPORT_SYMBOL_GPL(vcap_keyset_list_add);
+
+/* map keyset id to a string with the keyset name */
+const char *vcap_keyset_name(struct vcap_control *vctrl,
+ enum vcap_keyfield_set keyset)
+{
+ return vctrl->stats->keyfield_set_names[keyset];
+}
+EXPORT_SYMBOL_GPL(vcap_keyset_name);
+
+/* map key field id to a string with the key name */
+const char *vcap_keyfield_name(struct vcap_control *vctrl,
+ enum vcap_key_field key)
+{
+ return vctrl->stats->keyfield_names[key];
+}
+EXPORT_SYMBOL_GPL(vcap_keyfield_name);
+
+/* map action field id to a string with the action name */
+static const char *vcap_actionfield_name(struct vcap_control *vctrl,
+ enum vcap_action_field action)
+{
+ return vctrl->stats->actionfield_names[action];
+}
+
+/* Return the keyfield that matches a key in a keyset */
+static const struct vcap_field *
+vcap_find_keyset_keyfield(struct vcap_control *vctrl,
+ enum vcap_type vtype,
+ enum vcap_keyfield_set keyset,
+ enum vcap_key_field key)
+{
+ const struct vcap_field *fields;
+ int idx, count;
+
+ fields = vcap_keyfields(vctrl, vtype, keyset);
+ if (!fields)
+ return NULL;
+
+ /* Iterate the keyfields of the keyset */
+ count = vcap_keyfield_count(vctrl, vtype, keyset);
+ for (idx = 0; idx < count; ++idx) {
+ if (fields[idx].width == 0)
+ continue;
+
+ if (key == idx)
+ return &fields[idx];
+ }
+
+ return NULL;
+}
+
+/* Match a list of keys against the keysets available in a vcap type */
+static bool vcap_rule_find_keysets(struct vcap_rule_internal *ri,
+ struct vcap_keyset_list *matches)
+{
+ const struct vcap_client_keyfield *ckf;
+ int keyset, found, keycount, map_size;
+ const struct vcap_field **map;
+ enum vcap_type vtype;
+
+ vtype = ri->admin->vtype;
+ map = ri->vctrl->vcaps[vtype].keyfield_set_map;
+ map_size = ri->vctrl->vcaps[vtype].keyfield_set_size;
+
+ /* Get a count of the keyfields we want to match */
+ keycount = 0;
+ list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+ ++keycount;
+
+ matches->cnt = 0;
+ /* Iterate the keysets of the VCAP */
+ for (keyset = 0; keyset < map_size; ++keyset) {
+ if (!map[keyset])
+ continue;
+
+ /* Iterate the keys in the rule */
+ found = 0;
+ list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+ if (vcap_find_keyset_keyfield(ri->vctrl, vtype,
+ keyset, ckf->ctrl.key))
+ ++found;
+
+ /* Save the keyset if all keyfields were found */
+ if (found == keycount)
+ if (!vcap_keyset_list_add(matches, keyset))
+ /* bail out when the quota is filled */
+ break;
+ }
+
+ return matches->cnt > 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);
+ struct vcap_keyset_list matches = {};
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;
@@ -724,24 +891,41 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
ri->data.exterr = VCAP_ERR_NO_NETDEV;
return -EINVAL;
}
+
+ matches.keysets = keysets;
+ matches.max = ARRAY_SIZE(keysets);
if (ri->data.keyset == VCAP_KFS_NO_VALUE) {
- ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
- return -EINVAL;
+ /* Iterate over rule keyfields and select keysets that fits */
+ if (!vcap_rule_find_keysets(ri, &matches)) {
+ ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
+ return -EINVAL;
+ }
+ } else {
+ /* prepare for keyset validation */
+ keysets[0] = ri->data.keyset;
+ matches.cnt = 1;
}
- /* 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);
+ ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule,
+ &matches, 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;
}
+ /* use the keyset that is supported in the port lookups */
+ ret = vcap_set_rule_set_keyset(rule, ret);
+ if (ret < 0) {
+ pr_err("%s:%d: keyset was not updated: %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
if (ri->data.actionset == VCAP_AFS_NO_VALUE) {
+ /* Later also actionsets will be matched against actions in
+ * the rule, and the type will be set accordingly
+ */
ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH;
return -EINVAL;
}
@@ -951,6 +1135,7 @@ 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_enabled_port *eport, *next_eport;
struct vcap_rule_internal *ri, *next_ri;
int ret = vcap_api_check(vctrl);
@@ -962,6 +1147,13 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
kfree(ri);
}
admin->last_used_addr = admin->last_valid_addr;
+
+ /* Remove list of enabled ports */
+ list_for_each_entry_safe(eport, next_eport, &admin->enabled, list) {
+ list_del(&eport->list);
+ kfree(eport);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(vcap_del_rules);
@@ -992,14 +1184,60 @@ static void vcap_copy_from_client_keyfield(struct vcap_rule *rule,
memcpy(&field->data, data, sizeof(field->data));
}
+/* Check if the keyfield is already in the rule */
+static bool vcap_keyfield_unique(struct vcap_rule *rule,
+ enum vcap_key_field key)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ const struct vcap_client_keyfield *ckf;
+
+ list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+ if (ckf->ctrl.key == key)
+ return false;
+ return true;
+}
+
+/* Check if the keyfield is in the keyset */
+static bool vcap_keyfield_match_keyset(struct vcap_rule *rule,
+ enum vcap_key_field key)
+{
+ 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;
+
+ /* the field is accepted if the rule has no keyset yet */
+ if (keyset == VCAP_KFS_NO_VALUE)
+ return true;
+ fields = vcap_keyfields(ri->vctrl, vt, keyset);
+ if (!fields)
+ return false;
+ /* if there is a width there is a way */
+ return fields[key].width > 0;
+}
+
static int vcap_rule_add_key(struct vcap_rule *rule,
enum vcap_key_field key,
enum vcap_field_type ftype,
struct vcap_client_keyfield_data *data)
{
+ struct vcap_rule_internal *ri = to_intrule(rule);
struct vcap_client_keyfield *field;
- /* More validation will be added here later */
+ if (!vcap_keyfield_unique(rule, key)) {
+ pr_warn("%s:%d: keyfield %s is already in the rule\n",
+ __func__, __LINE__,
+ vcap_keyfield_name(ri->vctrl, key));
+ return -EINVAL;
+ }
+
+ if (!vcap_keyfield_match_keyset(rule, key)) {
+ pr_err("%s:%d: keyfield %s does not belong in the rule keyset\n",
+ __func__, __LINE__,
+ vcap_keyfield_name(ri->vctrl, key));
+ return -EINVAL;
+ }
+
field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field)
return -ENOMEM;
@@ -1073,6 +1311,17 @@ int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key,
}
EXPORT_SYMBOL_GPL(vcap_rule_add_key_u72);
+/* Add a 128 bit key with value and mask to the rule */
+int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key,
+ struct vcap_u128_key *fieldval)
+{
+ struct vcap_client_keyfield_data data;
+
+ memcpy(&data.u128, fieldval, sizeof(data.u128));
+ return vcap_rule_add_key(rule, key, VCAP_FIELD_U128, &data);
+}
+EXPORT_SYMBOL_GPL(vcap_rule_add_key_u128);
+
static void vcap_copy_from_client_actionfield(struct vcap_rule *rule,
struct vcap_client_actionfield *field,
struct vcap_client_actionfield_data *data)
@@ -1081,14 +1330,60 @@ static void vcap_copy_from_client_actionfield(struct vcap_rule *rule,
memcpy(&field->data, data, sizeof(field->data));
}
+/* Check if the actionfield is already in the rule */
+static bool vcap_actionfield_unique(struct vcap_rule *rule,
+ enum vcap_action_field act)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ const struct vcap_client_actionfield *caf;
+
+ list_for_each_entry(caf, &ri->data.actionfields, ctrl.list)
+ if (caf->ctrl.action == act)
+ return false;
+ return true;
+}
+
+/* Check if the actionfield is in the actionset */
+static bool vcap_actionfield_match_actionset(struct vcap_rule *rule,
+ enum vcap_action_field action)
+{
+ enum vcap_actionfield_set actionset = rule->actionset;
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ enum vcap_type vt = ri->admin->vtype;
+ const struct vcap_field *fields;
+
+ /* the field is accepted if the rule has no actionset yet */
+ if (actionset == VCAP_AFS_NO_VALUE)
+ return true;
+ fields = vcap_actionfields(ri->vctrl, vt, actionset);
+ if (!fields)
+ return false;
+ /* if there is a width there is a way */
+ return fields[action].width > 0;
+}
+
static int vcap_rule_add_action(struct vcap_rule *rule,
enum vcap_action_field action,
enum vcap_field_type ftype,
struct vcap_client_actionfield_data *data)
{
+ struct vcap_rule_internal *ri = to_intrule(rule);
struct vcap_client_actionfield *field;
- /* More validation will be added here later */
+ if (!vcap_actionfield_unique(rule, action)) {
+ pr_warn("%s:%d: actionfield %s is already in the rule\n",
+ __func__, __LINE__,
+ vcap_actionfield_name(ri->vctrl, action));
+ return -EINVAL;
+ }
+
+ if (!vcap_actionfield_match_actionset(rule, action)) {
+ pr_err("%s:%d: actionfield %s does not belong in the rule actionset\n",
+ __func__, __LINE__,
+ vcap_actionfield_name(ri->vctrl, action));
+ return -EINVAL;
+ }
+
field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field)
return -ENOMEM;
@@ -1179,6 +1474,109 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule)
}
EXPORT_SYMBOL_GPL(vcap_set_tc_exterr);
+/* Check if this port is already enabled for this VCAP instance */
+static bool vcap_is_enabled(struct vcap_admin *admin, struct net_device *ndev,
+ unsigned long cookie)
+{
+ struct vcap_enabled_port *eport;
+
+ list_for_each_entry(eport, &admin->enabled, list)
+ if (eport->cookie == cookie || eport->ndev == ndev)
+ return true;
+
+ return false;
+}
+
+/* Enable this port for this VCAP instance */
+static int vcap_enable(struct vcap_admin *admin, struct net_device *ndev,
+ unsigned long cookie)
+{
+ struct vcap_enabled_port *eport;
+
+ eport = kzalloc(sizeof(*eport), GFP_KERNEL);
+ if (!eport)
+ return -ENOMEM;
+
+ eport->ndev = ndev;
+ eport->cookie = cookie;
+ list_add_tail(&eport->list, &admin->enabled);
+
+ return 0;
+}
+
+/* Disable this port for this VCAP instance */
+static int vcap_disable(struct vcap_admin *admin, struct net_device *ndev,
+ unsigned long cookie)
+{
+ struct vcap_enabled_port *eport;
+
+ list_for_each_entry(eport, &admin->enabled, list) {
+ if (eport->cookie == cookie && eport->ndev == ndev) {
+ list_del(&eport->list);
+ kfree(eport);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/* Find the VCAP instance that enabled the port using a specific filter */
+static struct vcap_admin *vcap_find_admin_by_cookie(struct vcap_control *vctrl,
+ unsigned long cookie)
+{
+ 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->cookie == cookie)
+ return admin;
+
+ return NULL;
+}
+
+/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
+int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
+ int chain_id, unsigned long cookie, bool enable)
+{
+ struct vcap_admin *admin;
+ int err;
+
+ err = vcap_api_check(vctrl);
+ if (err)
+ return err;
+
+ if (!ndev)
+ return -ENODEV;
+
+ if (chain_id)
+ admin = vcap_find_admin(vctrl, chain_id);
+ else
+ admin = vcap_find_admin_by_cookie(vctrl, cookie);
+ if (!admin)
+ return -ENOENT;
+
+ /* first instance and first chain */
+ if (admin->vinst || chain_id > admin->first_cid)
+ return -EFAULT;
+
+ err = vctrl->ops->enable(ndev, admin, enable);
+ if (err)
+ return err;
+
+ if (chain_id) {
+ if (vcap_is_enabled(admin, ndev, cookie))
+ return -EADDRINUSE;
+ vcap_enable(admin, ndev, cookie);
+ } else {
+ vcap_disable(admin, ndev, cookie);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vcap_enable_lookups);
+
#ifdef CONFIG_VCAP_KUNIT_TEST
#include "vcap_api_kunit.c"
#endif
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h
index eb2eae75c7e8..bfb8ad535074 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h
@@ -166,6 +166,7 @@ enum vcap_rule_error {
struct vcap_admin {
struct list_head list; /* for insertion in vcap_control */
struct list_head rules; /* list of rules */
+ struct list_head enabled; /* list of enabled ports */
enum vcap_type vtype; /* type of vcap */
int vinst; /* instance number within the same type */
int first_cid; /* first chain id in this vcap */
@@ -255,6 +256,11 @@ struct vcap_operations {
int (*pf)(void *out, int arg, const char *fmt, ...),
void *out,
int arg);
+ /* enable/disable the lookups in a vcap instance */
+ int (*enable)
+ (struct net_device *ndev,
+ struct vcap_admin *admin,
+ bool enable);
};
/* VCAP API Client control interface */
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
index 5df6808679ff..0ea5ec96adc8 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
@@ -143,6 +143,10 @@ enum vcap_bit {
VCAP_BIT_1
};
+/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
+int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
+ int chain_id, unsigned long cookie, bool enable);
+
/* VCAP rule operations */
/* Allocate a rule and fill in the basic information */
struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
@@ -176,12 +180,16 @@ int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key,
struct vcap_u48_key *fieldval);
int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key,
struct vcap_u72_key *fieldval);
+int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key,
+ struct vcap_u128_key *fieldval);
int vcap_rule_add_action_bit(struct vcap_rule *rule,
enum vcap_action_field action, enum vcap_bit val);
int vcap_rule_add_action_u32(struct vcap_rule *rule,
enum vcap_action_field action, u32 value);
/* VCAP lookup operations */
+/* Convert a chain id to a VCAP lookup index */
+int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid);
/* Lookup a vcap instance using chain id */
struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid);
/* Find information on a key field in a rule */
@@ -189,6 +197,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);
@@ -199,4 +209,15 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule);
/* Cleanup a VCAP instance */
int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin);
+/* Add a keyset to a keyset list */
+bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist,
+ enum vcap_keyfield_set keyset);
+
+/* map keyset id to a string with the keyset name */
+const char *vcap_keyset_name(struct vcap_control *vctrl,
+ enum vcap_keyfield_set keyset);
+/* map key field id to a string with the key name */
+const char *vcap_keyfield_name(struct vcap_control *vctrl,
+ enum vcap_key_field key);
+
#endif /* __VCAP_API_CLIENT__ */
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
index d142ed660338..b0ec51b37683 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
@@ -22,6 +22,7 @@ static u32 test_init_start;
static u32 test_init_count;
static u32 test_hw_counter_id;
static struct vcap_cache_data test_hw_cache;
+static struct net_device test_netdev = {};
/* Callback used by the VCAP API */
static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev,
@@ -204,6 +205,13 @@ static int vcap_test_port_info(struct net_device *ndev, enum vcap_type vtype,
return 0;
}
+static int vcap_test_enable(struct net_device *ndev,
+ struct vcap_admin *admin,
+ bool enable)
+{
+ return 0;
+}
+
static struct vcap_operations test_callbacks = {
.validate_keyset = test_val_keyset,
.add_default_fields = test_add_def_fields,
@@ -214,6 +222,7 @@ static struct vcap_operations test_callbacks = {
.update = test_cache_update,
.move = test_cache_move,
.port_info = vcap_test_port_info,
+ .enable = vcap_test_enable,
};
static struct vcap_control test_vctrl = {
@@ -904,6 +913,586 @@ static void vcap_api_encode_rule_actionset_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[11]);
}
+static void vcap_api_rule_add_keyvalue_test(struct kunit *test)
+{
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS2,
+ };
+ struct vcap_rule_internal ri = {
+ .admin = &admin,
+ .data = {
+ .keyset = VCAP_KFS_NO_VALUE,
+ },
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_rule *rule = (struct vcap_rule *)&ri;
+ struct vcap_client_keyfield *kf;
+ int ret;
+ struct vcap_u128_key dip = {
+ .value = {0x17, 0x26, 0x35, 0x44, 0x63, 0x62, 0x71},
+ .mask = {0xf1, 0xf2, 0xf3, 0xf4, 0x4f, 0x3f, 0x2f, 0x1f},
+ };
+ int idx;
+
+ INIT_LIST_HEAD(&rule->keyfields);
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield,
+ ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value);
+ KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask);
+
+ INIT_LIST_HEAD(&rule->keyfields);
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield,
+ ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.value);
+ KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask);
+
+ INIT_LIST_HEAD(&rule->keyfields);
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS,
+ VCAP_BIT_ANY);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield,
+ ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value);
+ KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.mask);
+
+ INIT_LIST_HEAD(&rule->keyfields);
+ ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, 0x98765432, 0xff00ffab);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield,
+ ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_KF_TYPE, kf->ctrl.key);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, kf->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x98765432, kf->data.u32.value);
+ KUNIT_EXPECT_EQ(test, 0xff00ffab, kf->data.u32.mask);
+
+ INIT_LIST_HEAD(&rule->keyfields);
+ ret = vcap_rule_add_key_u128(rule, VCAP_KF_L3_IP6_SIP, &dip);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield,
+ ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_KF_L3_IP6_SIP, kf->ctrl.key);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_U128, kf->ctrl.type);
+ for (idx = 0; idx < ARRAY_SIZE(dip.value); ++idx)
+ KUNIT_EXPECT_EQ(test, dip.value[idx], kf->data.u128.value[idx]);
+ for (idx = 0; idx < ARRAY_SIZE(dip.mask); ++idx)
+ KUNIT_EXPECT_EQ(test, dip.mask[idx], kf->data.u128.mask[idx]);
+}
+
+static void vcap_api_rule_add_actionvalue_test(struct kunit *test)
+{
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS2,
+ };
+ struct vcap_rule_internal ri = {
+ .admin = &admin,
+ .data = {
+ .actionset = VCAP_AFS_NO_VALUE,
+ },
+ };
+ struct vcap_rule *rule = (struct vcap_rule *)&ri;
+ struct vcap_client_actionfield *af;
+ int ret;
+
+ INIT_LIST_HEAD(&rule->actionfields);
+ ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_0);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ af = list_first_entry(&rule->actionfields,
+ struct vcap_client_actionfield, ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value);
+
+ INIT_LIST_HEAD(&rule->actionfields);
+ ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ af = list_first_entry(&rule->actionfields,
+ struct vcap_client_actionfield, ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x1, af->data.u1.value);
+
+ INIT_LIST_HEAD(&rule->actionfields);
+ ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_ANY);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ af = list_first_entry(&rule->actionfields,
+ struct vcap_client_actionfield, ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value);
+
+ INIT_LIST_HEAD(&rule->actionfields);
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_TYPE, 0x98765432);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ af = list_first_entry(&rule->actionfields,
+ struct vcap_client_actionfield, ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_AF_TYPE, af->ctrl.action);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0x98765432, af->data.u32.value);
+
+ INIT_LIST_HEAD(&rule->actionfields);
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_MASK_MODE, 0xaabbccdd);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ af = list_first_entry(&rule->actionfields,
+ struct vcap_client_actionfield, ctrl.list);
+ KUNIT_EXPECT_EQ(test, VCAP_AF_MASK_MODE, af->ctrl.action);
+ KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type);
+ KUNIT_EXPECT_EQ(test, 0xaabbccdd, af->data.u32.value);
+}
+
+static void vcap_api_rule_find_keyset_basic_test(struct kunit *test)
+{
+ struct vcap_keyset_list matches = {};
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS2,
+ };
+ struct vcap_rule_internal ri = {
+ .admin = &admin,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_client_keyfield ckf[] = {
+ {
+ .ctrl.key = VCAP_KF_TYPE,
+ }, {
+ .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS,
+ }, {
+ .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_L3,
+ }, {
+ .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_RNG,
+ }, {
+ .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK,
+ }, {
+ .ctrl.key = VCAP_KF_L2_DMAC,
+ }, {
+ .ctrl.key = VCAP_KF_ETYPE_LEN_IS,
+ }, {
+ .ctrl.key = VCAP_KF_ETYPE,
+ },
+ };
+ int idx;
+ bool ret;
+ enum vcap_keyfield_set keysets[10] = {};
+
+ matches.keysets = keysets;
+ matches.max = ARRAY_SIZE(keysets);
+
+ INIT_LIST_HEAD(&ri.data.keyfields);
+ for (idx = 0; idx < ARRAY_SIZE(ckf); idx++)
+ list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields);
+
+ ret = vcap_rule_find_keysets(&ri, &matches);
+
+ KUNIT_EXPECT_EQ(test, true, ret);
+ KUNIT_EXPECT_EQ(test, 1, matches.cnt);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[0]);
+}
+
+static void vcap_api_rule_find_keyset_failed_test(struct kunit *test)
+{
+ struct vcap_keyset_list matches = {};
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS2,
+ };
+ struct vcap_rule_internal ri = {
+ .admin = &admin,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_client_keyfield ckf[] = {
+ {
+ .ctrl.key = VCAP_KF_TYPE,
+ }, {
+ .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS,
+ }, {
+ .ctrl.key = VCAP_KF_ARP_OPCODE,
+ }, {
+ .ctrl.key = VCAP_KF_L3_IP4_SIP,
+ }, {
+ .ctrl.key = VCAP_KF_L3_IP4_DIP,
+ }, {
+ .ctrl.key = VCAP_KF_8021Q_PCP_CLS,
+ }, {
+ .ctrl.key = VCAP_KF_ETYPE_LEN_IS, /* Not with ARP */
+ }, {
+ .ctrl.key = VCAP_KF_ETYPE, /* Not with ARP */
+ },
+ };
+ int idx;
+ bool ret;
+ enum vcap_keyfield_set keysets[10] = {};
+
+ matches.keysets = keysets;
+ matches.max = ARRAY_SIZE(keysets);
+
+ INIT_LIST_HEAD(&ri.data.keyfields);
+ for (idx = 0; idx < ARRAY_SIZE(ckf); idx++)
+ list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields);
+
+ ret = vcap_rule_find_keysets(&ri, &matches);
+
+ KUNIT_EXPECT_EQ(test, false, ret);
+ KUNIT_EXPECT_EQ(test, 0, matches.cnt);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_NO_VALUE, matches.keysets[0]);
+}
+
+static void vcap_api_rule_find_keyset_many_test(struct kunit *test)
+{
+ struct vcap_keyset_list matches = {};
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS2,
+ };
+ struct vcap_rule_internal ri = {
+ .admin = &admin,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_client_keyfield ckf[] = {
+ {
+ .ctrl.key = VCAP_KF_TYPE,
+ }, {
+ .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS,
+ }, {
+ .ctrl.key = VCAP_KF_8021Q_DEI_CLS,
+ }, {
+ .ctrl.key = VCAP_KF_8021Q_PCP_CLS,
+ }, {
+ .ctrl.key = VCAP_KF_8021Q_VID_CLS,
+ }, {
+ .ctrl.key = VCAP_KF_ISDX_CLS,
+ }, {
+ .ctrl.key = VCAP_KF_L2_MC_IS,
+ }, {
+ .ctrl.key = VCAP_KF_L2_BC_IS,
+ },
+ };
+ int idx;
+ bool ret;
+ enum vcap_keyfield_set keysets[10] = {};
+
+ matches.keysets = keysets;
+ matches.max = ARRAY_SIZE(keysets);
+
+ INIT_LIST_HEAD(&ri.data.keyfields);
+ for (idx = 0; idx < ARRAY_SIZE(ckf); idx++)
+ list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields);
+
+ ret = vcap_rule_find_keysets(&ri, &matches);
+
+ KUNIT_EXPECT_EQ(test, true, ret);
+ KUNIT_EXPECT_EQ(test, 6, matches.cnt);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_ARP, matches.keysets[0]);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_OTHER, matches.keysets[1]);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_TCP_UDP, matches.keysets[2]);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_IP6_STD, matches.keysets[3]);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_IP_7TUPLE, matches.keysets[4]);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[5]);
+}
+
+static void vcap_api_encode_rule_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin is2_admin = {
+ .vtype = VCAP_TYPE_IS2,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .last_valid_addr = 3071,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+ struct vcap_rule *rule = 0;
+ struct vcap_rule_internal *ri = 0;
+ int vcap_chain_id = 10005;
+ enum vcap_user user = VCAP_USER_VCAP_UTIL;
+ u16 priority = 10;
+ int id = 100;
+ int ret;
+ struct vcap_u48_key smac = {
+ .value = { 0x88, 0x75, 0x32, 0x34, 0x9e, 0xb1 },
+ .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+ };
+ struct vcap_u48_key dmac = {
+ .value = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 },
+ .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+ };
+ u32 port_mask_rng_value = 0x05;
+ u32 port_mask_rng_mask = 0x0f;
+ u32 igr_port_mask_value = 0xffabcd01;
+ u32 igr_port_mask_mask = ~0;
+ /* counter is not written yet, so it is not in expwriteaddr */
+ u32 expwriteaddr[] = {792, 793, 794, 795, 796, 797, 0};
+ int idx;
+
+ vcap_test_api_init(&is2_admin);
+
+ /* Allocate the rule */
+ rule = vcap_alloc_rule(&test_vctrl, &test_netdev, vcap_chain_id, user,
+ priority, id);
+ KUNIT_EXPECT_PTR_NE(test, NULL, rule);
+ ri = (struct vcap_rule_internal *)rule;
+
+ /* Add rule keys */
+ ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_DMAC, &dmac);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_SMAC, &smac);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ /* Cannot add the same field twice */
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1);
+ KUNIT_EXPECT_EQ(test, -EINVAL, ret);
+ ret = vcap_rule_add_key_bit(rule, VCAP_KF_IF_IGR_PORT_MASK_L3,
+ VCAP_BIT_ANY);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG,
+ port_mask_rng_value, port_mask_rng_mask);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK,
+ igr_port_mask_value, igr_port_mask_mask);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ /* Add rule actions */
+ ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_CNT_ID, id);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID, 1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID_MASK, 1);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ /* For now the actionset is hardcoded */
+ ret = vcap_set_rule_set_actionset(rule, VCAP_AFS_BASE_TYPE);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ /* Validation with validate keyset callback */
+ ret = vcap_val_rule(rule, ETH_P_ALL);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, rule->keyset);
+ KUNIT_EXPECT_EQ(test, VCAP_AFS_BASE_TYPE, rule->actionset);
+ KUNIT_EXPECT_EQ(test, 6, ri->size);
+ KUNIT_EXPECT_EQ(test, 2, ri->keyset_sw_regs);
+ KUNIT_EXPECT_EQ(test, 4, ri->actionset_sw_regs);
+
+ /* Add rule with write callback */
+ ret = vcap_add_rule(rule);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 792, is2_admin.last_used_addr);
+ for (idx = 0; idx < ARRAY_SIZE(expwriteaddr); ++idx)
+ KUNIT_EXPECT_EQ(test, expwriteaddr[idx], test_updateaddr[idx]);
+
+ /* Check that the rule has been added */
+ ret = list_empty(&is2_admin.rules);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ vcap_free_rule(rule);
+
+ /* Check that the rule has been freed: tricky to access since this
+ * memory should not be accessible anymore
+ */
+ KUNIT_EXPECT_PTR_NE(test, NULL, rule);
+ ret = list_empty(&rule->keyfields);
+ KUNIT_EXPECT_EQ(test, true, ret);
+ ret = list_empty(&rule->actionfields);
+ KUNIT_EXPECT_EQ(test, true, ret);
+}
+
+static void vcap_api_next_lookup_basic_test(struct kunit *test)
+{
+ struct vcap_admin admin1 = {
+ .vtype = VCAP_TYPE_IS2,
+ .vinst = 0,
+ .first_cid = 8000000,
+ .last_cid = 8199999,
+ .lookups = 4,
+ .lookups_per_instance = 2,
+ };
+ struct vcap_admin admin2 = {
+ .vtype = VCAP_TYPE_IS2,
+ .vinst = 1,
+ .first_cid = 8200000,
+ .last_cid = 8399999,
+ .lookups = 4,
+ .lookups_per_instance = 2,
+ };
+ bool ret;
+
+ vcap_test_api_init(&admin1);
+ list_add_tail(&admin2.list, &test_vctrl.list);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8000000, 1001000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8101000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8201000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8201000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8301000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+}
+
+static void vcap_api_next_lookup_advanced_test(struct kunit *test)
+{
+ struct vcap_admin admin1 = {
+ .vtype = VCAP_TYPE_IS0,
+ .vinst = 0,
+ .first_cid = 1000000,
+ .last_cid = 1199999,
+ .lookups = 6,
+ .lookups_per_instance = 2,
+ };
+ struct vcap_admin admin2 = {
+ .vtype = VCAP_TYPE_IS0,
+ .vinst = 1,
+ .first_cid = 1200000,
+ .last_cid = 1399999,
+ .lookups = 6,
+ .lookups_per_instance = 2,
+ };
+ struct vcap_admin admin3 = {
+ .vtype = VCAP_TYPE_IS0,
+ .vinst = 2,
+ .first_cid = 1400000,
+ .last_cid = 1599999,
+ .lookups = 6,
+ .lookups_per_instance = 2,
+ };
+ struct vcap_admin admin4 = {
+ .vtype = VCAP_TYPE_IS2,
+ .vinst = 0,
+ .first_cid = 8000000,
+ .last_cid = 8199999,
+ .lookups = 4,
+ .lookups_per_instance = 2,
+ };
+ struct vcap_admin admin5 = {
+ .vtype = VCAP_TYPE_IS2,
+ .vinst = 1,
+ .first_cid = 8200000,
+ .last_cid = 8399999,
+ .lookups = 4,
+ .lookups_per_instance = 2,
+ };
+ bool ret;
+
+ vcap_test_api_init(&admin1);
+ list_add_tail(&admin2.list, &test_vctrl.list);
+ list_add_tail(&admin3.list, &test_vctrl.list);
+ list_add_tail(&admin4.list, &test_vctrl.list);
+ list_add_tail(&admin5.list, &test_vctrl.list);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1001000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1101000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1201000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1301000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1100000, 8101000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1300000, 1401000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1400000, 1501000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 1500000, 8001000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+
+ ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000);
+ KUNIT_EXPECT_EQ(test, false, ret);
+ ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000);
+ KUNIT_EXPECT_EQ(test, true, ret);
+}
+
+static struct kunit_case vcap_api_support_test_cases[] = {
+ KUNIT_CASE(vcap_api_next_lookup_basic_test),
+ KUNIT_CASE(vcap_api_next_lookup_advanced_test),
+ {}
+};
+
+static struct kunit_suite vcap_api_support_test_suite = {
+ .name = "VCAP_API_Support_Testsuite",
+ .test_cases = vcap_api_support_test_cases,
+};
+
+static struct kunit_case vcap_api_full_rule_test_cases[] = {
+ KUNIT_CASE(vcap_api_rule_find_keyset_basic_test),
+ KUNIT_CASE(vcap_api_rule_find_keyset_failed_test),
+ KUNIT_CASE(vcap_api_rule_find_keyset_many_test),
+ KUNIT_CASE(vcap_api_encode_rule_test),
+ {}
+};
+
+static struct kunit_suite vcap_api_full_rule_test_suite = {
+ .name = "VCAP_API_Full_Rule_Testsuite",
+ .test_cases = vcap_api_full_rule_test_cases,
+};
+
+static struct kunit_case vcap_api_rule_value_test_cases[] = {
+ KUNIT_CASE(vcap_api_rule_add_keyvalue_test),
+ KUNIT_CASE(vcap_api_rule_add_actionvalue_test),
+ {}
+};
+
+static struct kunit_suite vcap_api_rule_value_test_suite = {
+ .name = "VCAP_API_Rule_Value_Testsuite",
+ .test_cases = vcap_api_rule_value_test_cases,
+};
+
static struct kunit_case vcap_api_encoding_test_cases[] = {
KUNIT_CASE(vcap_api_set_bit_1_test),
KUNIT_CASE(vcap_api_set_bit_0_test),
@@ -930,4 +1519,7 @@ static struct kunit_suite vcap_api_encoding_test_suite = {
.test_cases = vcap_api_encoding_test_cases,
};
+kunit_test_suite(vcap_api_support_test_suite);
+kunit_test_suite(vcap_api_full_rule_test_suite);
+kunit_test_suite(vcap_api_rule_value_test_suite);
kunit_test_suite(vcap_api_encoding_test_suite);