diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c')
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 617 |
1 files changed, 416 insertions, 201 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 695d33358988..3e70cee4d2f3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -8,6 +8,8 @@ #include <linux/string.h> #include <linux/rhashtable.h> #include <linux/netdevice.h> +#include <linux/mutex.h> +#include <linux/refcount.h> #include <net/net_namespace.h> #include <net/tc_act/tc_vlan.h> @@ -25,6 +27,7 @@ struct mlxsw_sp_acl { struct mlxsw_sp_fid *dummy_fid; struct rhashtable ruleset_ht; struct list_head rules; + struct mutex rules_lock; /* Protects rules list */ struct { struct delayed_work dw; unsigned long interval; /* ms */ @@ -38,23 +41,13 @@ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl) return acl->afk; } -struct mlxsw_sp_acl_block_binding { - struct list_head list; - struct net_device *dev; - struct mlxsw_sp_port *mlxsw_sp_port; - bool ingress; -}; - -struct mlxsw_sp_acl_block { - struct list_head binding_list; - struct mlxsw_sp_acl_ruleset *ruleset_zero; - struct mlxsw_sp *mlxsw_sp; - unsigned int rule_count; - unsigned int disable_count; -}; +struct mlxsw_sp_acl_tcam *mlxsw_sp_acl_to_tcam(struct mlxsw_sp_acl *acl) +{ + return &acl->tcam; +} struct mlxsw_sp_acl_ruleset_ht_key { - struct mlxsw_sp_acl_block *block; + struct mlxsw_sp_flow_block *block; u32 chain_index; const struct mlxsw_sp_acl_profile_ops *ops; }; @@ -63,8 +56,10 @@ struct mlxsw_sp_acl_ruleset { struct rhash_head ht_node; /* Member of acl HT */ struct mlxsw_sp_acl_ruleset_ht_key ht_key; struct rhashtable rule_ht; - unsigned int ref_count; - unsigned long priv[0]; + refcount_t ref_count; + unsigned int min_prio; + unsigned int max_prio; + unsigned long priv[]; /* priv has to be always the last item */ }; @@ -77,7 +72,8 @@ struct mlxsw_sp_acl_rule { u64 last_used; u64 last_packets; u64 last_bytes; - unsigned long priv[0]; + u64 last_drops; + unsigned long priv[]; /* priv has to be always the last item */ }; @@ -100,55 +96,16 @@ struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp) return mlxsw_sp->acl->dummy_fid; } -struct mlxsw_sp *mlxsw_sp_acl_block_mlxsw_sp(struct mlxsw_sp_acl_block *block) -{ - return block->mlxsw_sp; -} - -unsigned int mlxsw_sp_acl_block_rule_count(struct mlxsw_sp_acl_block *block) -{ - return block ? block->rule_count : 0; -} - -void mlxsw_sp_acl_block_disable_inc(struct mlxsw_sp_acl_block *block) -{ - if (block) - block->disable_count++; -} - -void mlxsw_sp_acl_block_disable_dec(struct mlxsw_sp_acl_block *block) -{ - if (block) - block->disable_count--; -} - -bool mlxsw_sp_acl_block_disabled(struct mlxsw_sp_acl_block *block) -{ - return block->disable_count; -} - -bool mlxsw_sp_acl_block_is_egress_bound(struct mlxsw_sp_acl_block *block) -{ - struct mlxsw_sp_acl_block_binding *binding; - - list_for_each_entry(binding, &block->binding_list, list) { - if (!binding->ingress) - return true; - } - return false; -} - static bool mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset) { /* We hold a reference on ruleset ourselves */ - return ruleset->ref_count == 2; + return refcount_read(&ruleset->ref_count) == 2; } -static int -mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, - struct mlxsw_sp_acl_block_binding *binding) +int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_flow_block *block, + struct mlxsw_sp_flow_block_binding *binding) { struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero; const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; @@ -157,10 +114,9 @@ mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, binding->mlxsw_sp_port, binding->ingress); } -static void -mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, - struct mlxsw_sp_acl_block_binding *binding) +void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_flow_block *block, + struct mlxsw_sp_flow_block_binding *binding) { struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero; const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; @@ -169,17 +125,12 @@ mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, binding->mlxsw_sp_port, binding->ingress); } -static bool mlxsw_sp_acl_ruleset_block_bound(struct mlxsw_sp_acl_block *block) -{ - return block->ruleset_zero; -} - static int mlxsw_sp_acl_ruleset_block_bind(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, - struct mlxsw_sp_acl_block *block) + struct mlxsw_sp_flow_block *block) { - struct mlxsw_sp_acl_block_binding *binding; + struct mlxsw_sp_flow_block_binding *binding; int err; block->ruleset_zero = ruleset; @@ -202,101 +153,18 @@ rollback: static void mlxsw_sp_acl_ruleset_block_unbind(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, - struct mlxsw_sp_acl_block *block) + struct mlxsw_sp_flow_block *block) { - struct mlxsw_sp_acl_block_binding *binding; + struct mlxsw_sp_flow_block_binding *binding; list_for_each_entry(binding, &block->binding_list, list) mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding); block->ruleset_zero = NULL; } -struct mlxsw_sp_acl_block *mlxsw_sp_acl_block_create(struct mlxsw_sp *mlxsw_sp, - struct net *net) -{ - struct mlxsw_sp_acl_block *block; - - block = kzalloc(sizeof(*block), GFP_KERNEL); - if (!block) - return NULL; - INIT_LIST_HEAD(&block->binding_list); - block->mlxsw_sp = mlxsw_sp; - return block; -} - -void mlxsw_sp_acl_block_destroy(struct mlxsw_sp_acl_block *block) -{ - WARN_ON(!list_empty(&block->binding_list)); - kfree(block); -} - -static struct mlxsw_sp_acl_block_binding * -mlxsw_sp_acl_block_lookup(struct mlxsw_sp_acl_block *block, - struct mlxsw_sp_port *mlxsw_sp_port, bool ingress) -{ - struct mlxsw_sp_acl_block_binding *binding; - - list_for_each_entry(binding, &block->binding_list, list) - if (binding->mlxsw_sp_port == mlxsw_sp_port && - binding->ingress == ingress) - return binding; - return NULL; -} - -int mlxsw_sp_acl_block_bind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, - struct mlxsw_sp_port *mlxsw_sp_port, - bool ingress) -{ - struct mlxsw_sp_acl_block_binding *binding; - int err; - - if (WARN_ON(mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress))) - return -EEXIST; - - binding = kzalloc(sizeof(*binding), GFP_KERNEL); - if (!binding) - return -ENOMEM; - binding->mlxsw_sp_port = mlxsw_sp_port; - binding->ingress = ingress; - - if (mlxsw_sp_acl_ruleset_block_bound(block)) { - err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding); - if (err) - goto err_ruleset_bind; - } - - list_add(&binding->list, &block->binding_list); - return 0; - -err_ruleset_bind: - kfree(binding); - return err; -} - -int mlxsw_sp_acl_block_unbind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, - struct mlxsw_sp_port *mlxsw_sp_port, - bool ingress) -{ - struct mlxsw_sp_acl_block_binding *binding; - - binding = mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress); - if (!binding) - return -ENOENT; - - list_del(&binding->list); - - if (mlxsw_sp_acl_ruleset_block_bound(block)) - mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding); - - kfree(binding); - return 0; -} - static struct mlxsw_sp_acl_ruleset * mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, u32 chain_index, + struct mlxsw_sp_flow_block *block, u32 chain_index, const struct mlxsw_sp_acl_profile_ops *ops, struct mlxsw_afk_element_usage *tmplt_elusage) { @@ -309,7 +177,7 @@ mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp, ruleset = kzalloc(alloc_size, GFP_KERNEL); if (!ruleset) return ERR_PTR(-ENOMEM); - ruleset->ref_count = 1; + refcount_set(&ruleset->ref_count, 1); ruleset->ht_key.block = block; ruleset->ht_key.chain_index = chain_index; ruleset->ht_key.ops = ops; @@ -319,7 +187,8 @@ mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp, goto err_rhashtable_init; err = ops->ruleset_add(mlxsw_sp, &acl->tcam, ruleset->priv, - tmplt_elusage); + tmplt_elusage, &ruleset->min_prio, + &ruleset->max_prio); if (err) goto err_ops_ruleset_add; @@ -354,20 +223,20 @@ static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset) { - ruleset->ref_count++; + refcount_inc(&ruleset->ref_count); } static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset) { - if (--ruleset->ref_count) + if (!refcount_dec_and_test(&ruleset->ref_count)) return; mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset); } static struct mlxsw_sp_acl_ruleset * __mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl, - struct mlxsw_sp_acl_block *block, u32 chain_index, + struct mlxsw_sp_flow_block *block, u32 chain_index, const struct mlxsw_sp_acl_profile_ops *ops) { struct mlxsw_sp_acl_ruleset_ht_key ht_key; @@ -382,7 +251,7 @@ __mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl, struct mlxsw_sp_acl_ruleset * mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, u32 chain_index, + struct mlxsw_sp_flow_block *block, u32 chain_index, enum mlxsw_sp_acl_profile profile) { const struct mlxsw_sp_acl_profile_ops *ops; @@ -400,7 +269,7 @@ mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset * mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_acl_block *block, u32 chain_index, + struct mlxsw_sp_flow_block *block, u32 chain_index, enum mlxsw_sp_acl_profile profile, struct mlxsw_afk_element_usage *tmplt_elusage) { @@ -434,6 +303,14 @@ u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset) return ops->ruleset_group_id(ruleset->priv); } +void mlxsw_sp_acl_ruleset_prio_get(struct mlxsw_sp_acl_ruleset *ruleset, + unsigned int *p_min_prio, + unsigned int *p_max_prio) +{ + *p_min_prio = ruleset->min_prio; + *p_max_prio = ruleset->max_prio; +} + struct mlxsw_sp_acl_rule_info * mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl, struct mlxsw_afa_block *afa_block) @@ -443,7 +320,7 @@ mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl, rulei = kzalloc(sizeof(*rulei), GFP_KERNEL); if (!rulei) - return NULL; + return ERR_PTR(-ENOMEM); if (afa_block) { rulei->act_block = afa_block; @@ -463,10 +340,17 @@ err_afa_block_create: return ERR_PTR(err); } -void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp_acl_rule_info *rulei) +void mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei) { if (rulei->action_created) mlxsw_afa_block_destroy(rulei->act_block); + if (rulei->src_port_range_reg_valid) + mlxsw_sp_port_range_reg_put(mlxsw_sp, + rulei->src_port_range_reg_index); + if (rulei->dst_port_range_reg_valid) + mlxsw_sp_port_range_reg_put(mlxsw_sp, + rulei->dst_port_range_reg_index); kfree(rulei); } @@ -478,7 +362,7 @@ int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei) void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei, unsigned int priority) { - rulei->priority = priority >> 16; + rulei->priority = priority; } void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei, @@ -514,9 +398,13 @@ int mlxsw_sp_acl_rulei_act_terminate(struct mlxsw_sp_acl_rule_info *rulei) return mlxsw_afa_block_terminate(rulei->act_block); } -int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei) +int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei, + bool ingress, + const struct flow_action_cookie *fa_cookie, + struct netlink_ext_ack *extack) { - return mlxsw_afa_block_append_drop(rulei->act_block); + return mlxsw_afa_block_append_drop(rulei->act_block, ingress, + fa_cookie, extack); } int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei) @@ -531,7 +419,7 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, struct netlink_ext_ack *extack) { struct mlxsw_sp_port *mlxsw_sp_port; - u8 local_port; + u16 local_port; bool in_port; if (out_dev) { @@ -559,11 +447,11 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, - struct mlxsw_sp_acl_block *block, + struct mlxsw_sp_flow_block *block, struct net_device *out_dev, struct netlink_ext_ack *extack) { - struct mlxsw_sp_acl_block_binding *binding; + struct mlxsw_sp_flow_block_binding *binding; struct mlxsw_sp_port *in_port; if (!list_is_singular(&block->binding_list)) { @@ -571,7 +459,7 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; } binding = list_first_entry(&block->binding_list, - struct mlxsw_sp_acl_block_binding, list); + struct mlxsw_sp_flow_block_binding, list); in_port = binding->mlxsw_sp_port; return mlxsw_afa_block_append_mirror(rulei->act_block, @@ -588,7 +476,7 @@ int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, { u8 ethertype; - if (action == TCA_VLAN_ACT_MODIFY) { + if (action == FLOW_ACTION_VLAN_MANGLE) { switch (proto) { case ETH_P_8021Q: ethertype = 0; @@ -613,12 +501,272 @@ int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, } } +int mlxsw_sp_acl_rulei_act_priority(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u32 prio, struct netlink_ext_ack *extack) +{ + /* Even though both Linux and Spectrum switches support 16 priorities, + * spectrum_qdisc only processes the first eight priomap elements, and + * the DCB and PFC features are tied to 8 priorities as well. Therefore + * bounce attempts to prioritize packets to higher priorities. + */ + if (prio >= IEEE_8021QAZ_MAX_TCS) { + NL_SET_ERR_MSG_MOD(extack, "Only priorities 0..7 are supported"); + return -EINVAL; + } + return mlxsw_afa_block_append_qos_switch_prio(rulei->act_block, prio, + extack); +} + +struct mlxsw_sp_acl_mangle_action { + enum flow_action_mangle_base htype; + /* Offset is u32-aligned. */ + u32 offset; + /* Mask bits are unset for the modified field. */ + u32 mask; + /* Shift required to extract the set value. */ + u32 shift; + enum mlxsw_sp_acl_mangle_field field; +}; + +#define MLXSW_SP_ACL_MANGLE_ACTION(_htype, _offset, _mask, _shift, _field) \ + { \ + .htype = _htype, \ + .offset = _offset, \ + .mask = _mask, \ + .shift = _shift, \ + .field = MLXSW_SP_ACL_MANGLE_FIELD_##_field, \ + } + +#define MLXSW_SP_ACL_MANGLE_ACTION_IP4(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP4, \ + _offset, _mask, _shift, _field) + +#define MLXSW_SP_ACL_MANGLE_ACTION_IP6(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP6, \ + _offset, _mask, _shift, _field) + +#define MLXSW_SP_ACL_MANGLE_ACTION_TCP(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_TCP, _offset, _mask, _shift, _field) + +#define MLXSW_SP_ACL_MANGLE_ACTION_UDP(_offset, _mask, _shift, _field) \ + MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_UDP, _offset, _mask, _shift, _field) + +static struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = { + MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff00ffff, 16, IP_DSFIELD), + MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff03ffff, 18, IP_DSCP), + MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xfffcffff, 16, IP_ECN), + + MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf00fffff, 20, IP_DSFIELD), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf03fffff, 22, IP_DSCP), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xffcfffff, 20, IP_ECN), + + MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0x0000ffff, 16, IP_SPORT), + MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0xffff0000, 0, IP_DPORT), + + MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT), + MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0, IP_DPORT), + + MLXSW_SP_ACL_MANGLE_ACTION_IP4(12, 0x00000000, 0, IP4_SIP), + MLXSW_SP_ACL_MANGLE_ACTION_IP4(16, 0x00000000, 0, IP4_DIP), + + MLXSW_SP_ACL_MANGLE_ACTION_IP6(8, 0x00000000, 0, IP6_SIP_1), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(12, 0x00000000, 0, IP6_SIP_2), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(16, 0x00000000, 0, IP6_SIP_3), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(20, 0x00000000, 0, IP6_SIP_4), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(24, 0x00000000, 0, IP6_DIP_1), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(28, 0x00000000, 0, IP6_DIP_2), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(32, 0x00000000, 0, IP6_DIP_3), + MLXSW_SP_ACL_MANGLE_ACTION_IP6(36, 0x00000000, 0, IP6_DIP_4), +}; + +static int +mlxsw_sp_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, + u32 val, struct netlink_ext_ack *extack) +{ + switch (mact->field) { + case MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD: + return mlxsw_afa_block_append_qos_dsfield(rulei->act_block, + val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP: + return mlxsw_afa_block_append_qos_dscp(rulei->act_block, + val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN: + return mlxsw_afa_block_append_qos_ecn(rulei->act_block, + val, extack); + default: + return -EOPNOTSUPP; + } +} + +static int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, + u32 val, struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack); + if (err != -EOPNOTSUPP) + return err; + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + return err; +} + +static int +mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(struct mlxsw_sp_acl_rule_info *rulei, + enum mlxsw_sp_acl_mangle_field field, + u32 val, struct netlink_ext_ack *extack) +{ + if (!rulei->ipv6_valid) { + rulei->ipv6.prev_val = val; + rulei->ipv6_valid = true; + rulei->ipv6.prev_field = field; + return 0; + } + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field order"); + return -EOPNOTSUPP; +} + +static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_acl_mangle_action *mact, + u32 val, struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack); + if (err != -EOPNOTSUPP) + return err; + + switch (mact->field) { + case MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT: + return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT: + return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack); + /* IPv4 fields */ + case MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP: + return mlxsw_afa_block_append_ip(rulei->act_block, false, + true, val, 0, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP: + return mlxsw_afa_block_append_ip(rulei->act_block, true, + true, val, 0, extack); + /* IPv6 fields */ + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1: + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3: + return mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(rulei, + mact->field, + val, extack); + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + false, false, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + false, true, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + true, false, val, + rulei->ipv6.prev_val, + extack); + } + break; + case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4: + if (rulei->ipv6_valid && + rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3) { + rulei->ipv6_valid = false; + return mlxsw_afa_block_append_ip(rulei->act_block, + true, true, val, + rulei->ipv6.prev_val, + extack); + } + break; + default: + break; + } + + NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field"); + return err; +} + +int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + enum flow_action_mangle_base htype, + u32 offset, u32 mask, u32 val, + struct netlink_ext_ack *extack) +{ + const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops = mlxsw_sp->acl_rulei_ops; + struct mlxsw_sp_acl_mangle_action *mact; + size_t i; + + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_acl_mangle_actions); ++i) { + mact = &mlxsw_sp_acl_mangle_actions[i]; + if (mact->htype == htype && + mact->offset == offset && + mact->mask == mask) { + val >>= mact->shift; + return acl_rulei_ops->act_mangle_field(mlxsw_sp, + rulei, mact, + val, extack); + } + } + + NL_SET_ERR_MSG_MOD(extack, "Unknown mangle field"); + return -EINVAL; +} + +int mlxsw_sp_acl_rulei_act_police(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u32 index, u64 rate_bytes_ps, + u32 burst, struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_afa_block_append_police(rulei->act_block, index, + rate_bytes_ps, burst, + &rulei->policer_index, extack); + if (err) + return err; + + rulei->policer_index_valid = true; + + return 0; +} + int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, struct netlink_ext_ack *extack) { - return mlxsw_afa_block_append_counter(rulei->act_block, - &rulei->counter_index, extack); + int err; + + err = mlxsw_afa_block_append_counter(rulei->act_block, + &rulei->counter_index, extack); + if (err) + return err; + rulei->counter_valid = true; + return 0; } int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, @@ -628,6 +776,40 @@ int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack); } +int mlxsw_sp_acl_rulei_act_ignore(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + bool disable_learning, bool disable_security) +{ + return mlxsw_afa_block_append_ignore(rulei->act_block, + disable_learning, + disable_security); +} + +int mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct mlxsw_sp_flow_block *block, + struct psample_group *psample_group, u32 rate, + u32 trunc_size, bool truncate, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_flow_block_binding *binding; + struct mlxsw_sp_port *mlxsw_sp_port; + + if (!list_is_singular(&block->binding_list)) { + NL_SET_ERR_MSG_MOD(extack, "Only a single sampling source is allowed"); + return -EOPNOTSUPP; + } + binding = list_first_entry(&block->binding_list, + struct mlxsw_sp_flow_block_binding, list); + mlxsw_sp_port = binding->mlxsw_sp_port; + + return mlxsw_afa_block_append_sampler(rulei->act_block, + mlxsw_sp_port->local_port, + psample_group, rate, trunc_size, + truncate, binding->ingress, + extack); +} + struct mlxsw_sp_acl_rule * mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, @@ -640,7 +822,7 @@ mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, int err; mlxsw_sp_acl_ruleset_ref_inc(ruleset); - rule = kzalloc(sizeof(*rule) + ops->rule_priv_size(mlxsw_sp), + rule = kzalloc(sizeof(*rule) + ops->rule_priv_size, GFP_KERNEL); if (!rule) { err = -ENOMEM; @@ -669,7 +851,7 @@ void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; - mlxsw_sp_acl_rulei_destroy(rule->rulei); + mlxsw_sp_acl_rulei_destroy(mlxsw_sp, rule->rulei); kfree(rule); mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset); } @@ -679,6 +861,7 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + struct mlxsw_sp_flow_block *block = ruleset->ht_key.block; int err; err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei); @@ -696,14 +879,17 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp, * one, to be directly bound to device. The rest of the * rulesets are bound by "Goto action set". */ - err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset, - ruleset->ht_key.block); + err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset, block); if (err) goto err_ruleset_block_bind; } + mutex_lock(&mlxsw_sp->acl->rules_lock); list_add_tail(&rule->list, &mlxsw_sp->acl->rules); - ruleset->ht_key.block->rule_count++; + mutex_unlock(&mlxsw_sp->acl->rules_lock); + block->rule_count++; + block->ingress_blocker_rule_count += rule->rulei->ingress_bind_blocker; + block->egress_blocker_rule_count += rule->rulei->egress_bind_blocker; return 0; err_ruleset_block_bind: @@ -719,13 +905,17 @@ void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + struct mlxsw_sp_flow_block *block = ruleset->ht_key.block; - ruleset->ht_key.block->rule_count--; + block->egress_blocker_rule_count -= rule->rulei->egress_bind_blocker; + block->ingress_blocker_rule_count -= rule->rulei->ingress_bind_blocker; + block->rule_count--; + mutex_lock(&mlxsw_sp->acl->rules_lock); list_del(&rule->list); + mutex_unlock(&mlxsw_sp->acl->rules_lock); if (!ruleset->ht_key.chain_index && mlxsw_sp_acl_ruleset_is_singular(ruleset)) - mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset, - ruleset->ht_key.block); + mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset, block); rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, mlxsw_sp_acl_rule_ht_params); ops->rule_del(mlxsw_sp, rule->priv); @@ -742,8 +932,7 @@ int mlxsw_sp_acl_rule_action_replace(struct mlxsw_sp *mlxsw_sp, rulei = mlxsw_sp_acl_rule_rulei(rule); rulei->act_block = afa_block; - return ops->rule_action_replace(mlxsw_sp, ruleset->priv, rule->priv, - rule->rulei); + return ops->rule_action_replace(mlxsw_sp, rule->priv, rule->rulei); } struct mlxsw_sp_acl_rule * @@ -782,19 +971,18 @@ static int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl) struct mlxsw_sp_acl_rule *rule; int err; - /* Protect internal structures from changes */ - rtnl_lock(); + mutex_lock(&acl->rules_lock); list_for_each_entry(rule, &acl->rules, list) { err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp, rule); if (err) goto err_rule_update; } - rtnl_unlock(); + mutex_unlock(&acl->rules_lock); return 0; err_rule_update: - rtnl_unlock(); + mutex_unlock(&acl->rules_lock); return err; } @@ -806,7 +994,7 @@ static void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl) msecs_to_jiffies(interval)); } -static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work) +static void mlxsw_sp_acl_rule_activity_update_work(struct work_struct *work) { struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl, rule_activity_update.dw.work); @@ -821,26 +1009,42 @@ static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work) int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule *rule, - u64 *packets, u64 *bytes, u64 *last_use) + u64 *packets, u64 *bytes, u64 *drops, + u64 *last_use, + enum flow_action_hw_stats *used_hw_stats) { + enum mlxsw_sp_policer_type type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE; struct mlxsw_sp_acl_rule_info *rulei; - u64 current_packets; - u64 current_bytes; + u64 current_packets = 0; + u64 current_bytes = 0; + u64 current_drops = 0; int err; rulei = mlxsw_sp_acl_rule_rulei(rule); - err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index, - ¤t_packets, ¤t_bytes); - if (err) - return err; - + if (rulei->counter_valid) { + err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index, + false, ¤t_packets, + ¤t_bytes); + if (err) + return err; + *used_hw_stats = FLOW_ACTION_HW_STATS_IMMEDIATE; + } + if (rulei->policer_index_valid) { + err = mlxsw_sp_policer_drops_counter_get(mlxsw_sp, type, + rulei->policer_index, + ¤t_drops); + if (err) + return err; + } *packets = current_packets - rule->last_packets; *bytes = current_bytes - rule->last_bytes; + *drops = current_drops - rule->last_drops; *last_use = rule->last_used; rule->last_bytes = current_bytes; rule->last_packets = current_packets; + rule->last_drops = current_drops; return 0; } @@ -879,18 +1083,20 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp) acl->dummy_fid = fid; INIT_LIST_HEAD(&acl->rules); + mutex_init(&acl->rules_lock); err = mlxsw_sp_acl_tcam_init(mlxsw_sp, &acl->tcam); if (err) goto err_acl_ops_init; /* Create the delayed work for the rule activity_update */ INIT_DELAYED_WORK(&acl->rule_activity_update.dw, - mlxsw_sp_acl_rul_activity_update_work); + mlxsw_sp_acl_rule_activity_update_work); acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS; mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0); return 0; err_acl_ops_init: + mutex_destroy(&acl->rules_lock); mlxsw_sp_fid_put(fid); err_fid_get: rhashtable_destroy(&acl->ruleset_ht); @@ -907,9 +1113,18 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp) cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw); mlxsw_sp_acl_tcam_fini(mlxsw_sp, &acl->tcam); + mutex_destroy(&acl->rules_lock); WARN_ON(!list_empty(&acl->rules)); mlxsw_sp_fid_put(acl->dummy_fid); rhashtable_destroy(&acl->ruleset_ht); mlxsw_afk_destroy(acl->afk); kfree(acl); } + +struct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops = { + .act_mangle_field = mlxsw_sp1_acl_rulei_act_mangle_field, +}; + +struct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops = { + .act_mangle_field = mlxsw_sp2_acl_rulei_act_mangle_field, +}; |
