Commit 814e7693 authored by Steen Hegelund's avatar Steen Hegelund Committed by David S. Miller

net: microchip: vcap api: Add a storage state to a VCAP rule

This allows a VCAP rule to be in one of 3 states:

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

This way important VCAP rules can be added even before the user enables the
VCAP lookups using a TC matchall filter.
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarSteen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 784c3067
......@@ -950,9 +950,12 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie)
}
EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie);
/* Make a shallow copy of the rule without the fields */
struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri)
/* Make a copy of the rule, shallow or full */
static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri,
bool full)
{
struct vcap_client_actionfield *caf, *newcaf;
struct vcap_client_keyfield *ckf, *newckf;
struct vcap_rule_internal *duprule;
/* Allocate the client part */
......@@ -965,6 +968,27 @@ struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri)
/* No elements in these lists */
INIT_LIST_HEAD(&duprule->data.keyfields);
INIT_LIST_HEAD(&duprule->data.actionfields);
/* A full rule copy includes keys and actions */
if (!full)
return duprule;
list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) {
newckf = kzalloc(sizeof(*newckf), GFP_KERNEL);
if (!newckf)
return ERR_PTR(-ENOMEM);
memcpy(newckf, ckf, sizeof(*newckf));
list_add_tail(&newckf->ctrl.list, &duprule->data.keyfields);
}
list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) {
newcaf = kzalloc(sizeof(*newcaf), GFP_KERNEL);
if (!newcaf)
return ERR_PTR(-ENOMEM);
memcpy(newcaf, caf, sizeof(*newcaf));
list_add_tail(&newcaf->ctrl.list, &duprule->data.actionfields);
}
return duprule;
}
......@@ -1877,8 +1901,8 @@ static int vcap_insert_rule(struct vcap_rule_internal *ri,
ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri);
admin->last_used_addr = ri->addr;
/* Add a shallow copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri);
/* Add a copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri, ri->state == VCAP_RS_DISABLED);
if (IS_ERR(duprule))
return PTR_ERR(duprule);
......@@ -1891,8 +1915,8 @@ static int vcap_insert_rule(struct vcap_rule_internal *ri,
ri->addr = vcap_next_rule_addr(addr, ri);
addr = ri->addr;
/* Add a shallow copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri);
/* Add a copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri, ri->state == VCAP_RS_DISABLED);
if (IS_ERR(duprule))
return PTR_ERR(duprule);
......@@ -1939,6 +1963,72 @@ static bool vcap_is_chain_used(struct vcap_control *vctrl,
return false;
}
/* Fetch the next chain in the enabled list for the port */
static int vcap_get_next_chain(struct vcap_control *vctrl,
struct net_device *ndev,
int dst_cid)
{
struct vcap_enabled_port *eport;
struct vcap_admin *admin;
list_for_each_entry(admin, &vctrl->list, list) {
list_for_each_entry(eport, &admin->enabled, list) {
if (eport->ndev != ndev)
continue;
if (eport->src_cid == dst_cid)
return eport->dst_cid;
}
}
return 0;
}
static bool vcap_path_exist(struct vcap_control *vctrl, struct net_device *ndev,
int dst_cid)
{
struct vcap_enabled_port *eport, *elem;
struct vcap_admin *admin;
int tmp;
/* Find first entry that starts from chain 0*/
list_for_each_entry(admin, &vctrl->list, list) {
list_for_each_entry(elem, &admin->enabled, list) {
if (elem->src_cid == 0 && elem->ndev == ndev) {
eport = elem;
break;
}
}
if (eport)
break;
}
if (!eport)
return false;
tmp = eport->dst_cid;
while (tmp != dst_cid && tmp != 0)
tmp = vcap_get_next_chain(vctrl, ndev, tmp);
return !!tmp;
}
/* Internal clients can always store their rules in HW
* External clients can store their rules if the chain is enabled all
* the way from chain 0, otherwise the rule will be cached until
* the chain is enabled.
*/
static void vcap_rule_set_state(struct vcap_rule_internal *ri)
{
if (ri->data.user <= VCAP_USER_QOS)
ri->state = VCAP_RS_PERMANENT;
else if (vcap_path_exist(ri->vctrl, ri->ndev, ri->data.vcap_chain_id))
ri->state = VCAP_RS_ENABLED;
else
ri->state = VCAP_RS_DISABLED;
/* For now always store directly in HW */
ri->state = VCAP_RS_PERMANENT;
}
/* Encode and write a validated rule to the VCAP */
int vcap_add_rule(struct vcap_rule *rule)
{
......@@ -1952,6 +2042,8 @@ int vcap_add_rule(struct vcap_rule *rule)
return ret;
/* Insert the new rule in the list of vcap rules */
mutex_lock(&ri->admin->lock);
vcap_rule_set_state(ri);
ret = vcap_insert_rule(ri, &move);
if (ret < 0) {
pr_err("%s:%d: could not insert rule in vcap list: %d\n",
......@@ -1960,6 +2052,13 @@ int vcap_add_rule(struct vcap_rule *rule)
}
if (move.count > 0)
vcap_move_rules(ri, &move);
if (ri->state == VCAP_RS_DISABLED) {
/* Erase the rule area */
ri->vctrl->ops->init(ri->ndev, ri->admin, ri->addr, ri->size);
goto out;
}
vcap_erase_cache(ri);
ret = vcap_encode_rule(ri);
if (ret) {
......@@ -2071,9 +2170,13 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id)
if (!elem)
return NULL;
mutex_lock(&elem->admin->lock);
ri = vcap_dup_rule(elem);
ri = vcap_dup_rule(elem, elem->state == VCAP_RS_DISABLED);
if (IS_ERR(ri))
goto unlock;
if (ri->state == VCAP_RS_DISABLED)
goto unlock;
err = vcap_read_rule(ri);
if (err) {
ri = ERR_PTR(err);
......@@ -2111,6 +2214,11 @@ int vcap_mod_rule(struct vcap_rule *rule)
return -ENOENT;
mutex_lock(&ri->admin->lock);
vcap_rule_set_state(ri);
if (ri->state == VCAP_RS_DISABLED)
goto out;
/* Encode the bitstreams to the VCAP cache */
vcap_erase_cache(ri);
err = vcap_encode_rule(ri);
......@@ -2203,7 +2311,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
mutex_lock(&admin->lock);
list_del(&ri->list);
vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap);
kfree(ri);
vcap_free_rule(&ri->data);
mutex_unlock(&admin->lock);
/* Update the last used address, set to default when no rules */
......@@ -2232,7 +2340,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
list_for_each_entry_safe(ri, next_ri, &admin->rules, list) {
vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size);
list_del(&ri->list);
kfree(ri);
vcap_free_rule(&ri->data);
}
admin->last_used_addr = admin->last_valid_addr;
......
......@@ -152,37 +152,45 @@ vcap_debugfs_show_rule_actionfield(struct vcap_control *vctrl,
out->prf(out->dst, "\n");
}
static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri,
struct vcap_output_print *out)
static int vcap_debugfs_show_keysets(struct vcap_rule_internal *ri,
struct vcap_output_print *out)
{
struct vcap_control *vctrl = ri->vctrl;
struct vcap_admin *admin = ri->admin;
enum vcap_keyfield_set keysets[10];
const struct vcap_field *keyfield;
enum vcap_type vt = admin->vtype;
struct vcap_client_keyfield *ckf;
struct vcap_keyset_list matches;
u32 *maskstream;
u32 *keystream;
int res;
int err;
keystream = admin->cache.keystream;
maskstream = admin->cache.maskstream;
matches.keysets = keysets;
matches.cnt = 0;
matches.max = ARRAY_SIZE(keysets);
res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream,
err = vcap_find_keystream_keysets(ri->vctrl, admin->vtype,
admin->cache.keystream,
admin->cache.maskstream,
false, 0, &matches);
if (res < 0) {
if (err) {
pr_err("%s:%d: could not find valid keysets: %d\n",
__func__, __LINE__, res);
return -EINVAL;
__func__, __LINE__, err);
return err;
}
out->prf(out->dst, " keysets:");
for (int idx = 0; idx < matches.cnt; ++idx)
out->prf(out->dst, " %s",
vcap_keyset_name(vctrl, matches.keysets[idx]));
vcap_keyset_name(ri->vctrl, matches.keysets[idx]));
out->prf(out->dst, "\n");
return 0;
}
static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri,
struct vcap_output_print *out)
{
struct vcap_control *vctrl = ri->vctrl;
struct vcap_admin *admin = ri->admin;
const struct vcap_field *keyfield;
struct vcap_client_keyfield *ckf;
vcap_debugfs_show_keysets(ri, out);
out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw);
out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs);
......@@ -233,6 +241,18 @@ static void vcap_show_admin_rule(struct vcap_control *vctrl,
out->prf(out->dst, " chain_id: %d\n", ri->data.vcap_chain_id);
out->prf(out->dst, " user: %d\n", ri->data.user);
out->prf(out->dst, " priority: %d\n", ri->data.priority);
out->prf(out->dst, " state: ");
switch (ri->state) {
case VCAP_RS_PERMANENT:
out->prf(out->dst, "permanent\n");
break;
case VCAP_RS_DISABLED:
out->prf(out->dst, "disabled\n");
break;
case VCAP_RS_ENABLED:
out->prf(out->dst, "enabled\n");
break;
}
vcap_debugfs_show_rule_keyset(ri, out);
vcap_debugfs_show_rule_actionset(ri, out);
}
......
......@@ -445,6 +445,7 @@ static const char * const test_admin_expect[] = {
" chain_id: 0\n",
" user: 0\n",
" priority: 0\n",
" state: permanent\n",
" keysets: VCAP_KFS_MAC_ETYPE\n",
" keyset_sw: 6\n",
" keyset_sw_regs: 2\n",
......
......@@ -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);
......
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