[PATCH net-next 6/8] net: microchip: sparx5: Add VCAP debugFS key/action support for the VCAP API

From: Steen Hegelund
Date: Wed Nov 16 2022 - 04:01:03 EST


This add support for displaying the keys and actions in a rule.
The keys and action display format will be determined by the size and the
type of the key or action. The longer keys will typically be displayed as a
hexadecimal byte array.

The actionset is not decoded in full as the Sparx5 IS2 only has one
supported action, so this will be added later with other VCAP types.

Signed-off-by: Steen Hegelund <steen.hegelund@xxxxxxxxxxxxx>
---
.../net/ethernet/microchip/vcap/vcap_api.c | 12 +-
.../microchip/vcap/vcap_api_debugfs.c | 311 +++++++++++++++++-
.../microchip/vcap/vcap_api_private.h | 16 +
support | 1 +
4 files changed, 330 insertions(+), 10 deletions(-)
create mode 160000 support

diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index 3da714e9639c..3415605350c9 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -316,7 +316,7 @@ static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri)
}

/* Return the list of actionfields for the actionset */
-static const struct vcap_field *
+const struct vcap_field *
vcap_actionfields(struct vcap_control *vctrl,
enum vcap_type vt, enum vcap_actionfield_set actionset)
{
@@ -326,7 +326,7 @@ vcap_actionfields(struct vcap_control *vctrl,
return vctrl->vcaps[vt].actionfield_set_map[actionset];
}

-static const struct vcap_set *
+const struct vcap_set *
vcap_actionfieldset(struct vcap_control *vctrl,
enum vcap_type vt, enum vcap_actionfield_set actionset)
{
@@ -342,7 +342,7 @@ vcap_actionfieldset(struct vcap_control *vctrl,
}

/* Return the typegroup table for the matching actionset (using subword size) */
-static const struct vcap_typegroup *
+const struct vcap_typegroup *
vcap_actionfield_typegroup(struct vcap_control *vctrl,
enum vcap_type vt, enum vcap_actionfield_set actionset)
{
@@ -355,9 +355,9 @@ vcap_actionfield_typegroup(struct vcap_control *vctrl,
}

/* Return the number of actionfields in the actionset */
-static int vcap_actionfield_count(struct vcap_control *vctrl,
- enum vcap_type vt,
- enum vcap_actionfield_set actionset)
+int vcap_actionfield_count(struct vcap_control *vctrl,
+ enum vcap_type vt,
+ enum vcap_actionfield_set actionset)
{
/* Check that the actionset exists in the vcap actionset list */
if (actionset >= vctrl->vcaps[vt].actionfield_set_size)
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
index a2d66a36db1c..8e5b1cc24d80 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
@@ -120,6 +120,27 @@ static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl,
return -EINVAL;
}

+/* Find the subword width of the action typegroup that matches the stream data */
+static int vcap_find_actionstream_typegroup_sw(struct vcap_control *vctrl,
+ enum vcap_type vt, u32 *stream,
+ int sw_max)
+{
+ const struct vcap_typegroup **tgt;
+ int sw_idx, res;
+
+ tgt = vctrl->vcaps[vt].actionfield_set_typegroups;
+ /* Try the longest subword match first */
+ for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) {
+ if (!tgt[sw_idx])
+ continue;
+ res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].act_width,
+ tgt[sw_idx], false, sw_max);
+ if (res == 0)
+ return sw_idx;
+ }
+ return -EINVAL;
+}
+
/* Verify that the type id in the stream matches the type id of the keyset */
static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl,
enum vcap_type vt,
@@ -205,6 +226,72 @@ vcap_keyfield_set vcap_find_keystream_keyset(struct vcap_control *vctrl,
return -EINVAL;
}

+/* Verify that the type id in the stream matches the type id of the actionset */
+static bool vcap_verify_actionstream_actionset(struct vcap_control *vctrl,
+ enum vcap_type vt,
+ u32 *actionstream,
+ enum vcap_actionfield_set actionset)
+{
+ const struct vcap_typegroup *tgt;
+ const struct vcap_field *fields;
+ const struct vcap_set *info;
+
+ if (vcap_actionfield_count(vctrl, vt, actionset) == 0)
+ return false;
+
+ info = vcap_actionfieldset(vctrl, vt, actionset);
+ /* Check that the actionset is valid */
+ if (!info)
+ return false;
+
+ /* a type_id of value -1 means that there is no type field */
+ if (info->type_id == (u8)-1)
+ return true;
+
+ /* Get a valid typegroup for the specific actionset */
+ tgt = vcap_actionfield_typegroup(vctrl, vt, actionset);
+ if (!tgt)
+ return false;
+
+ fields = vcap_actionfields(vctrl, vt, actionset);
+ if (!fields)
+ return false;
+
+ /* Later this will be expanded with a check of the type id */
+ return true;
+}
+
+/* Verify that the typegroup information, subword count, actionset and type id
+ * are in sync and correct, return the actionset
+ */
+static enum
+vcap_actionfield_set vcap_find_actionstream_actionset(struct vcap_control *vctrl,
+ enum vcap_type vt,
+ u32 *stream,
+ int sw_max)
+{
+ const struct vcap_set *actionfield_set;
+ int sw_count, idx;
+ bool res;
+
+ sw_count = vcap_find_actionstream_typegroup_sw(vctrl, vt, stream,
+ sw_max);
+ if (sw_count < 0)
+ return sw_count;
+
+ actionfield_set = vctrl->vcaps[vt].actionfield_set;
+ for (idx = 0; idx < vctrl->vcaps[vt].actionfield_set_size; ++idx) {
+ if (actionfield_set[idx].sw_per_item != sw_count)
+ continue;
+
+ res = vcap_verify_actionstream_actionset(vctrl, vt,
+ stream, idx);
+ if (res)
+ return idx;
+ }
+ return -EINVAL;
+}
+
/* Read key data from a VCAP address and discover if there is a rule keyset here */
static int vcap_addr_keyset(struct vcap_control *vctrl,
struct net_device *ndev,
@@ -263,6 +350,224 @@ static int vcap_read_rule(struct vcap_rule_internal *ri)
return 0;
}

+/* Dump the keyfields value and mask values */
+static void vcap_debugfs_show_rule_keyfield(struct vcap_control *vctrl,
+ struct vcap_output_print *out,
+ enum vcap_key_field key,
+ const struct vcap_field *keyfield,
+ u8 *value, u8 *mask)
+{
+ bool hex = false;
+ int idx, bytes;
+
+ out->prf(out->dst, " %s: W%d: ", vcap_keyfield_name(vctrl, key),
+ keyfield[key].width);
+
+ switch (keyfield[key].type) {
+ case VCAP_FIELD_BIT:
+ out->prf(out->dst, "%d/%d", value[0], mask[0]);
+ break;
+ case VCAP_FIELD_U32:
+ if (key == VCAP_KF_L3_IP4_SIP || key == VCAP_KF_L3_IP4_DIP) {
+ out->prf(out->dst, "%pI4h/%pI4h", value, mask);
+ } else if (key == VCAP_KF_ETYPE ||
+ key == VCAP_KF_IF_IGR_PORT_MASK) {
+ hex = true;
+ } else {
+ u32 fmsk = (1 << keyfield[key].width) - 1;
+ u32 val = *(u32 *)value;
+ u32 msk = *(u32 *)mask;
+
+ out->prf(out->dst, "%u/%u", val & fmsk, msk & fmsk);
+ }
+ break;
+ case VCAP_FIELD_U48:
+ if (key == VCAP_KF_L2_SMAC || key == VCAP_KF_L2_DMAC)
+ out->prf(out->dst, "%pMR/%pMR", value, mask);
+ else
+ hex = true;
+ break;
+ case VCAP_FIELD_U56:
+ case VCAP_FIELD_U64:
+ case VCAP_FIELD_U72:
+ case VCAP_FIELD_U112:
+ hex = true;
+ break;
+ case VCAP_FIELD_U128:
+ if (key == VCAP_KF_L3_IP6_SIP || key == VCAP_KF_L3_IP6_DIP) {
+ u8 nvalue[16], nmask[16];
+
+ vcap_netbytes_copy(nvalue, value, sizeof(nvalue));
+ vcap_netbytes_copy(nmask, mask, sizeof(nmask));
+ out->prf(out->dst, "%pI6/%pI6", nvalue, nmask);
+ } else {
+ hex = true;
+ }
+ break;
+ }
+ if (hex) {
+ bytes = DIV_ROUND_UP(keyfield[key].width, BITS_PER_BYTE);
+ out->prf(out->dst, "0x");
+ for (idx = 0; idx < bytes; ++idx)
+ out->prf(out->dst, "%02x", value[bytes - idx - 1]);
+ out->prf(out->dst, "/0x");
+ for (idx = 0; idx < bytes; ++idx)
+ out->prf(out->dst, "%02x", mask[bytes - idx - 1]);
+ }
+ out->prf(out->dst, "\n");
+}
+
+static void
+vcap_debugfs_show_rule_actionfield(struct vcap_control *vctrl,
+ struct vcap_output_print *out,
+ enum vcap_action_field action,
+ const struct vcap_field *actionfield,
+ u8 *value)
+{
+ bool hex = false;
+ int idx, bytes;
+ u32 fmsk, val;
+
+ out->prf(out->dst, " %s: W%d: ",
+ vcap_actionfield_name(vctrl, action),
+ actionfield[action].width);
+
+ switch (actionfield[action].type) {
+ case VCAP_FIELD_BIT:
+ out->prf(out->dst, "%d", value[0]);
+ break;
+ case VCAP_FIELD_U32:
+ fmsk = (1 << actionfield[action].width) - 1;
+ val = *(u32 *)value;
+ out->prf(out->dst, "%u", val & fmsk);
+ break;
+ case VCAP_FIELD_U48:
+ case VCAP_FIELD_U56:
+ case VCAP_FIELD_U64:
+ case VCAP_FIELD_U72:
+ case VCAP_FIELD_U112:
+ case VCAP_FIELD_U128:
+ hex = true;
+ break;
+ }
+ if (hex) {
+ bytes = DIV_ROUND_UP(actionfield[action].width, BITS_PER_BYTE);
+ out->prf(out->dst, "0x");
+ for (idx = 0; idx < bytes; ++idx)
+ out->prf(out->dst, "%02x", value[bytes - idx - 1]);
+ }
+ out->prf(out->dst, "\n");
+}
+
+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_stream_iter kiter, miter;
+ struct vcap_admin *admin = ri->admin;
+ const struct vcap_field *keyfield;
+ enum vcap_type vt = admin->vtype;
+ const struct vcap_typegroup *tgt;
+ enum vcap_keyfield_set keyset;
+ int idx, res, keyfield_count;
+ u32 *maskstream;
+ u32 *keystream;
+ u8 value[16];
+ u8 mask[16];
+
+ keystream = admin->cache.keystream;
+ maskstream = admin->cache.maskstream;
+ res = vcap_find_keystream_keyset(vctrl, vt, keystream, maskstream,
+ false, 0);
+ if (res < 0) {
+ pr_err("%s:%d: could not find valid keyset: %d\n",
+ __func__, __LINE__, res);
+ return -EINVAL;
+ }
+ keyset = res;
+ out->prf(out->dst, " keyset: %s\n",
+ vcap_keyset_name(vctrl, ri->data.keyset));
+ out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw);
+ out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs);
+ keyfield_count = vcap_keyfield_count(vctrl, vt, keyset);
+ keyfield = vcap_keyfields(vctrl, vt, keyset);
+ tgt = vcap_keyfield_typegroup(vctrl, vt, keyset);
+ /* Start decoding the streams */
+ for (idx = 0; idx < keyfield_count; ++idx) {
+ if (keyfield[idx].width <= 0)
+ continue;
+ /* First get the mask */
+ memset(mask, 0, DIV_ROUND_UP(keyfield[idx].width, 8));
+ vcap_iter_init(&miter, vctrl->vcaps[vt].sw_width, tgt,
+ keyfield[idx].offset);
+ vcap_decode_field(maskstream, &miter, keyfield[idx].width,
+ mask);
+ /* Skip if no mask bits are set */
+ if (vcap_bitarray_zero(keyfield[idx].width, mask))
+ continue;
+ /* Get the key */
+ memset(value, 0, DIV_ROUND_UP(keyfield[idx].width, 8));
+ vcap_iter_init(&kiter, vctrl->vcaps[vt].sw_width, tgt,
+ keyfield[idx].offset);
+ vcap_decode_field(keystream, &kiter, keyfield[idx].width,
+ value);
+ vcap_debugfs_show_rule_keyfield(vctrl, out, idx, keyfield,
+ value, mask);
+ }
+ return 0;
+}
+
+static int vcap_debugfs_show_rule_actionset(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 *actionfield;
+ enum vcap_actionfield_set actionset;
+ enum vcap_type vt = admin->vtype;
+ const struct vcap_typegroup *tgt;
+ struct vcap_stream_iter iter;
+ int idx, res, actfield_count;
+ u32 *actstream;
+ u8 value[16];
+ bool no_bits;
+
+ actstream = admin->cache.actionstream;
+ res = vcap_find_actionstream_actionset(vctrl, vt, actstream, 0);
+ if (res < 0) {
+ pr_err("%s:%d: could not find valid actionset: %d\n",
+ __func__, __LINE__, res);
+ return -EINVAL;
+ }
+ actionset = res;
+ out->prf(out->dst, " actionset: %s\n",
+ vcap_actionset_name(vctrl, ri->data.actionset));
+ out->prf(out->dst, " actionset_sw: %d\n", ri->actionset_sw);
+ out->prf(out->dst, " actionset_sw_regs: %d\n", ri->actionset_sw_regs);
+ actfield_count = vcap_actionfield_count(vctrl, vt, actionset);
+ actionfield = vcap_actionfields(vctrl, vt, actionset);
+ tgt = vcap_actionfield_typegroup(vctrl, vt, actionset);
+ /* Start decoding the stream */
+ for (idx = 0; idx < actfield_count; ++idx) {
+ if (actionfield[idx].width <= 0)
+ continue;
+ /* Get the action */
+ memset(value, 0, DIV_ROUND_UP(actionfield[idx].width, 8));
+ vcap_iter_init(&iter, vctrl->vcaps[vt].act_width, tgt,
+ actionfield[idx].offset);
+ vcap_decode_field(actstream, &iter, actionfield[idx].width,
+ value);
+ /* Skip if no bits are set */
+ no_bits = vcap_bitarray_zero(actionfield[idx].width, value);
+ if (no_bits)
+ continue;
+ /* Later the action id will also be checked */
+ vcap_debugfs_show_rule_actionfield(vctrl, out, idx, actionfield,
+ value);
+ }
+ return 0;
+}
+
static void vcap_show_admin_rule(struct vcap_control *vctrl,
struct vcap_admin *admin,
struct vcap_output_print *out,
@@ -276,10 +581,8 @@ 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, " keyset: %s\n",
- vcap_keyset_name(vctrl, ri->data.keyset));
- out->prf(out->dst, " actionset: %s\n",
- vcap_actionset_name(vctrl, ri->data.actionset));
+ vcap_debugfs_show_rule_keyset(ri, out);
+ vcap_debugfs_show_rule_actionset(ri, out);
}

static void vcap_show_admin_info(struct vcap_control *vctrl,
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
index b13e1c000c30..c447da2da683 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
@@ -77,6 +77,22 @@ const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl,

/* Actionset and actionfield functionality */

+/* Return the actionset information for the actionset */
+const struct vcap_set *
+vcap_actionfieldset(struct vcap_control *vctrl,
+ enum vcap_type vt, enum vcap_actionfield_set actionset);
+/* Return the number of actionfields in the actionset */
+int vcap_actionfield_count(struct vcap_control *vctrl,
+ enum vcap_type vt,
+ enum vcap_actionfield_set actionset);
+/* Return the typegroup table for the matching actionset (using subword size) */
+const struct vcap_typegroup *
+vcap_actionfield_typegroup(struct vcap_control *vctrl,
+ enum vcap_type vt, enum vcap_actionfield_set actionset);
+/* Return the list of actionfields for the actionset */
+const struct vcap_field *
+vcap_actionfields(struct vcap_control *vctrl,
+ enum vcap_type vt, enum vcap_actionfield_set actionset);
/* Map actionset id to a string with the actionset name */
const char *vcap_actionset_name(struct vcap_control *vctrl,
enum vcap_actionfield_set actionset);
diff --git a/support b/support
new file mode 160000
index 000000000000..fcb2abd961e8
--- /dev/null
+++ b/support
@@ -0,0 +1 @@
+Subproject commit fcb2abd961e884577b51e677216a3cf84a870fca
--
2.38.1