net: prestera: manage matchall and flower priorities
matchall rules can be added only to chain 0 and their priorities have limitations: - new matchall ingress rule's priority must be higher (lower value) than any existing flower rule; - new matchall egress rule's priority must be lower (higher value) than any existing flower rule. The opposite works for flower rule adding: - new flower ingress rule's priority must be lower (higher value) than any existing matchall rule; - new flower egress rule's priority must be higher (lower value) than any existing matchall rule. This is a hardware limitation and thus must be properly handled in driver by reporting errors to the user when newly added rule has such a priority that cannot be installed into the hardware. To achieve this, the driver must maintain both min/max matchall priorities for every flower block when user adds/deletes a matchall rule, as well as both min/max flower priorities for chain 0 for every adding/deletion of flower rules for chain 0. Cc: Serhiy Boiko <serhiy.boiko@plvision.eu> Signed-off-by: Maksym Glubokiy <maksym.glubokiy@plvision.eu> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
8c448c2b5f
commit
44af95718f
8 changed files with 163 additions and 0 deletions
|
@ -54,6 +54,10 @@ struct prestera_acl_ruleset {
|
||||||
struct prestera_acl_ruleset_ht_key ht_key;
|
struct prestera_acl_ruleset_ht_key ht_key;
|
||||||
struct rhashtable rule_ht;
|
struct rhashtable rule_ht;
|
||||||
struct prestera_acl *acl;
|
struct prestera_acl *acl;
|
||||||
|
struct {
|
||||||
|
u32 min;
|
||||||
|
u32 max;
|
||||||
|
} prio;
|
||||||
unsigned long rule_count;
|
unsigned long rule_count;
|
||||||
refcount_t refcount;
|
refcount_t refcount;
|
||||||
void *keymask;
|
void *keymask;
|
||||||
|
@ -162,6 +166,9 @@ prestera_acl_ruleset_create(struct prestera_acl *acl,
|
||||||
ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
|
ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index);
|
||||||
ruleset->index = uid;
|
ruleset->index = uid;
|
||||||
|
|
||||||
|
ruleset->prio.min = UINT_MAX;
|
||||||
|
ruleset->prio.max = 0;
|
||||||
|
|
||||||
err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
|
err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
|
||||||
prestera_acl_ruleset_ht_params);
|
prestera_acl_ruleset_ht_params);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -365,6 +372,26 @@ prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset,
|
||||||
block->ruleset_zero = NULL;
|
block->ruleset_zero = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prestera_acl_ruleset_prio_refresh(struct prestera_acl *acl,
|
||||||
|
struct prestera_acl_ruleset *ruleset)
|
||||||
|
{
|
||||||
|
struct prestera_acl_rule *rule;
|
||||||
|
|
||||||
|
ruleset->prio.min = UINT_MAX;
|
||||||
|
ruleset->prio.max = 0;
|
||||||
|
|
||||||
|
list_for_each_entry(rule, &acl->rules, list) {
|
||||||
|
if (ruleset->ingress != rule->ruleset->ingress)
|
||||||
|
continue;
|
||||||
|
if (ruleset->ht_key.chain_index != rule->chain_index)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ruleset->prio.min = min(ruleset->prio.min, rule->priority);
|
||||||
|
ruleset->prio.max = max(ruleset->prio.max, rule->priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
|
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id)
|
||||||
{
|
{
|
||||||
|
@ -389,6 +416,13 @@ u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset)
|
||||||
return ruleset->index;
|
return ruleset->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset,
|
||||||
|
u32 *prio_min, u32 *prio_max)
|
||||||
|
{
|
||||||
|
*prio_min = ruleset->prio.min;
|
||||||
|
*prio_max = ruleset->prio.max;
|
||||||
|
}
|
||||||
|
|
||||||
bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
|
bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset)
|
||||||
{
|
{
|
||||||
return ruleset->offload;
|
return ruleset->offload;
|
||||||
|
@ -429,6 +463,13 @@ void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
|
||||||
kfree(rule);
|
kfree(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prestera_acl_ruleset_prio_update(struct prestera_acl_ruleset *ruleset,
|
||||||
|
u32 prio)
|
||||||
|
{
|
||||||
|
ruleset->prio.min = min(ruleset->prio.min, prio);
|
||||||
|
ruleset->prio.max = max(ruleset->prio.max, prio);
|
||||||
|
}
|
||||||
|
|
||||||
int prestera_acl_rule_add(struct prestera_switch *sw,
|
int prestera_acl_rule_add(struct prestera_switch *sw,
|
||||||
struct prestera_acl_rule *rule)
|
struct prestera_acl_rule *rule)
|
||||||
{
|
{
|
||||||
|
@ -468,6 +509,7 @@ int prestera_acl_rule_add(struct prestera_switch *sw,
|
||||||
|
|
||||||
list_add_tail(&rule->list, &sw->acl->rules);
|
list_add_tail(&rule->list, &sw->acl->rules);
|
||||||
ruleset->rule_count++;
|
ruleset->rule_count++;
|
||||||
|
prestera_acl_ruleset_prio_update(ruleset, rule->priority);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_acl_block_bind:
|
err_acl_block_bind:
|
||||||
|
@ -492,6 +534,7 @@ void prestera_acl_rule_del(struct prestera_switch *sw,
|
||||||
list_del(&rule->list);
|
list_del(&rule->list);
|
||||||
|
|
||||||
prestera_acl_rule_entry_destroy(sw->acl, rule->re);
|
prestera_acl_rule_entry_destroy(sw->acl, rule->re);
|
||||||
|
prestera_acl_ruleset_prio_refresh(sw->acl, ruleset);
|
||||||
|
|
||||||
/* unbind block (all ports) */
|
/* unbind block (all ports) */
|
||||||
if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
|
if (!ruleset->ht_key.chain_index && !ruleset->rule_count)
|
||||||
|
|
|
@ -195,6 +195,8 @@ int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset,
|
||||||
int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
|
int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset,
|
||||||
struct prestera_port *port);
|
struct prestera_port *port);
|
||||||
u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset);
|
u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset);
|
||||||
|
void prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset,
|
||||||
|
u32 *prio_min, u32 *prio_max);
|
||||||
void
|
void
|
||||||
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule,
|
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule,
|
||||||
u16 pcl_id);
|
u16 pcl_id);
|
||||||
|
|
|
@ -90,6 +90,9 @@ prestera_flow_block_create(struct prestera_switch *sw,
|
||||||
INIT_LIST_HEAD(&block->template_list);
|
INIT_LIST_HEAD(&block->template_list);
|
||||||
block->net = net;
|
block->net = net;
|
||||||
block->sw = sw;
|
block->sw = sw;
|
||||||
|
block->mall.prio_min = UINT_MAX;
|
||||||
|
block->mall.prio_max = 0;
|
||||||
|
block->mall.bound = false;
|
||||||
block->ingress = ingress;
|
block->ingress = ingress;
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
|
|
|
@ -22,6 +22,11 @@ struct prestera_flow_block {
|
||||||
struct prestera_acl_ruleset *ruleset_zero;
|
struct prestera_acl_ruleset *ruleset_zero;
|
||||||
struct flow_block_cb *block_cb;
|
struct flow_block_cb *block_cb;
|
||||||
struct list_head template_list;
|
struct list_head template_list;
|
||||||
|
struct {
|
||||||
|
u32 prio_min;
|
||||||
|
u32 prio_max;
|
||||||
|
bool bound;
|
||||||
|
} mall;
|
||||||
unsigned int rule_count;
|
unsigned int rule_count;
|
||||||
bool ingress;
|
bool ingress;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "prestera_acl.h"
|
#include "prestera_acl.h"
|
||||||
#include "prestera_flow.h"
|
#include "prestera_flow.h"
|
||||||
#include "prestera_flower.h"
|
#include "prestera_flower.h"
|
||||||
|
#include "prestera_matchall.h"
|
||||||
|
|
||||||
struct prestera_flower_template {
|
struct prestera_flower_template {
|
||||||
struct prestera_acl_ruleset *ruleset;
|
struct prestera_acl_ruleset *ruleset;
|
||||||
|
@ -360,6 +361,49 @@ static int prestera_flower_parse(struct prestera_flow_block *block,
|
||||||
f->common.extack);
|
f->common.extack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int prestera_flower_prio_check(struct prestera_flow_block *block,
|
||||||
|
struct flow_cls_offload *f)
|
||||||
|
{
|
||||||
|
u32 mall_prio_min;
|
||||||
|
u32 mall_prio_max;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = prestera_mall_prio_get(block, &mall_prio_min, &mall_prio_max);
|
||||||
|
if (err == -ENOENT)
|
||||||
|
/* No matchall filters installed on this chain. */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack, "Failed to get matchall priorities");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->common.prio <= mall_prio_max && block->ingress) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack,
|
||||||
|
"Failed to add in front of existing matchall rules");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
if (f->common.prio >= mall_prio_min && !block->ingress) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing matchall rules");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index,
|
||||||
|
u32 *prio_min, u32 *prio_max)
|
||||||
|
{
|
||||||
|
struct prestera_acl_ruleset *ruleset;
|
||||||
|
|
||||||
|
ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, chain_index);
|
||||||
|
if (IS_ERR(ruleset))
|
||||||
|
return PTR_ERR(ruleset);
|
||||||
|
|
||||||
|
prestera_acl_ruleset_prio_get(ruleset, prio_min, prio_max);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int prestera_flower_replace(struct prestera_flow_block *block,
|
int prestera_flower_replace(struct prestera_flow_block *block,
|
||||||
struct flow_cls_offload *f)
|
struct flow_cls_offload *f)
|
||||||
{
|
{
|
||||||
|
@ -368,6 +412,10 @@ int prestera_flower_replace(struct prestera_flow_block *block,
|
||||||
struct prestera_acl_rule *rule;
|
struct prestera_acl_rule *rule;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
err = prestera_flower_prio_check(block, f);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
|
ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index);
|
||||||
if (IS_ERR(ruleset))
|
if (IS_ERR(ruleset))
|
||||||
return PTR_ERR(ruleset);
|
return PTR_ERR(ruleset);
|
||||||
|
|
|
@ -19,5 +19,7 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block,
|
||||||
void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
|
void prestera_flower_tmplt_destroy(struct prestera_flow_block *block,
|
||||||
struct flow_cls_offload *f);
|
struct flow_cls_offload *f);
|
||||||
void prestera_flower_template_cleanup(struct prestera_flow_block *block);
|
void prestera_flower_template_cleanup(struct prestera_flow_block *block);
|
||||||
|
int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index,
|
||||||
|
u32 *prio_min, u32 *prio_max);
|
||||||
|
|
||||||
#endif /* _PRESTERA_FLOWER_H_ */
|
#endif /* _PRESTERA_FLOWER_H_ */
|
||||||
|
|
|
@ -11,6 +11,54 @@
|
||||||
#include "prestera_matchall.h"
|
#include "prestera_matchall.h"
|
||||||
#include "prestera_span.h"
|
#include "prestera_span.h"
|
||||||
|
|
||||||
|
static int prestera_mall_prio_check(struct prestera_flow_block *block,
|
||||||
|
struct tc_cls_matchall_offload *f)
|
||||||
|
{
|
||||||
|
u32 flower_prio_min;
|
||||||
|
u32 flower_prio_max;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = prestera_flower_prio_get(block, f->common.chain_index,
|
||||||
|
&flower_prio_min, &flower_prio_max);
|
||||||
|
if (err == -ENOENT)
|
||||||
|
/* No flower filters installed on this chain. */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->common.prio <= flower_prio_max && !block->ingress) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
if (f->common.prio >= flower_prio_min && block->ingress) {
|
||||||
|
NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prestera_mall_prio_get(struct prestera_flow_block *block,
|
||||||
|
u32 *prio_min, u32 *prio_max)
|
||||||
|
{
|
||||||
|
if (!block->mall.bound)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
*prio_min = block->mall.prio_min;
|
||||||
|
*prio_max = block->mall.prio_max;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prestera_mall_prio_update(struct prestera_flow_block *block,
|
||||||
|
struct tc_cls_matchall_offload *f)
|
||||||
|
{
|
||||||
|
block->mall.prio_min = min(block->mall.prio_min, f->common.prio);
|
||||||
|
block->mall.prio_max = max(block->mall.prio_max, f->common.prio);
|
||||||
|
}
|
||||||
|
|
||||||
int prestera_mall_replace(struct prestera_flow_block *block,
|
int prestera_mall_replace(struct prestera_flow_block *block,
|
||||||
struct tc_cls_matchall_offload *f)
|
struct tc_cls_matchall_offload *f)
|
||||||
{
|
{
|
||||||
|
@ -40,6 +88,10 @@ int prestera_mall_replace(struct prestera_flow_block *block,
|
||||||
if (protocol != htons(ETH_P_ALL))
|
if (protocol != htons(ETH_P_ALL))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
err = prestera_mall_prio_check(block, f);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
port = netdev_priv(act->dev);
|
port = netdev_priv(act->dev);
|
||||||
|
|
||||||
list_for_each_entry(binding, &block->binding_list, list) {
|
list_for_each_entry(binding, &block->binding_list, list) {
|
||||||
|
@ -48,6 +100,9 @@ int prestera_mall_replace(struct prestera_flow_block *block,
|
||||||
goto rollback;
|
goto rollback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prestera_mall_prio_update(block, f);
|
||||||
|
|
||||||
|
block->mall.bound = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rollback:
|
rollback:
|
||||||
|
@ -64,4 +119,7 @@ void prestera_mall_destroy(struct prestera_flow_block *block)
|
||||||
list_for_each_entry(binding, &block->binding_list, list)
|
list_for_each_entry(binding, &block->binding_list, list)
|
||||||
prestera_span_rule_del(binding, block->ingress);
|
prestera_span_rule_del(binding, block->ingress);
|
||||||
|
|
||||||
|
block->mall.prio_min = UINT_MAX;
|
||||||
|
block->mall.prio_max = 0;
|
||||||
|
block->mall.bound = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,7 @@ struct prestera_flow_block;
|
||||||
int prestera_mall_replace(struct prestera_flow_block *block,
|
int prestera_mall_replace(struct prestera_flow_block *block,
|
||||||
struct tc_cls_matchall_offload *f);
|
struct tc_cls_matchall_offload *f);
|
||||||
void prestera_mall_destroy(struct prestera_flow_block *block);
|
void prestera_mall_destroy(struct prestera_flow_block *block);
|
||||||
|
int prestera_mall_prio_get(struct prestera_flow_block *block,
|
||||||
|
u32 *prio_min, u32 *prio_max);
|
||||||
|
|
||||||
#endif /* _PRESTERA_MATCHALL_H_ */
|
#endif /* _PRESTERA_MATCHALL_H_ */
|
||||||
|
|
Loading…
Add table
Reference in a new issue