Commit 87b08a8c authored by David S. Miller's avatar David S. Miller

Merge branch 'net-microchip-vcap-rules'

Steen Hegelund says:

====================
net: microchip: Add support for two classes of VCAP rules

This adds support for two classes of VCAP rules:

- Permanent rules (added e.g. for PTP support)
- TC user rules (added by the TC userspace tool)

For this to work the VCAP Loopups must be enabled from boot, so that the
"internal" clients like PTP can add rules that are always active.

When the TC tool add a flower filter the VCAP rule corresponding to this
filter will be disabled (kept in memory) until a TC matchall filter creates
a link from chain 0 to the chain (lookup) where the flower filter was
added.

When the flower filter is enabled it will be written to the appropriate
VCAP lookup and become active in HW.

Likewise the flower filter will be disabled if there is no link from chain
0 to the chain of the filter (lookup), and when that happens the
corresponding VCAP rule will be read from the VCAP instance and stored in
memory until it is deleted or enabled again.

Version History:
================
v4      Removed a leftover 'Fixes' tag from v2.  No functional changes.

v3      Removed the change that allowed rules to always be added in the
        LAN996x even though the lookups are not enabled (Horatiu Vultur).
        This was sent separately to net instead.

        Removed the 'Fixes' tags due to the patch sent to net by Horatiu
        Vultur.

        Added a check for validity of the chain source when enabling a
        lookup.

v2      Adding a missing goto exit in vcap_add_rule (Dan Carpenter).
        Added missing checks for error returns in vcap_enable_rule.

v1      Initial version
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ce870af3 18a15c76
......@@ -4,7 +4,7 @@
#include "vcap_api_client.h"
int lan966x_goto_port_add(struct lan966x_port *port,
struct flow_action_entry *act,
int from_cid, int to_cid,
unsigned long goto_id,
struct netlink_ext_ack *extack)
{
......@@ -12,7 +12,7 @@ int lan966x_goto_port_add(struct lan966x_port *port,
int err;
err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev,
act->chain_index, goto_id,
from_cid, to_cid, goto_id,
true);
if (err == -EFAULT) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported goto chain");
......@@ -29,8 +29,6 @@ int lan966x_goto_port_add(struct lan966x_port *port,
return err;
}
port->tc.goto_id = goto_id;
return 0;
}
......@@ -41,14 +39,12 @@ int lan966x_goto_port_del(struct lan966x_port *port,
struct lan966x *lan966x = port->lan966x;
int err;
err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, 0,
err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, 0, 0,
goto_id, false);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not disable VCAP lookups");
return err;
}
port->tc.goto_id = 0;
return 0;
}
......@@ -332,7 +332,6 @@ struct lan966x_port_tc {
unsigned long police_id;
unsigned long ingress_mirror_id;
unsigned long egress_mirror_id;
unsigned long goto_id;
struct flow_stats police_stat;
struct flow_stats mirror_stat;
};
......@@ -607,7 +606,7 @@ int lan966x_tc_flower(struct lan966x_port *port,
struct flow_cls_offload *f);
int lan966x_goto_port_add(struct lan966x_port *port,
struct flow_action_entry *act,
int from_cid, int to_cid,
unsigned long goto_id,
struct netlink_ext_ack *extack);
int lan966x_goto_port_del(struct lan966x_port *port,
......
......@@ -82,8 +82,8 @@ static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
}
static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
struct flow_cls_offload *fco,
struct vcap_admin *admin)
struct net_device *dev,
struct flow_cls_offload *fco)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
struct flow_action_entry *actent, *last_actent = NULL;
......@@ -109,21 +109,23 @@ static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
last_actent = actent; /* Save last action for later check */
}
/* Check that last action is a goto */
if (last_actent->id != FLOW_ACTION_GOTO) {
/* Check that last action is a goto
* The last chain/lookup does not need to have goto action
*/
if (last_actent->id == FLOW_ACTION_GOTO) {
/* Check if the destination chain is in one of the VCAPs */
if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
last_actent->chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid goto chain");
return -EINVAL;
}
} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Last action must be 'goto'");
return -EINVAL;
}
/* Check if the goto chain is in the next lookup */
if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
last_actent->chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid goto chain");
return -EINVAL;
}
/* Catch unsupported combinations of actions */
if (action_mask & BIT(FLOW_ACTION_TRAP) &&
action_mask & BIT(FLOW_ACTION_ACCEPT)) {
......@@ -145,8 +147,8 @@ static int lan966x_tc_flower_add(struct lan966x_port *port,
struct vcap_rule *vrule;
int err, idx;
err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f,
admin);
err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,
port->dev, f);
if (err)
return err;
......
......@@ -24,7 +24,8 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port,
return lan966x_mirror_port_add(port, act, f->cookie,
ingress, f->common.extack);
case FLOW_ACTION_GOTO:
return lan966x_goto_port_add(port, act, f->cookie,
return lan966x_goto_port_add(port, f->common.chain_index,
act->chain_index, f->cookie,
f->common.extack);
default:
NL_SET_ERR_MSG_MOD(f->common.extack,
......@@ -46,13 +47,8 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port,
f->cookie == port->tc.egress_mirror_id) {
return lan966x_mirror_port_del(port, ingress,
f->common.extack);
} else if (f->cookie == port->tc.goto_id) {
return lan966x_goto_port_del(port, f->cookie,
f->common.extack);
} else {
NL_SET_ERR_MSG_MOD(f->common.extack,
"Unsupported action");
return -EOPNOTSUPP;
return lan966x_goto_port_del(port, f->cookie, f->common.extack);
}
return 0;
......@@ -80,12 +76,6 @@ int lan966x_tc_matchall(struct lan966x_port *port,
struct tc_cls_matchall_offload *f,
bool ingress)
{
if (!tc_cls_can_offload_and_chain0(port->dev, &f->common)) {
NL_SET_ERR_MSG_MOD(f->common.extack,
"Only chain zero is supported");
return -EOPNOTSUPP;
}
switch (f->command) {
case TC_CLSMATCHALL_REPLACE:
return lan966x_tc_matchall_add(port, f, ingress);
......
......@@ -390,20 +390,6 @@ static int lan966x_vcap_port_info(struct net_device *dev,
return 0;
}
static int lan966x_vcap_enable(struct net_device *dev,
struct vcap_admin *admin,
bool enable)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(enable),
ANA_VCAP_S2_CFG_ENA,
lan966x, ANA_VCAP_S2_CFG(port->chip_port));
return 0;
}
static struct vcap_operations lan966x_vcap_ops = {
.validate_keyset = lan966x_vcap_validate_keyset,
.add_default_fields = lan966x_vcap_add_default_fields,
......@@ -414,7 +400,6 @@ static struct vcap_operations lan966x_vcap_ops = {
.update = lan966x_vcap_update,
.move = lan966x_vcap_move,
.port_info = lan966x_vcap_port_info,
.enable = lan966x_vcap_enable,
};
static void lan966x_vcap_admin_free(struct vcap_admin *admin)
......@@ -521,6 +506,12 @@ int lan966x_vcap_init(struct lan966x *lan966x)
list_add_tail(&admin->list, &ctrl->list);
}
for (int p = 0; p < lan966x->num_phys_ports; ++p)
if (lan966x->ports[p])
lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(true),
ANA_VCAP_S2_CFG_ENA, lan966x,
ANA_VCAP_S2_CFG(lan966x->ports[p]->chip_port));
lan966x->vcap_ctrl = ctrl;
return 0;
......
......@@ -573,8 +573,8 @@ static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
}
static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
struct flow_cls_offload *fco,
struct vcap_admin *admin)
struct net_device *ndev,
struct flow_cls_offload *fco)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
struct flow_action_entry *actent, *last_actent = NULL;
......@@ -600,21 +600,23 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
last_actent = actent; /* Save last action for later check */
}
/* Check that last action is a goto */
if (last_actent->id != FLOW_ACTION_GOTO) {
/* Check if last action is a goto
* The last chain/lookup does not need to have a goto action
*/
if (last_actent->id == FLOW_ACTION_GOTO) {
/* Check if the destination chain is in one of the VCAPs */
if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
last_actent->chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid goto chain");
return -EINVAL;
}
} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Last action must be 'goto'");
return -EINVAL;
}
/* Check if the goto chain is in the next lookup */
if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
last_actent->chain_index)) {
NL_SET_ERR_MSG_MOD(fco->common.extack,
"Invalid goto chain");
return -EINVAL;
}
/* Catch unsupported combinations of actions */
if (action_mask & BIT(FLOW_ACTION_TRAP) &&
action_mask & BIT(FLOW_ACTION_ACCEPT)) {
......@@ -833,7 +835,7 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
vctrl = port->sparx5->vcap_ctrl;
err = sparx5_tc_flower_action_check(vctrl, fco, admin);
err = sparx5_tc_flower_action_check(vctrl, ndev, fco);
if (err)
return err;
......
......@@ -31,6 +31,7 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
switch (action->id) {
case FLOW_ACTION_GOTO:
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
tmo->common.chain_index,
action->chain_index, tmo->cookie,
true);
if (err == -EFAULT) {
......@@ -43,6 +44,11 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
"VCAP already enabled");
return -EOPNOTSUPP;
}
if (err == -EADDRNOTAVAIL) {
NL_SET_ERR_MSG_MOD(tmo->common.extack,
"Already matching this chain");
return -EOPNOTSUPP;
}
if (err) {
NL_SET_ERR_MSG_MOD(tmo->common.extack,
"Could not enable VCAP lookups");
......@@ -66,8 +72,8 @@ static int sparx5_tc_matchall_destroy(struct net_device *ndev,
sparx5 = port->sparx5;
if (!tmo->rule && tmo->cookie) {
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 0,
tmo->cookie, false);
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
0, 0, tmo->cookie, false);
if (err)
return err;
return 0;
......@@ -80,12 +86,6 @@ int sparx5_tc_matchall(struct net_device *ndev,
struct tc_cls_matchall_offload *tmo,
bool ingress)
{
if (!tc_cls_can_offload_and_chain0(ndev, &tmo->common)) {
NL_SET_ERR_MSG_MOD(tmo->common.extack,
"Only chain zero is supported");
return -EOPNOTSUPP;
}
switch (tmo->command) {
case TC_CLSMATCHALL_REPLACE:
return sparx5_tc_matchall_replace(ndev, tmo, ingress);
......
......@@ -29,7 +29,7 @@ static void sparx5_vcap_port_keys(struct sparx5 *sparx5,
/* Get lookup state */
value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_CFG(port->portno));
out->prf(out->dst, "\n state: ");
if (ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(value))
if (ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(value) & BIT(lookup))
out->prf(out->dst, "on");
else
out->prf(out->dst, "off");
......
......@@ -510,28 +510,6 @@ static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin,
sparx5_vcap_wait_super_update(sparx5);
}
/* Enable all lookups in the VCAP instance */
static int sparx5_vcap_enable(struct net_device *ndev,
struct vcap_admin *admin,
bool enable)
{
struct sparx5_port *port = netdev_priv(ndev);
struct sparx5 *sparx5;
int portno;
sparx5 = port->sparx5;
portno = port->portno;
/* For now we only consider IS2 */
if (enable)
spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5,
ANA_ACL_VCAP_S2_CFG(portno));
else
spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), sparx5,
ANA_ACL_VCAP_S2_CFG(portno));
return 0;
}
/* API callback operations: only IS2 is supported for now */
static struct vcap_operations sparx5_vcap_ops = {
.validate_keyset = sparx5_vcap_validate_keyset,
......@@ -543,7 +521,6 @@ static struct vcap_operations sparx5_vcap_ops = {
.update = sparx5_vcap_update,
.move = sparx5_vcap_move,
.port_info = sparx5_port_info,
.enable = sparx5_vcap_enable,
};
/* Enable lookups per port and set the keyset generation: only IS2 for now */
......@@ -568,6 +545,12 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5,
ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup));
}
}
/* IS2 lookups are in bit 0:3 */
for (portno = 0; portno < SPX5_PORTS; ++portno)
spx5_rmw(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf),
ANA_ACL_VCAP_S2_CFG_SEC_ENA,
sparx5,
ANA_ACL_VCAP_S2_CFG(portno));
}
/* Disable lookups per port and set the keyset generation: only IS2 for now */
......
......@@ -37,11 +37,13 @@ struct vcap_rule_move {
int count; /* blocksize of addresses to move */
};
/* Stores the filter cookie that enabled the port */
/* Stores the filter cookie and chain id 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 */
int src_cid; /* source chain id */
int dst_cid; /* destination chain id */
};
void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width,
......@@ -508,10 +510,133 @@ static void vcap_encode_keyfield_typegroups(struct vcap_control *vctrl,
vcap_encode_typegroups(cache->maskstream, sw_width, tgt, true);
}
/* Copy data from src to dst but reverse the data in chunks of 32bits.
* For example if src is 00:11:22:33:44:55 where 55 is LSB the dst will
* have the value 22:33:44:55:00:11.
*/
static void vcap_copy_to_w32be(u8 *dst, const u8 *src, int size)
{
for (int idx = 0; idx < size; ++idx) {
int first_byte_index = 0;
int nidx;
first_byte_index = size - (((idx >> 2) + 1) << 2);
if (first_byte_index < 0)
first_byte_index = 0;
nidx = idx + first_byte_index - (idx & ~0x3);
dst[nidx] = src[idx];
}
}
static void
vcap_copy_from_client_keyfield(struct vcap_rule *rule,
struct vcap_client_keyfield *dst,
const struct vcap_client_keyfield *src)
{
struct vcap_rule_internal *ri = to_intrule(rule);
const struct vcap_client_keyfield_data *sdata;
struct vcap_client_keyfield_data *ddata;
int size;
dst->ctrl.type = src->ctrl.type;
dst->ctrl.key = src->ctrl.key;
INIT_LIST_HEAD(&dst->ctrl.list);
sdata = &src->data;
ddata = &dst->data;
if (!ri->admin->w32be) {
memcpy(ddata, sdata, sizeof(dst->data));
return;
}
size = keyfield_size_table[dst->ctrl.type] / 2;
switch (dst->ctrl.type) {
case VCAP_FIELD_BIT:
case VCAP_FIELD_U32:
memcpy(ddata, sdata, sizeof(dst->data));
break;
case VCAP_FIELD_U48:
vcap_copy_to_w32be(ddata->u48.value, src->data.u48.value, size);
vcap_copy_to_w32be(ddata->u48.mask, src->data.u48.mask, size);
break;
case VCAP_FIELD_U56:
vcap_copy_to_w32be(ddata->u56.value, sdata->u56.value, size);
vcap_copy_to_w32be(ddata->u56.mask, sdata->u56.mask, size);
break;
case VCAP_FIELD_U64:
vcap_copy_to_w32be(ddata->u64.value, sdata->u64.value, size);
vcap_copy_to_w32be(ddata->u64.mask, sdata->u64.mask, size);
break;
case VCAP_FIELD_U72:
vcap_copy_to_w32be(ddata->u72.value, sdata->u72.value, size);
vcap_copy_to_w32be(ddata->u72.mask, sdata->u72.mask, size);
break;
case VCAP_FIELD_U112:
vcap_copy_to_w32be(ddata->u112.value, sdata->u112.value, size);
vcap_copy_to_w32be(ddata->u112.mask, sdata->u112.mask, size);
break;
case VCAP_FIELD_U128:
vcap_copy_to_w32be(ddata->u128.value, sdata->u128.value, size);
vcap_copy_to_w32be(ddata->u128.mask, sdata->u128.mask, size);
break;
}
}
static void
vcap_copy_from_client_actionfield(struct vcap_rule *rule,
struct vcap_client_actionfield *dst,
const struct vcap_client_actionfield *src)
{
struct vcap_rule_internal *ri = to_intrule(rule);
const struct vcap_client_actionfield_data *sdata;
struct vcap_client_actionfield_data *ddata;
int size;
dst->ctrl.type = src->ctrl.type;
dst->ctrl.action = src->ctrl.action;
INIT_LIST_HEAD(&dst->ctrl.list);
sdata = &src->data;
ddata = &dst->data;
if (!ri->admin->w32be) {
memcpy(ddata, sdata, sizeof(dst->data));
return;
}
size = actionfield_size_table[dst->ctrl.type];
switch (dst->ctrl.type) {
case VCAP_FIELD_BIT:
case VCAP_FIELD_U32:
memcpy(ddata, sdata, sizeof(dst->data));
break;
case VCAP_FIELD_U48:
vcap_copy_to_w32be(ddata->u48.value, sdata->u48.value, size);
break;
case VCAP_FIELD_U56:
vcap_copy_to_w32be(ddata->u56.value, sdata->u56.value, size);
break;
case VCAP_FIELD_U64:
vcap_copy_to_w32be(ddata->u64.value, sdata->u64.value, size);
break;
case VCAP_FIELD_U72:
vcap_copy_to_w32be(ddata->u72.value, sdata->u72.value, size);
break;
case VCAP_FIELD_U112:
vcap_copy_to_w32be(ddata->u112.value, sdata->u112.value, size);
break;
case VCAP_FIELD_U128:
vcap_copy_to_w32be(ddata->u128.value, sdata->u128.value, size);
break;
}
}
static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri)
{
const struct vcap_client_keyfield *ckf;
const struct vcap_typegroup *tg_table;
struct vcap_client_keyfield tempkf;
const struct vcap_field *kf_table;
int keyset_size;
......@@ -552,7 +677,9 @@ static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri)
__func__, __LINE__, ckf->ctrl.key);
return -EINVAL;
}
vcap_encode_keyfield(ri, ckf, &kf_table[ckf->ctrl.key], tg_table);
vcap_copy_from_client_keyfield(&ri->data, &tempkf, ckf);
vcap_encode_keyfield(ri, &tempkf, &kf_table[ckf->ctrl.key],
tg_table);
}
/* Add typegroup bits to the key/mask bitstreams */
vcap_encode_keyfield_typegroups(ri->vctrl, ri, tg_table);
......@@ -667,6 +794,7 @@ static int vcap_encode_rule_actionset(struct vcap_rule_internal *ri)
{
const struct vcap_client_actionfield *caf;
const struct vcap_typegroup *tg_table;
struct vcap_client_actionfield tempaf;
const struct vcap_field *af_table;
int actionset_size;
......@@ -707,8 +835,9 @@ static int vcap_encode_rule_actionset(struct vcap_rule_internal *ri)
__func__, __LINE__, caf->ctrl.action);
return -EINVAL;
}
vcap_encode_actionfield(ri, caf, &af_table[caf->ctrl.action],
tg_table);
vcap_copy_from_client_actionfield(&ri->data, &tempaf, caf);
vcap_encode_actionfield(ri, &tempaf,
&af_table[caf->ctrl.action], tg_table);
}
/* Add typegroup bits to the entry bitstreams */
vcap_encode_actionfield_typegroups(ri, tg_table);
......@@ -738,7 +867,7 @@ 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->enable) {
!ctrl->ops->port_info) {
pr_err("%s:%d: client operations are missing\n",
__func__, __LINE__);
return -ENOENT;
......@@ -821,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 */
......@@ -836,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;
}
......@@ -1424,39 +1577,31 @@ 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)
/* Is the next chain id in one of the following lookups
* For now this does not support filters linked to other filters using
* keys and actions. That will be added later.
*/
bool vcap_is_next_lookup(struct vcap_control *vctrl, int src_cid, int dst_cid)
{
struct vcap_admin *admin, *next_admin;
int lookup, next_lookup;
struct vcap_admin *admin;
int next_cid;
/* The offset must be at least one lookup */
if (next_cid < cur_cid + VCAP_CID_LOOKUP_SIZE)
if (vcap_api_check(vctrl))
return false;
if (vcap_api_check(vctrl))
/* The offset must be at least one lookup, round up */
next_cid = src_cid + VCAP_CID_LOOKUP_SIZE;
next_cid /= VCAP_CID_LOOKUP_SIZE;
next_cid *= VCAP_CID_LOOKUP_SIZE;
if (dst_cid < next_cid)
return false;
admin = vcap_find_admin(vctrl, cur_cid);
admin = vcap_find_admin(vctrl, dst_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;
return true;
}
EXPORT_SYMBOL_GPL(vcap_is_next_lookup);
......@@ -1756,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);
......@@ -1770,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);
......@@ -1803,11 +1948,94 @@ static void vcap_move_rules(struct vcap_rule_internal *ri,
move->offset, move->count);
}
/* Check if the chain is already used to enable a VCAP lookup for this port */
static bool vcap_is_chain_used(struct vcap_control *vctrl,
struct net_device *ndev, int src_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->src_cid == src_cid && eport->ndev == ndev)
return true;
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;
if (dst_cid == 0) /* Chain zero is always available */
return true;
/* 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;
}
/* Encode and write a validated rule to the VCAP */
int vcap_add_rule(struct vcap_rule *rule)
{
struct vcap_rule_internal *ri = to_intrule(rule);
struct vcap_rule_move move = {0};
struct vcap_counter ctr = {0};
int ret;
ret = vcap_api_check(ri->vctrl);
......@@ -1815,6 +2043,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",
......@@ -1823,6 +2053,14 @@ 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) {
pr_err("%s:%d: rule encoding error: %d\n", __func__, __LINE__, ret);
......@@ -1830,8 +2068,12 @@ int vcap_add_rule(struct vcap_rule *rule)
}
ret = vcap_write_rule(ri);
if (ret)
if (ret) {
pr_err("%s:%d: rule write error: %d\n", __func__, __LINE__, ret);
goto out;
}
/* Set the counter to zero */
ret = vcap_write_counter(ri, &ctr);
out:
mutex_unlock(&ri->admin->lock);
return ret;
......@@ -1885,7 +2127,6 @@ 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:
......@@ -1930,9 +2171,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);
......@@ -1970,6 +2215,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);
......@@ -2062,7 +2312,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 */
......@@ -2091,7 +2341,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;
......@@ -2137,69 +2387,6 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
}
EXPORT_SYMBOL_GPL(vcap_lookup_keyfield);
/* Copy data from src to dst but reverse the data in chunks of 32bits.
* For example if src is 00:11:22:33:44:55 where 55 is LSB the dst will
* have the value 22:33:44:55:00:11.
*/
static void vcap_copy_to_w32be(u8 *dst, u8 *src, int size)
{
for (int idx = 0; idx < size; ++idx) {
int first_byte_index = 0;
int nidx;
first_byte_index = size - (((idx >> 2) + 1) << 2);
if (first_byte_index < 0)
first_byte_index = 0;
nidx = idx + first_byte_index - (idx & ~0x3);
dst[nidx] = src[idx];
}
}
static void vcap_copy_from_client_keyfield(struct vcap_rule *rule,
struct vcap_client_keyfield *field,
struct vcap_client_keyfield_data *data)
{
struct vcap_rule_internal *ri = to_intrule(rule);
int size;
if (!ri->admin->w32be) {
memcpy(&field->data, data, sizeof(field->data));
return;
}
size = keyfield_size_table[field->ctrl.type] / 2;
switch (field->ctrl.type) {
case VCAP_FIELD_BIT:
case VCAP_FIELD_U32:
memcpy(&field->data, data, sizeof(field->data));
break;
case VCAP_FIELD_U48:
vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size);
vcap_copy_to_w32be(field->data.u48.mask, data->u48.mask, size);
break;
case VCAP_FIELD_U56:
vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size);
vcap_copy_to_w32be(field->data.u56.mask, data->u56.mask, size);
break;
case VCAP_FIELD_U64:
vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size);
vcap_copy_to_w32be(field->data.u64.mask, data->u64.mask, size);
break;
case VCAP_FIELD_U72:
vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size);
vcap_copy_to_w32be(field->data.u72.mask, data->u72.mask, size);
break;
case VCAP_FIELD_U112:
vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size);
vcap_copy_to_w32be(field->data.u112.mask, data->u112.mask, size);
break;
case VCAP_FIELD_U128:
vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size);
vcap_copy_to_w32be(field->data.u128.mask, data->u128.mask, size);
break;
}
}
/* Check if the keyfield is already in the rule */
static bool vcap_keyfield_unique(struct vcap_rule *rule,
enum vcap_key_field key)
......@@ -2257,9 +2444,9 @@ static int vcap_rule_add_key(struct vcap_rule *rule,
field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field)
return -ENOMEM;
memcpy(&field->data, data, sizeof(field->data));
field->ctrl.key = key;
field->ctrl.type = ftype;
vcap_copy_from_client_keyfield(rule, field, data);
list_add_tail(&field->ctrl.list, &rule->keyfields);
return 0;
}
......@@ -2367,45 +2554,6 @@ vcap_find_actionfield(struct vcap_rule *rule, enum vcap_action_field act)
return NULL;
}
static void vcap_copy_from_client_actionfield(struct vcap_rule *rule,
struct vcap_client_actionfield *field,
struct vcap_client_actionfield_data *data)
{
struct vcap_rule_internal *ri = to_intrule(rule);
int size;
if (!ri->admin->w32be) {
memcpy(&field->data, data, sizeof(field->data));
return;
}
size = actionfield_size_table[field->ctrl.type];
switch (field->ctrl.type) {
case VCAP_FIELD_BIT:
case VCAP_FIELD_U32:
memcpy(&field->data, data, sizeof(field->data));
break;
case VCAP_FIELD_U48:
vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size);
break;
case VCAP_FIELD_U56:
vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size);
break;
case VCAP_FIELD_U64:
vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size);
break;
case VCAP_FIELD_U72:
vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size);
break;
case VCAP_FIELD_U112:
vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size);
break;
case VCAP_FIELD_U128:
vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size);
break;
}
}
/* Check if the actionfield is already in the rule */
static bool vcap_actionfield_unique(struct vcap_rule *rule,
enum vcap_action_field act)
......@@ -2463,9 +2611,9 @@ static int vcap_rule_add_action(struct vcap_rule *rule,
field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field)
return -ENOMEM;
memcpy(&field->data, data, sizeof(field->data));
field->ctrl.action = action;
field->ctrl.type = ftype;
vcap_copy_from_client_actionfield(rule, field, data);
list_add_tail(&field->ctrl.list, &rule->actionfields);
return 0;
}
......@@ -2564,24 +2712,153 @@ void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule)
}
EXPORT_SYMBOL_GPL(vcap_set_tc_exterr);
/* Write a rule to VCAP HW to enable it */
static int vcap_enable_rule(struct vcap_rule_internal *ri)
{
struct vcap_client_actionfield *af, *naf;
struct vcap_client_keyfield *kf, *nkf;
int err;
vcap_erase_cache(ri);
err = vcap_encode_rule(ri);
if (err)
goto out;
err = vcap_write_rule(ri);
if (err)
goto out;
/* Deallocate the list of keys and actions */
list_for_each_entry_safe(kf, nkf, &ri->data.keyfields, ctrl.list) {
list_del(&kf->ctrl.list);
kfree(kf);
}
list_for_each_entry_safe(af, naf, &ri->data.actionfields, ctrl.list) {
list_del(&af->ctrl.list);
kfree(af);
}
ri->state = VCAP_RS_ENABLED;
out:
return err;
}
/* Enable all disabled rules for a specific chain/port in the VCAP HW */
static int vcap_enable_rules(struct vcap_control *vctrl,
struct net_device *ndev, int chain)
{
struct vcap_rule_internal *ri;
struct vcap_admin *admin;
int err = 0;
list_for_each_entry(admin, &vctrl->list, list) {
if (!(chain >= admin->first_cid && chain <= admin->last_cid))
continue;
/* Found the admin, now find the offloadable rules */
mutex_lock(&admin->lock);
list_for_each_entry(ri, &admin->rules, list) {
if (ri->data.vcap_chain_id != chain)
continue;
if (ri->ndev != ndev)
continue;
if (ri->state != VCAP_RS_DISABLED)
continue;
err = vcap_enable_rule(ri);
if (err)
break;
}
mutex_unlock(&admin->lock);
if (err)
break;
}
return err;
}
/* Read and erase a rule from VCAP HW to disable it */
static int vcap_disable_rule(struct vcap_rule_internal *ri)
{
int err;
err = vcap_read_rule(ri);
if (err)
return err;
err = vcap_decode_keyset(ri);
if (err)
return err;
err = vcap_decode_actionset(ri);
if (err)
return err;
ri->state = VCAP_RS_DISABLED;
ri->vctrl->ops->init(ri->ndev, ri->admin, ri->addr, ri->size);
return 0;
}
/* Disable all enabled rules for a specific chain/port in the VCAP HW */
static int vcap_disable_rules(struct vcap_control *vctrl,
struct net_device *ndev, int chain)
{
struct vcap_rule_internal *ri;
struct vcap_admin *admin;
int err = 0;
list_for_each_entry(admin, &vctrl->list, list) {
if (!(chain >= admin->first_cid && chain <= admin->last_cid))
continue;
/* Found the admin, now find the rules on the chain */
mutex_lock(&admin->lock);
list_for_each_entry(ri, &admin->rules, list) {
if (ri->data.vcap_chain_id != chain)
continue;
if (ri->ndev != ndev)
continue;
if (ri->state != VCAP_RS_ENABLED)
continue;
err = vcap_disable_rule(ri);
if (err)
break;
}
mutex_unlock(&admin->lock);
if (err)
break;
}
return err;
}
/* 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)
static bool vcap_is_enabled(struct vcap_control *vctrl, struct net_device *ndev,
int dst_cid)
{
struct vcap_enabled_port *eport;
struct vcap_admin *admin;
list_for_each_entry(eport, &admin->enabled, list)
if (eport->cookie == cookie || eport->ndev == ndev)
return true;
list_for_each_entry(admin, &vctrl->list, list)
list_for_each_entry(eport, &admin->enabled, list)
if (eport->dst_cid == dst_cid && 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)
/* Enable this port and chain id in a VCAP instance */
static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev,
unsigned long cookie, int src_cid, int dst_cid)
{
struct vcap_enabled_port *eport;
struct vcap_admin *admin;
if (src_cid >= dst_cid)
return -EFAULT;
admin = vcap_find_admin(vctrl, dst_cid);
if (!admin)
return -ENOENT;
eport = kzalloc(sizeof(*eport), GFP_KERNEL);
if (!eport)
......@@ -2589,48 +2866,72 @@ static int vcap_enable(struct vcap_admin *admin, struct net_device *ndev,
eport->ndev = ndev;
eport->cookie = cookie;
eport->src_cid = src_cid;
eport->dst_cid = dst_cid;
mutex_lock(&admin->lock);
list_add_tail(&eport->list, &admin->enabled);
mutex_unlock(&admin->lock);
if (vcap_path_exist(vctrl, ndev, src_cid)) {
/* Enable chained lookups */
while (dst_cid) {
admin = vcap_find_admin(vctrl, dst_cid);
if (!admin)
return -ENOENT;
vcap_enable_rules(vctrl, ndev, dst_cid);
dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid);
}
}
return 0;
}
/* Disable this port for this VCAP instance */
static int vcap_disable(struct vcap_admin *admin, struct net_device *ndev,
/* Disable this port and chain id for a VCAP instance */
static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev,
unsigned long cookie)
{
struct vcap_enabled_port *eport;
struct vcap_enabled_port *elem, *eport = NULL;
struct vcap_admin *found = NULL, *admin;
int dst_cid;
list_for_each_entry(eport, &admin->enabled, list) {
if (eport->cookie == cookie && eport->ndev == ndev) {
list_del(&eport->list);
kfree(eport);
return 0;
list_for_each_entry(admin, &vctrl->list, list) {
list_for_each_entry(elem, &admin->enabled, list) {
if (elem->cookie == cookie && elem->ndev == ndev) {
eport = elem;
found = admin;
break;
}
}
if (eport)
break;
}
return -ENOENT;
}
if (!eport)
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;
/* Disable chained lookups */
dst_cid = eport->dst_cid;
while (dst_cid) {
admin = vcap_find_admin(vctrl, dst_cid);
if (!admin)
return -ENOENT;
list_for_each_entry(admin, &vctrl->list, list)
list_for_each_entry(eport, &admin->enabled, list)
if (eport->cookie == cookie)
return admin;
vcap_disable_rules(vctrl, ndev, dst_cid);
dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid);
}
return NULL;
mutex_lock(&found->lock);
list_del(&eport->list);
mutex_unlock(&found->lock);
kfree(eport);
return 0;
}
/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
/* Enable/Disable the VCAP instance lookups */
int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
int chain_id, unsigned long cookie, bool enable)
int src_cid, int dst_cid, unsigned long cookie,
bool enable)
{
struct vcap_admin *admin;
int err;
err = vcap_api_check(vctrl);
......@@ -2640,36 +2941,45 @@ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
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)
/* Source and destination must be the first chain in a lookup */
if (src_cid % VCAP_CID_LOOKUP_SIZE)
return -EFAULT;
if (dst_cid % VCAP_CID_LOOKUP_SIZE)
return -EFAULT;
err = vctrl->ops->enable(ndev, admin, enable);
if (err)
return err;
if (chain_id) {
if (vcap_is_enabled(admin, ndev, cookie))
if (enable) {
if (vcap_is_enabled(vctrl, ndev, dst_cid))
return -EADDRINUSE;
mutex_lock(&admin->lock);
vcap_enable(admin, ndev, cookie);
if (vcap_is_chain_used(vctrl, ndev, src_cid))
return -EADDRNOTAVAIL;
err = vcap_enable(vctrl, ndev, cookie, src_cid, dst_cid);
} else {
mutex_lock(&admin->lock);
vcap_disable(admin, ndev, cookie);
err = vcap_disable(vctrl, ndev, cookie);
}
mutex_unlock(&admin->lock);
return 0;
return err;
}
EXPORT_SYMBOL_GPL(vcap_enable_lookups);
/* Is this chain id the last lookup of all VCAPs */
bool vcap_is_last_chain(struct vcap_control *vctrl, int cid)
{
struct vcap_admin *admin;
int lookup;
if (vcap_api_check(vctrl))
return false;
admin = vcap_find_admin(vctrl, cid);
if (!admin)
return false;
/* This must be the last lookup in this VCAP type */
lookup = vcap_chain_id_to_lookup(admin, cid);
return lookup == admin->lookups - 1;
}
EXPORT_SYMBOL_GPL(vcap_is_last_chain);
/* Set a rule counter id (for certain vcaps only) */
void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id)
{
......@@ -2736,6 +3046,65 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
}
EXPORT_SYMBOL_GPL(vcap_rule_get_counter);
/* Get a copy of a client key field */
static int vcap_rule_get_key(struct vcap_rule *rule,
enum vcap_key_field key,
struct vcap_client_keyfield *ckf)
{
struct vcap_client_keyfield *field;
field = vcap_find_keyfield(rule, key);
if (!field)
return -EINVAL;
memcpy(ckf, field, sizeof(*ckf));
INIT_LIST_HEAD(&ckf->ctrl.list);
return 0;
}
/* Get the keysets that matches the rule key type/mask */
int vcap_rule_get_keysets(struct vcap_rule_internal *ri,
struct vcap_keyset_list *matches)
{
struct vcap_control *vctrl = ri->vctrl;
enum vcap_type vt = ri->admin->vtype;
const struct vcap_set *keyfield_set;
struct vcap_client_keyfield kf = {};
u32 value, mask;
int err, idx;
err = vcap_rule_get_key(&ri->data, VCAP_KF_TYPE, &kf);
if (err)
return err;
if (kf.ctrl.type == VCAP_FIELD_BIT) {
value = kf.data.u1.value;
mask = kf.data.u1.mask;
} else if (kf.ctrl.type == VCAP_FIELD_U32) {
value = kf.data.u32.value;
mask = kf.data.u32.mask;
} else {
return -EINVAL;
}
keyfield_set = vctrl->vcaps[vt].keyfield_set;
for (idx = 0; idx < vctrl->vcaps[vt].keyfield_set_size; ++idx) {
if (keyfield_set[idx].sw_per_item != ri->keyset_sw)
continue;
if (keyfield_set[idx].type_id == (u8)-1) {
vcap_keyset_list_add(matches, idx);
continue;
}
if ((keyfield_set[idx].type_id & mask) == value)
vcap_keyset_list_add(matches, idx);
}
if (matches->cnt > 0)
return 0;
return -EINVAL;
}
static int vcap_rule_mod_key(struct vcap_rule *rule,
enum vcap_key_field key,
enum vcap_field_type ftype,
......@@ -2746,7 +3115,7 @@ static int vcap_rule_mod_key(struct vcap_rule *rule,
field = vcap_find_keyfield(rule, key);
if (!field)
return vcap_rule_add_key(rule, key, ftype, data);
vcap_copy_from_client_keyfield(rule, field, data);
memcpy(&field->data, data, sizeof(field->data));
return 0;
}
......@@ -2772,7 +3141,7 @@ static int vcap_rule_mod_action(struct vcap_rule *rule,
field = vcap_find_actionfield(rule, action);
if (!field)
return vcap_rule_add_action(rule, action, ftype, data);
vcap_copy_from_client_actionfield(rule, field, data);
memcpy(&field->data, data, sizeof(field->data));
return 0;
}
......
......@@ -259,11 +259,6 @@ struct vcap_operations {
(struct net_device *ndev,
struct vcap_admin *admin,
struct vcap_output_print *out);
/* 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 */
......
......@@ -148,9 +148,10 @@ struct vcap_counter {
bool sticky;
};
/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
/* Enable/Disable the VCAP instance lookups */
int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
int chain_id, unsigned long cookie, bool enable);
int from_cid, int to_cid, unsigned long cookie,
bool enable);
/* VCAP rule operations */
/* Allocate a rule and fill in the basic information */
......@@ -216,6 +217,8 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
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);
/* Is this chain id the last lookup of all VCAPs */
bool vcap_is_last_chain(struct vcap_control *vctrl, int cid);
/* Provide all rules via a callback interface */
int vcap_rule_iter(struct vcap_control *vctrl,
int (*callback)(void *, struct vcap_rule *), void *arg);
......@@ -261,5 +264,4 @@ int vcap_rule_mod_action_u32(struct vcap_rule *rule,
/* Get a 32 bit key field value and mask from the rule */
int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key,
u32 *value, u32 *mask);
#endif /* __VCAP_API_CLIENT__ */
......@@ -152,37 +152,48 @@ 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,
false, 0, &matches);
if (res < 0) {
if (ri->state == VCAP_RS_DISABLED)
err = vcap_rule_get_keysets(ri, &matches);
else
err = vcap_find_keystream_keysets(ri->vctrl, admin->vtype,
admin->cache.keystream,
admin->cache.maskstream,
false, 0, &matches);
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 +244,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);
}
......
......@@ -221,13 +221,6 @@ static int vcap_test_port_info(struct net_device *ndev,
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,
......@@ -238,7 +231,6 @@ 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 = {
......@@ -253,6 +245,7 @@ static void vcap_test_api_init(struct vcap_admin *admin)
INIT_LIST_HEAD(&test_vctrl.list);
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
list_add_tail(&admin->list, &test_vctrl.list);
memset(test_updateaddr, 0, sizeof(test_updateaddr));
test_updateaddridx = 0;
......@@ -452,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",
......
......@@ -211,13 +211,6 @@ static int vcap_test_port_info(struct net_device *ndev,
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,
......@@ -228,7 +221,6 @@ 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 = {
......@@ -243,6 +235,7 @@ static void vcap_test_api_init(struct vcap_admin *admin)
INIT_LIST_HEAD(&test_vctrl.list);
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
list_add_tail(&admin->list, &test_vctrl.list);
memset(test_updateaddr, 0, sizeof(test_updateaddr));
test_updateaddridx = 0;
......@@ -1312,8 +1305,8 @@ static void vcap_api_encode_rule_test(struct kunit *test)
struct vcap_admin is2_admin = {
.vtype = VCAP_TYPE_IS2,
.first_cid = 10000,
.last_cid = 19999,
.first_cid = 8000000,
.last_cid = 8099999,
.lookups = 4,
.last_valid_addr = 3071,
.first_valid_addr = 0,
......@@ -1326,7 +1319,7 @@ static void vcap_api_encode_rule_test(struct kunit *test)
};
struct vcap_rule *rule;
struct vcap_rule_internal *ri;
int vcap_chain_id = 10005;
int vcap_chain_id = 8000000;
enum vcap_user user = VCAP_USER_VCAP_UTIL;
u16 priority = 10;
int id = 100;
......@@ -1343,8 +1336,8 @@ static void vcap_api_encode_rule_test(struct kunit *test)
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};
/* counter is written as the last operation */
u32 expwriteaddr[] = {792, 793, 794, 795, 796, 797, 792};
int idx;
vcap_test_api_init(&is2_admin);
......@@ -1398,6 +1391,11 @@ static void vcap_api_encode_rule_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, 2, ri->keyset_sw_regs);
KUNIT_EXPECT_EQ(test, 4, ri->actionset_sw_regs);
/* Enable lookup, so the rule will be written */
ret = vcap_enable_lookups(&test_vctrl, &test_netdev, 0,
rule->vcap_chain_id, rule->cookie, true);
KUNIT_EXPECT_EQ(test, 0, ret);
/* Add rule with write callback */
ret = vcap_add_rule(rule);
KUNIT_EXPECT_EQ(test, 0, ret);
......@@ -1872,7 +1870,7 @@ static void vcap_api_next_lookup_basic_test(struct kunit *test)
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);
KUNIT_EXPECT_EQ(test, false, ret);
}
static void vcap_api_next_lookup_advanced_test(struct kunit *test)
......@@ -1933,9 +1931,9 @@ static void vcap_api_next_lookup_advanced_test(struct kunit *test)
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);
KUNIT_EXPECT_EQ(test, true, ret);
ret = vcap_is_next_lookup(&test_vctrl, 1100000, 8101000);
KUNIT_EXPECT_EQ(test, false, ret);
KUNIT_EXPECT_EQ(test, true, 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);
......@@ -1951,7 +1949,7 @@ static void vcap_api_next_lookup_advanced_test(struct kunit *test)
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);
KUNIT_EXPECT_EQ(test, false, ret);
}
static void vcap_api_filter_unsupported_keys_test(struct kunit *test)
......
......@@ -13,6 +13,12 @@
#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);
......@@ -110,4 +115,7 @@ int vcap_find_keystream_keysets(struct vcap_control *vctrl, enum vcap_type vt,
u32 *keystream, u32 *mskstream, bool mask,
int sw_max, struct vcap_keyset_list *kslist);
/* Get the keysets that matches the rule key type/mask */
int vcap_rule_get_keysets(struct vcap_rule_internal *ri,
struct vcap_keyset_list *matches);
#endif /* __VCAP_API_PRIVATE__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment