mirror of https://gitee.com/openkylin/linux.git
Merge branch 'Offload-tc-flower-to-mscc_ocelot-switch-using-VCAP-chains'
Vladimir Oltean says: ==================== Offload tc-flower to mscc_ocelot switch using VCAP chains The purpose of this patch is to add more comprehensive support for flow offloading in the mscc_ocelot library and switch drivers. The design (with chains) is the result of this discussion: https://lkml.org/lkml/2020/6/2/203 I have tested it on Seville VSC9953 and Felix VSC9959, but it should also work on Ocelot-1 VSC7514. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0c2a01dc27
|
@ -12542,6 +12542,7 @@ F: drivers/net/dsa/ocelot/*
|
|||
F: drivers/net/ethernet/mscc/
|
||||
F: include/soc/mscc/ocelot*
|
||||
F: net/dsa/tag_ocelot.c
|
||||
F: tools/testing/selftests/drivers/net/ocelot/*
|
||||
|
||||
OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER
|
||||
M: Frederic Barrat <fbarrat@linux.ibm.com>
|
||||
|
|
|
@ -810,3 +810,25 @@ const struct dsa_switch_ops felix_switch_ops = {
|
|||
.cls_flower_stats = felix_cls_flower_stats,
|
||||
.port_setup_tc = felix_port_setup_tc,
|
||||
};
|
||||
|
||||
struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
|
||||
{
|
||||
struct felix *felix = ocelot_to_felix(ocelot);
|
||||
struct dsa_switch *ds = felix->ds;
|
||||
|
||||
if (!dsa_is_user_port(ds, port))
|
||||
return NULL;
|
||||
|
||||
return dsa_to_port(ds, port)->slave;
|
||||
}
|
||||
|
||||
int felix_netdev_to_port(struct net_device *dev)
|
||||
{
|
||||
struct dsa_port *dp;
|
||||
|
||||
dp = dsa_port_from_netdev(dev);
|
||||
if (IS_ERR(dp))
|
||||
return -EINVAL;
|
||||
|
||||
return dp->index;
|
||||
}
|
||||
|
|
|
@ -52,4 +52,7 @@ struct felix {
|
|||
resource_size_t imdio_base;
|
||||
};
|
||||
|
||||
struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port);
|
||||
int felix_netdev_to_port(struct net_device *dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -711,6 +711,7 @@ static const struct vcap_field vsc9959_vcap_is1_actions[] = {
|
|||
[VCAP_IS1_ACT_PAG_OVERRIDE_MASK] = { 13, 8},
|
||||
[VCAP_IS1_ACT_PAG_VAL] = { 21, 8},
|
||||
[VCAP_IS1_ACT_RSV] = { 29, 9},
|
||||
/* The fields below are incorrectly shifted by 2 in the manual */
|
||||
[VCAP_IS1_ACT_VID_REPLACE_ENA] = { 38, 1},
|
||||
[VCAP_IS1_ACT_VID_ADD_VAL] = { 39, 12},
|
||||
[VCAP_IS1_ACT_FID_SEL] = { 51, 2},
|
||||
|
@ -1006,6 +1007,8 @@ static u16 vsc9959_wm_enc(u16 value)
|
|||
static const struct ocelot_ops vsc9959_ops = {
|
||||
.reset = vsc9959_reset,
|
||||
.wm_enc = vsc9959_wm_enc,
|
||||
.port_to_netdev = felix_port_to_netdev,
|
||||
.netdev_to_port = felix_netdev_to_port,
|
||||
};
|
||||
|
||||
static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
|
||||
|
|
|
@ -1058,6 +1058,8 @@ static u16 vsc9953_wm_enc(u16 value)
|
|||
static const struct ocelot_ops vsc9953_ops = {
|
||||
.reset = vsc9953_reset,
|
||||
.wm_enc = vsc9953_wm_enc,
|
||||
.port_to_netdev = felix_port_to_netdev,
|
||||
.netdev_to_port = felix_netdev_to_port,
|
||||
};
|
||||
|
||||
static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
|
||||
|
|
|
@ -108,6 +108,13 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
|
|||
ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
|
||||
ANA_PORT_VCAP_S2_CFG, port);
|
||||
|
||||
ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA,
|
||||
ANA_PORT_VCAP_CFG, port);
|
||||
|
||||
ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN,
|
||||
REW_PORT_CFG_ES0_EN,
|
||||
REW_PORT_CFG, port);
|
||||
}
|
||||
|
||||
static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
|
||||
|
|
|
@ -98,6 +98,8 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,
|
|||
struct net_device *bond);
|
||||
void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
|
||||
struct net_device *bond);
|
||||
struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port);
|
||||
int ocelot_netdev_to_port(struct net_device *dev);
|
||||
|
||||
u32 ocelot_port_readl(struct ocelot_port *port, u32 reg);
|
||||
void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
|
||||
|
|
|
@ -5,56 +5,410 @@
|
|||
|
||||
#include <net/pkt_cls.h>
|
||||
#include <net/tc_act/tc_gact.h>
|
||||
|
||||
#include <soc/mscc/ocelot_vcap.h>
|
||||
#include "ocelot_vcap.h"
|
||||
|
||||
static int ocelot_flower_parse_action(struct flow_cls_offload *f,
|
||||
/* Arbitrarily chosen constants for encoding the VCAP block and lookup number
|
||||
* into the chain number. This is UAPI.
|
||||
*/
|
||||
#define VCAP_BLOCK 10000
|
||||
#define VCAP_LOOKUP 1000
|
||||
#define VCAP_IS1_NUM_LOOKUPS 3
|
||||
#define VCAP_IS2_NUM_LOOKUPS 2
|
||||
#define VCAP_IS2_NUM_PAG 256
|
||||
#define VCAP_IS1_CHAIN(lookup) \
|
||||
(1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)
|
||||
#define VCAP_IS2_CHAIN(lookup, pag) \
|
||||
(2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))
|
||||
|
||||
static int ocelot_chain_to_block(int chain, bool ingress)
|
||||
{
|
||||
int lookup, pag;
|
||||
|
||||
if (!ingress) {
|
||||
if (chain == 0)
|
||||
return VCAP_ES0;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Backwards compatibility with older, single-chain tc-flower
|
||||
* offload support in Ocelot
|
||||
*/
|
||||
if (chain == 0)
|
||||
return VCAP_IS2;
|
||||
|
||||
for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)
|
||||
if (chain == VCAP_IS1_CHAIN(lookup))
|
||||
return VCAP_IS1;
|
||||
|
||||
for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)
|
||||
for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
|
||||
if (chain == VCAP_IS2_CHAIN(lookup, pag))
|
||||
return VCAP_IS2;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Caller must ensure this is a valid IS1 or IS2 chain first,
|
||||
* by calling ocelot_chain_to_block.
|
||||
*/
|
||||
static int ocelot_chain_to_lookup(int chain)
|
||||
{
|
||||
return (chain / VCAP_LOOKUP) % 10;
|
||||
}
|
||||
|
||||
/* Caller must ensure this is a valid IS2 chain first,
|
||||
* by calling ocelot_chain_to_block.
|
||||
*/
|
||||
static int ocelot_chain_to_pag(int chain)
|
||||
{
|
||||
int lookup = ocelot_chain_to_lookup(chain);
|
||||
|
||||
/* calculate PAG value as chain index relative to the first PAG */
|
||||
return chain - VCAP_IS2_CHAIN(lookup, 0);
|
||||
}
|
||||
|
||||
static bool ocelot_is_goto_target_valid(int goto_target, int chain,
|
||||
bool ingress)
|
||||
{
|
||||
int pag;
|
||||
|
||||
/* Can't offload GOTO in VCAP ES0 */
|
||||
if (!ingress)
|
||||
return (goto_target < 0);
|
||||
|
||||
/* Non-optional GOTOs */
|
||||
if (chain == 0)
|
||||
/* VCAP IS1 can be skipped, either partially or completely */
|
||||
return (goto_target == VCAP_IS1_CHAIN(0) ||
|
||||
goto_target == VCAP_IS1_CHAIN(1) ||
|
||||
goto_target == VCAP_IS1_CHAIN(2) ||
|
||||
goto_target == VCAP_IS2_CHAIN(0, 0) ||
|
||||
goto_target == VCAP_IS2_CHAIN(1, 0));
|
||||
|
||||
if (chain == VCAP_IS1_CHAIN(0))
|
||||
return (goto_target == VCAP_IS1_CHAIN(1));
|
||||
|
||||
if (chain == VCAP_IS1_CHAIN(1))
|
||||
return (goto_target == VCAP_IS1_CHAIN(2));
|
||||
|
||||
/* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,
|
||||
* using a Policy Association Group (PAG) value, which is an 8-bit
|
||||
* value encoding a VCAP IS2 target chain.
|
||||
*/
|
||||
if (chain == VCAP_IS1_CHAIN(2)) {
|
||||
for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
|
||||
if (goto_target == VCAP_IS2_CHAIN(0, pag))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
|
||||
* We cannot change the PAG at this point.
|
||||
*/
|
||||
for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
|
||||
if (chain == VCAP_IS2_CHAIN(0, pag))
|
||||
return (goto_target == VCAP_IS2_CHAIN(1, pag));
|
||||
|
||||
/* VCAP IS2 lookup 1 cannot jump anywhere */
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct ocelot_vcap_filter *
|
||||
ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
|
||||
{
|
||||
struct ocelot_vcap_filter *filter;
|
||||
struct ocelot_vcap_block *block;
|
||||
int block_id;
|
||||
|
||||
block_id = ocelot_chain_to_block(chain, true);
|
||||
if (block_id < 0)
|
||||
return NULL;
|
||||
|
||||
if (block_id == VCAP_IS2) {
|
||||
block = &ocelot->block[VCAP_IS1];
|
||||
|
||||
list_for_each_entry(filter, &block->rules, list)
|
||||
if (filter->type == OCELOT_VCAP_FILTER_PAG &&
|
||||
filter->goto_target == chain)
|
||||
return filter;
|
||||
}
|
||||
|
||||
list_for_each_entry(filter, &ocelot->dummy_rules, list)
|
||||
if (filter->goto_target == chain)
|
||||
return filter;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ocelot_flower_parse_action(struct ocelot *ocelot, bool ingress,
|
||||
struct flow_cls_offload *f,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct netlink_ext_ack *extack = f->common.extack;
|
||||
bool allow_missing_goto_target = false;
|
||||
const struct flow_action_entry *a;
|
||||
enum ocelot_tag_tpid_sel tpid;
|
||||
int i, chain, egress_port;
|
||||
u64 rate;
|
||||
int i;
|
||||
|
||||
if (!flow_offload_has_one_action(&f->rule->action))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!flow_action_basic_hw_stats_check(&f->rule->action,
|
||||
f->common.extack))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
chain = f->common.chain_index;
|
||||
filter->block_id = ocelot_chain_to_block(chain, ingress);
|
||||
if (filter->block_id < 0) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)
|
||||
filter->lookup = ocelot_chain_to_lookup(chain);
|
||||
if (filter->block_id == VCAP_IS2)
|
||||
filter->pag = ocelot_chain_to_pag(chain);
|
||||
|
||||
filter->goto_target = -1;
|
||||
filter->type = OCELOT_VCAP_FILTER_DUMMY;
|
||||
|
||||
flow_action_for_each(i, a, &f->rule->action) {
|
||||
switch (a->id) {
|
||||
case FLOW_ACTION_DROP:
|
||||
filter->action = OCELOT_VCAP_ACTION_DROP;
|
||||
if (filter->block_id != VCAP_IS2) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Drop action can only be offloaded to VCAP IS2");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
|
||||
filter->action.port_mask = 0;
|
||||
filter->action.police_ena = true;
|
||||
filter->action.pol_ix = OCELOT_POLICER_DISCARD;
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_TRAP:
|
||||
filter->action = OCELOT_VCAP_ACTION_TRAP;
|
||||
if (filter->block_id != VCAP_IS2) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Trap action can only be offloaded to VCAP IS2");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
|
||||
filter->action.port_mask = 0;
|
||||
filter->action.cpu_copy_ena = true;
|
||||
filter->action.cpu_qu_num = 0;
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_POLICE:
|
||||
filter->action = OCELOT_VCAP_ACTION_POLICE;
|
||||
if (filter->block_id != VCAP_IS2 ||
|
||||
filter->lookup != 0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Police action can only be offloaded to VCAP IS2 lookup 0");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.police_ena = true;
|
||||
rate = a->police.rate_bytes_ps;
|
||||
filter->pol.rate = div_u64(rate, 1000) * 8;
|
||||
filter->pol.burst = a->police.burst;
|
||||
filter->action.pol.rate = div_u64(rate, 1000) * 8;
|
||||
filter->action.pol.burst = a->police.burst;
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_REDIRECT:
|
||||
if (filter->block_id != VCAP_IS2) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Redirect action can only be offloaded to VCAP IS2");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
egress_port = ocelot->ops->netdev_to_port(a->dev);
|
||||
if (egress_port < 0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Destination not an ocelot port");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
|
||||
filter->action.port_mask = BIT(egress_port);
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_VLAN_POP:
|
||||
if (filter->block_id != VCAP_IS1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VLAN pop action can only be offloaded to VCAP IS1");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.vlan_pop_cnt_ena = true;
|
||||
filter->action.vlan_pop_cnt++;
|
||||
if (filter->action.vlan_pop_cnt > 2) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Cannot pop more than 2 VLAN headers");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_PRIORITY:
|
||||
if (filter->block_id != VCAP_IS1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Priority action can only be offloaded to VCAP IS1");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (filter->goto_target != -1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Last action must be GOTO");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.qos_ena = true;
|
||||
filter->action.qos_val = a->priority;
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
case FLOW_ACTION_GOTO:
|
||||
filter->goto_target = a->chain_index;
|
||||
|
||||
if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
|
||||
int pag = ocelot_chain_to_pag(filter->goto_target);
|
||||
|
||||
filter->action.pag_override_mask = 0xff;
|
||||
filter->action.pag_val = pag;
|
||||
filter->type = OCELOT_VCAP_FILTER_PAG;
|
||||
}
|
||||
break;
|
||||
case FLOW_ACTION_VLAN_PUSH:
|
||||
if (filter->block_id != VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VLAN push action can only be offloaded to VCAP ES0");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
switch (ntohs(a->vlan.proto)) {
|
||||
case ETH_P_8021Q:
|
||||
tpid = OCELOT_TAG_TPID_SEL_8021Q;
|
||||
break;
|
||||
case ETH_P_8021AD:
|
||||
tpid = OCELOT_TAG_TPID_SEL_8021AD;
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Cannot push custom TPID");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
filter->action.tag_a_tpid_sel = tpid;
|
||||
filter->action.push_outer_tag = OCELOT_ES0_TAG;
|
||||
filter->action.tag_a_vid_sel = 1;
|
||||
filter->action.vid_a_val = a->vlan.vid;
|
||||
filter->action.pcp_a_val = a->vlan.prio;
|
||||
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->goto_target == -1) {
|
||||
if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
|
||||
chain == 0) {
|
||||
allow_missing_goto_target = true;
|
||||
} else {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&
|
||||
!allow_missing_goto_target) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
|
||||
struct flow_cls_offload *f,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
|
||||
int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
|
||||
struct netlink_ext_ack *extack = f->common.extack;
|
||||
struct net_device *dev, *indev;
|
||||
struct flow_match_meta match;
|
||||
int ingress_port;
|
||||
|
||||
flow_rule_match_meta(rule, &match);
|
||||
|
||||
if (!match.mask->ingress_ifindex)
|
||||
return 0;
|
||||
|
||||
if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
dev = ocelot->ops->port_to_netdev(ocelot, port);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
|
||||
if (!indev) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Can't find the ingress port to match on");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ingress_port = ocelot->ops->netdev_to_port(indev);
|
||||
if (ingress_port < 0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Can only offload an ocelot ingress port");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (ingress_port == port) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Ingress port is equal to the egress port");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
filter->ingress_port.value = ingress_port;
|
||||
filter->ingress_port.mask = GENMASK(key_length - 1, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
|
||||
struct flow_cls_offload *f,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
|
||||
struct flow_dissector *dissector = rule->match.dissector;
|
||||
struct netlink_ext_ack *extack = f->common.extack;
|
||||
u16 proto = ntohs(f->common.protocol);
|
||||
bool match_protocol = true;
|
||||
int ret;
|
||||
|
||||
if (dissector->used_keys &
|
||||
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
|
||||
BIT(FLOW_DISSECTOR_KEY_BASIC) |
|
||||
BIT(FLOW_DISSECTOR_KEY_META) |
|
||||
BIT(FLOW_DISSECTOR_KEY_PORTS) |
|
||||
BIT(FLOW_DISSECTOR_KEY_VLAN) |
|
||||
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
|
||||
|
@ -63,6 +417,13 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* For VCAP ES0 (egress rewriter) we can match on the ingress port */
|
||||
if (!ingress) {
|
||||
ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
|
||||
struct flow_match_control match;
|
||||
|
||||
|
@ -72,6 +433,19 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||
struct flow_match_eth_addrs match;
|
||||
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on MAC address");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (filter->block_id == VCAP_IS1 &&
|
||||
!is_zero_ether_addr(match.mask->dst)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Key type S1_NORMAL cannot match on destination MAC");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* The hw support mac matches only for MAC_ETYPE key,
|
||||
* therefore if other matches(port, tcp flags, etc) are added
|
||||
* then just bail out
|
||||
|
@ -103,6 +477,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
|
||||
flow_rule_match_basic(rule, &match);
|
||||
if (ntohs(match.key->n_proto) == ETH_P_IP) {
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on IP protocol");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
filter->key_type = OCELOT_VCAP_KEY_IPV4;
|
||||
filter->key.ipv4.proto.value[0] =
|
||||
match.key->ip_proto;
|
||||
|
@ -111,6 +491,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
match_protocol = false;
|
||||
}
|
||||
if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on IP protocol");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
filter->key_type = OCELOT_VCAP_KEY_IPV6;
|
||||
filter->key.ipv6.proto.value[0] =
|
||||
match.key->ip_proto;
|
||||
|
@ -125,6 +511,18 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
struct flow_match_ipv4_addrs match;
|
||||
u8 *tmp;
|
||||
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on IP address");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Key type S1_NORMAL cannot match on destination IP");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_rule_match_ipv4_addrs(rule, &match);
|
||||
tmp = &filter->key.ipv4.sip.value.addr[0];
|
||||
memcpy(tmp, &match.key->src, 4);
|
||||
|
@ -148,6 +546,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
|
||||
struct flow_match_ports match;
|
||||
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on L4 ports");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_rule_match_ports(rule, &match);
|
||||
filter->key.ipv4.sport.value = ntohs(match.key->src);
|
||||
filter->key.ipv4.sport.mask = ntohs(match.mask->src);
|
||||
|
@ -170,6 +574,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
|
||||
finished_key_parsing:
|
||||
if (match_protocol && proto != ETH_P_ALL) {
|
||||
if (filter->block_id == VCAP_ES0) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"VCAP ES0 cannot match on L2 proto");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* TODO: support SNAP, LLC etc */
|
||||
if (proto < ETH_P_802_3_MIN)
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -182,7 +592,8 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_flower_parse(struct flow_cls_offload *f,
|
||||
static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
|
||||
struct flow_cls_offload *f,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
int ret;
|
||||
|
@ -190,16 +601,16 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
|
|||
filter->prio = f->common.prio;
|
||||
filter->id = f->cookie;
|
||||
|
||||
ret = ocelot_flower_parse_action(f, filter);
|
||||
ret = ocelot_flower_parse_action(ocelot, ingress, f, filter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ocelot_flower_parse_key(f, filter);
|
||||
return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
|
||||
}
|
||||
|
||||
static struct ocelot_vcap_filter
|
||||
*ocelot_vcap_filter_create(struct ocelot *ocelot, int port,
|
||||
struct flow_cls_offload *f)
|
||||
*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
struct ocelot_vcap_filter *filter;
|
||||
|
||||
|
@ -207,26 +618,65 @@ static struct ocelot_vcap_filter
|
|||
if (!filter)
|
||||
return NULL;
|
||||
|
||||
filter->ingress_port_mask = BIT(port);
|
||||
if (ingress) {
|
||||
filter->ingress_port_mask = BIT(port);
|
||||
} else {
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
|
||||
int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
|
||||
|
||||
filter->egress_port.value = port;
|
||||
filter->egress_port.mask = GENMASK(key_length - 1, 0);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
list_add(&filter->list, &ocelot->dummy_rules);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
list_del(&filter->list);
|
||||
kfree(filter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
|
||||
struct flow_cls_offload *f, bool ingress)
|
||||
{
|
||||
struct netlink_ext_ack *extack = f->common.extack;
|
||||
struct ocelot_vcap_filter *filter;
|
||||
int chain = f->common.chain_index;
|
||||
int ret;
|
||||
|
||||
filter = ocelot_vcap_filter_create(ocelot, port, f);
|
||||
if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
|
||||
if (!filter)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ocelot_flower_parse(f, filter);
|
||||
ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
|
||||
if (ret) {
|
||||
kfree(filter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The non-optional GOTOs for the TCAM skeleton don't need
|
||||
* to be actually offloaded.
|
||||
*/
|
||||
if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
|
||||
return ocelot_vcap_dummy_filter_add(ocelot, filter);
|
||||
|
||||
return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
|
||||
|
@ -234,13 +684,23 @@ EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
|
|||
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
|
||||
struct flow_cls_offload *f, bool ingress)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_filter *filter;
|
||||
struct ocelot_vcap_block *block;
|
||||
int block_id;
|
||||
|
||||
block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
|
||||
if (block_id < 0)
|
||||
return 0;
|
||||
|
||||
block = &ocelot->block[block_id];
|
||||
|
||||
filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
|
||||
if (!filter)
|
||||
return 0;
|
||||
|
||||
if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
|
||||
return ocelot_vcap_dummy_filter_del(ocelot, filter);
|
||||
|
||||
return ocelot_vcap_filter_del(ocelot, filter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
|
||||
|
@ -248,12 +708,18 @@ EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
|
|||
int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
|
||||
struct flow_cls_offload *f, bool ingress)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_filter *filter;
|
||||
int ret;
|
||||
struct ocelot_vcap_block *block;
|
||||
int block_id, ret;
|
||||
|
||||
block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
|
||||
if (block_id < 0)
|
||||
return 0;
|
||||
|
||||
block = &ocelot->block[block_id];
|
||||
|
||||
filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
|
||||
if (!filter)
|
||||
if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
|
||||
return 0;
|
||||
|
||||
ret = ocelot_vcap_filter_stats_update(ocelot, filter);
|
||||
|
|
|
@ -656,6 +656,36 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
|
|||
.ndo_do_ioctl = ocelot_ioctl,
|
||||
};
|
||||
|
||||
struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port)
|
||||
{
|
||||
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||||
struct ocelot_port_private *priv;
|
||||
|
||||
if (!ocelot_port)
|
||||
return NULL;
|
||||
|
||||
priv = container_of(ocelot_port, struct ocelot_port_private, port);
|
||||
|
||||
return priv->dev;
|
||||
}
|
||||
|
||||
static bool ocelot_port_dev_check(const struct net_device *dev)
|
||||
{
|
||||
return dev->netdev_ops == &ocelot_port_netdev_ops;
|
||||
}
|
||||
|
||||
int ocelot_netdev_to_port(struct net_device *dev)
|
||||
{
|
||||
struct ocelot_port_private *priv;
|
||||
|
||||
if (!dev || !ocelot_port_dev_check(dev))
|
||||
return -EINVAL;
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
return priv->chip_port;
|
||||
}
|
||||
|
||||
static void ocelot_port_get_strings(struct net_device *netdev, u32 sset,
|
||||
u8 *data)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "ocelot_police.h"
|
||||
#include "ocelot_vcap.h"
|
||||
|
||||
#define OCELOT_POLICER_DISCARD 0x17f
|
||||
#define ENTRY_WIDTH 32
|
||||
|
||||
enum vcap_sel {
|
||||
|
@ -332,35 +331,14 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
|
|||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS2];
|
||||
struct ocelot_vcap_action *a = &filter->action;
|
||||
|
||||
switch (filter->action) {
|
||||
case OCELOT_VCAP_ACTION_DROP:
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_PORT_MASK, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, 1);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, 1);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_IDX,
|
||||
OCELOT_POLICER_DISCARD);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
|
||||
break;
|
||||
case OCELOT_VCAP_ACTION_TRAP:
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_PORT_MASK, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, 1);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_IDX, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1);
|
||||
break;
|
||||
case OCELOT_VCAP_ACTION_POLICE:
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_PORT_MASK, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, 1);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_IDX,
|
||||
filter->pol_ix);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
|
||||
break;
|
||||
}
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, a->mask_mode);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_PORT_MASK, a->port_mask);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, a->police_ena);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_IDX, a->pol_ix);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_QU_NUM, a->cpu_qu_num);
|
||||
vcap_action_set(vcap, data, VCAP_IS2_ACT_CPU_COPY_ENA, a->cpu_copy_ena);
|
||||
}
|
||||
|
||||
static void is2_entry_set(struct ocelot *ocelot, int ix,
|
||||
|
@ -389,7 +367,10 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
|
|||
|
||||
data.type = IS2_ACTION_TYPE_NORMAL;
|
||||
|
||||
vcap_key_set(vcap, &data, VCAP_IS2_HK_PAG, 0, 0);
|
||||
vcap_key_set(vcap, &data, VCAP_IS2_HK_PAG, filter->pag, 0xff);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_FIRST,
|
||||
(filter->lookup == 0) ? OCELOT_VCAP_BIT_1 :
|
||||
OCELOT_VCAP_BIT_0);
|
||||
vcap_key_set(vcap, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0,
|
||||
~filter->ingress_port_mask);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_ANY);
|
||||
|
@ -662,15 +643,227 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
|
|||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
|
||||
}
|
||||
|
||||
static void
|
||||
vcap_entry_get(struct ocelot *ocelot, struct ocelot_vcap_filter *filter, int ix)
|
||||
static void is1_action_set(struct ocelot *ocelot, struct vcap_data *data,
|
||||
const struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS2];
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1];
|
||||
const struct ocelot_vcap_action *a = &filter->action;
|
||||
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_VID_REPLACE_ENA,
|
||||
a->vid_replace_ena);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_VID_ADD_VAL, a->vid);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_VLAN_POP_CNT_ENA,
|
||||
a->vlan_pop_cnt_ena);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_VLAN_POP_CNT,
|
||||
a->vlan_pop_cnt);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_PCP_DEI_ENA, a->pcp_dei_ena);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_PCP_VAL, a->pcp);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_DEI_VAL, a->dei);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_QOS_ENA, a->qos_ena);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_QOS_VAL, a->qos_val);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_OVERRIDE_MASK,
|
||||
a->pag_override_mask);
|
||||
vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_VAL, a->pag_val);
|
||||
}
|
||||
|
||||
static void is1_entry_set(struct ocelot *ocelot, int ix,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1];
|
||||
struct ocelot_vcap_key_vlan *tag = &filter->vlan;
|
||||
struct ocelot_vcap_u64 payload;
|
||||
struct vcap_data data;
|
||||
int row = ix / 2;
|
||||
u32 type;
|
||||
|
||||
memset(&payload, 0, sizeof(payload));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
/* Read row */
|
||||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_ALL);
|
||||
vcap_cache2entry(ocelot, vcap, &data);
|
||||
vcap_cache2action(ocelot, vcap, &data);
|
||||
|
||||
data.tg_sw = VCAP_TG_HALF;
|
||||
data.type = IS1_ACTION_TYPE_NORMAL;
|
||||
vcap_data_offset_get(vcap, &data, ix);
|
||||
data.tg = (data.tg & ~data.tg_mask);
|
||||
if (filter->prio != 0)
|
||||
data.tg |= data.tg_value;
|
||||
|
||||
vcap_key_set(vcap, &data, VCAP_IS1_HK_LOOKUP, filter->lookup, 0x3);
|
||||
vcap_key_set(vcap, &data, VCAP_IS1_HK_IGR_PORT_MASK, 0,
|
||||
~filter->ingress_port_mask);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_MC, filter->dmac_mc);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_BC, filter->dmac_bc);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_TAGGED, tag->tagged);
|
||||
vcap_key_set(vcap, &data, VCAP_IS1_HK_VID,
|
||||
tag->vid.value, tag->vid.mask);
|
||||
vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP,
|
||||
tag->pcp.value[0], tag->pcp.mask[0]);
|
||||
type = IS1_TYPE_S1_NORMAL;
|
||||
|
||||
switch (filter->key_type) {
|
||||
case OCELOT_VCAP_KEY_ETYPE: {
|
||||
struct ocelot_vcap_key_etype *etype = &filter->key.etype;
|
||||
|
||||
vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L2_SMAC,
|
||||
etype->smac.value, etype->smac.mask);
|
||||
vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE,
|
||||
etype->etype.value, etype->etype.mask);
|
||||
break;
|
||||
}
|
||||
case OCELOT_VCAP_KEY_IPV4: {
|
||||
struct ocelot_vcap_key_ipv4 *ipv4 = &filter->key.ipv4;
|
||||
struct ocelot_vcap_udp_tcp *sport = &ipv4->sport;
|
||||
struct ocelot_vcap_udp_tcp *dport = &ipv4->dport;
|
||||
enum ocelot_vcap_bit tcp_udp = OCELOT_VCAP_BIT_0;
|
||||
struct ocelot_vcap_u8 proto = ipv4->proto;
|
||||
struct ocelot_vcap_ipv4 sip = ipv4->sip;
|
||||
u32 val, msk;
|
||||
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP,
|
||||
OCELOT_VCAP_BIT_1);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4,
|
||||
OCELOT_VCAP_BIT_1);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN,
|
||||
OCELOT_VCAP_BIT_1);
|
||||
vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP,
|
||||
sip.value.addr, sip.mask.addr);
|
||||
|
||||
val = proto.value[0];
|
||||
msk = proto.mask[0];
|
||||
|
||||
if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && msk == 0xff)
|
||||
tcp_udp = OCELOT_VCAP_BIT_1;
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP_UDP, tcp_udp);
|
||||
|
||||
if (tcp_udp) {
|
||||
enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0;
|
||||
|
||||
if (val == NEXTHDR_TCP)
|
||||
tcp = OCELOT_VCAP_BIT_1;
|
||||
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, tcp);
|
||||
vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_L4_SPORT,
|
||||
sport);
|
||||
/* Overloaded field */
|
||||
vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_ETYPE,
|
||||
dport);
|
||||
} else {
|
||||
/* IPv4 "other" frame */
|
||||
struct ocelot_vcap_u16 etype = {0};
|
||||
|
||||
/* Overloaded field */
|
||||
etype.value[0] = proto.value[0];
|
||||
etype.mask[0] = proto.mask[0];
|
||||
|
||||
vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE,
|
||||
etype.value, etype.mask);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TYPE,
|
||||
type ? OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0);
|
||||
|
||||
is1_action_set(ocelot, &data, filter);
|
||||
vcap_data_set(data.counter, data.counter_offset,
|
||||
vcap->counter_width, filter->stats.pkts);
|
||||
|
||||
/* Write row */
|
||||
vcap_entry2cache(ocelot, vcap, &data);
|
||||
vcap_action2cache(ocelot, vcap, &data);
|
||||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
|
||||
}
|
||||
|
||||
static void es0_action_set(struct ocelot *ocelot, struct vcap_data *data,
|
||||
const struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
|
||||
const struct ocelot_vcap_action *a = &filter->action;
|
||||
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_PUSH_OUTER_TAG,
|
||||
a->push_outer_tag);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_PUSH_INNER_TAG,
|
||||
a->push_inner_tag);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_TPID_SEL,
|
||||
a->tag_a_tpid_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_VID_SEL,
|
||||
a->tag_a_vid_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_PCP_SEL,
|
||||
a->tag_a_pcp_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_VID_A_VAL, a->vid_a_val);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_PCP_A_VAL, a->pcp_a_val);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_TPID_SEL,
|
||||
a->tag_b_tpid_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_VID_SEL,
|
||||
a->tag_b_vid_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_PCP_SEL,
|
||||
a->tag_b_pcp_sel);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_VID_B_VAL, a->vid_b_val);
|
||||
vcap_action_set(vcap, data, VCAP_ES0_ACT_PCP_B_VAL, a->pcp_b_val);
|
||||
}
|
||||
|
||||
static void es0_entry_set(struct ocelot *ocelot, int ix,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
|
||||
struct ocelot_vcap_key_vlan *tag = &filter->vlan;
|
||||
struct ocelot_vcap_u64 payload;
|
||||
struct vcap_data data;
|
||||
int row = ix;
|
||||
|
||||
memset(&payload, 0, sizeof(payload));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
/* Read row */
|
||||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_ALL);
|
||||
vcap_cache2entry(ocelot, vcap, &data);
|
||||
vcap_cache2action(ocelot, vcap, &data);
|
||||
|
||||
data.tg_sw = VCAP_TG_FULL;
|
||||
data.type = ES0_ACTION_TYPE_NORMAL;
|
||||
vcap_data_offset_get(vcap, &data, ix);
|
||||
data.tg = (data.tg & ~data.tg_mask);
|
||||
if (filter->prio != 0)
|
||||
data.tg |= data.tg_value;
|
||||
|
||||
vcap_key_set(vcap, &data, VCAP_ES0_IGR_PORT, filter->ingress_port.value,
|
||||
filter->ingress_port.mask);
|
||||
vcap_key_set(vcap, &data, VCAP_ES0_EGR_PORT, filter->egress_port.value,
|
||||
filter->egress_port.mask);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_ES0_L2_MC, filter->dmac_mc);
|
||||
vcap_key_bit_set(vcap, &data, VCAP_ES0_L2_BC, filter->dmac_bc);
|
||||
vcap_key_set(vcap, &data, VCAP_ES0_VID,
|
||||
tag->vid.value, tag->vid.mask);
|
||||
vcap_key_set(vcap, &data, VCAP_ES0_PCP,
|
||||
tag->pcp.value[0], tag->pcp.mask[0]);
|
||||
|
||||
es0_action_set(ocelot, &data, filter);
|
||||
vcap_data_set(data.counter, data.counter_offset,
|
||||
vcap->counter_width, filter->stats.pkts);
|
||||
|
||||
/* Write row */
|
||||
vcap_entry2cache(ocelot, vcap, &data);
|
||||
vcap_action2cache(ocelot, vcap, &data);
|
||||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
|
||||
}
|
||||
|
||||
static void vcap_entry_get(struct ocelot *ocelot, int ix,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
const struct vcap_props *vcap = &ocelot->vcap[filter->block_id];
|
||||
struct vcap_data data;
|
||||
int row, count;
|
||||
u32 cnt;
|
||||
|
||||
data.tg_sw = VCAP_TG_HALF;
|
||||
if (filter->block_id == VCAP_ES0)
|
||||
data.tg_sw = VCAP_TG_FULL;
|
||||
else
|
||||
data.tg_sw = VCAP_TG_HALF;
|
||||
|
||||
count = (1 << (data.tg_sw - 1));
|
||||
row = (ix / count);
|
||||
vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_COUNTER);
|
||||
|
@ -682,6 +875,17 @@ vcap_entry_get(struct ocelot *ocelot, struct ocelot_vcap_filter *filter, int ix)
|
|||
filter->stats.pkts = cnt;
|
||||
}
|
||||
|
||||
static void vcap_entry_set(struct ocelot *ocelot, int ix,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
if (filter->block_id == VCAP_IS1)
|
||||
return is1_entry_set(ocelot, ix, filter);
|
||||
if (filter->block_id == VCAP_IS2)
|
||||
return is2_entry_set(ocelot, ix, filter);
|
||||
if (filter->block_id == VCAP_ES0)
|
||||
return es0_entry_set(ocelot, ix, filter);
|
||||
}
|
||||
|
||||
static int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix,
|
||||
struct ocelot_policer *pol)
|
||||
{
|
||||
|
@ -710,11 +914,12 @@ static void ocelot_vcap_policer_del(struct ocelot *ocelot,
|
|||
|
||||
list_for_each_entry(filter, &block->rules, list) {
|
||||
index++;
|
||||
if (filter->action == OCELOT_VCAP_ACTION_POLICE &&
|
||||
filter->pol_ix < pol_ix) {
|
||||
filter->pol_ix += 1;
|
||||
ocelot_vcap_policer_add(ocelot, filter->pol_ix,
|
||||
&filter->pol);
|
||||
if (filter->block_id == VCAP_IS2 &&
|
||||
filter->action.police_ena &&
|
||||
filter->action.pol_ix < pol_ix) {
|
||||
filter->action.pol_ix += 1;
|
||||
ocelot_vcap_policer_add(ocelot, filter->action.pol_ix,
|
||||
&filter->action.pol);
|
||||
is2_entry_set(ocelot, index, filter);
|
||||
}
|
||||
}
|
||||
|
@ -732,10 +937,11 @@ static void ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
|
|||
struct ocelot_vcap_filter *tmp;
|
||||
struct list_head *pos, *n;
|
||||
|
||||
if (filter->action == OCELOT_VCAP_ACTION_POLICE) {
|
||||
if (filter->block_id == VCAP_IS2 && filter->action.police_ena) {
|
||||
block->pol_lpr--;
|
||||
filter->pol_ix = block->pol_lpr;
|
||||
ocelot_vcap_policer_add(ocelot, filter->pol_ix, &filter->pol);
|
||||
filter->action.pol_ix = block->pol_lpr;
|
||||
ocelot_vcap_policer_add(ocelot, filter->action.pol_ix,
|
||||
&filter->action.pol);
|
||||
}
|
||||
|
||||
block->count++;
|
||||
|
@ -807,23 +1013,23 @@ ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block, int id)
|
|||
* on any _other_ keys than MAC_ETYPE ones.
|
||||
*/
|
||||
static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port,
|
||||
bool on)
|
||||
int lookup, bool on)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (on)
|
||||
val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(3) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(3) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(3) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(3) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(3);
|
||||
val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(BIT(lookup));
|
||||
|
||||
ocelot_rmw_gix(ocelot, val,
|
||||
ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_M |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_M |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_M |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_M |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS_M,
|
||||
ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(BIT(lookup)) |
|
||||
ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(BIT(lookup)),
|
||||
ANA_PORT_VCAP_S2_CFG, port);
|
||||
}
|
||||
|
||||
|
@ -869,35 +1075,43 @@ static bool
|
|||
ocelot_exclusive_mac_etype_filter_rules(struct ocelot *ocelot,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_block *block = &ocelot->block[filter->block_id];
|
||||
struct ocelot_vcap_filter *tmp;
|
||||
unsigned long port;
|
||||
int i;
|
||||
|
||||
/* We only have the S2_IP_TCPUDP_DIS set of knobs for VCAP IS2 */
|
||||
if (filter->block_id != VCAP_IS2)
|
||||
return true;
|
||||
|
||||
if (ocelot_vcap_is_problematic_mac_etype(filter)) {
|
||||
/* Search for any non-MAC_ETYPE rules on the port */
|
||||
for (i = 0; i < block->count; i++) {
|
||||
tmp = ocelot_vcap_block_find_filter_by_index(block, i);
|
||||
if (tmp->ingress_port_mask & filter->ingress_port_mask &&
|
||||
tmp->lookup == filter->lookup &&
|
||||
ocelot_vcap_is_problematic_non_mac_etype(tmp))
|
||||
return false;
|
||||
}
|
||||
|
||||
for_each_set_bit(port, &filter->ingress_port_mask,
|
||||
ocelot->num_phys_ports)
|
||||
ocelot_match_all_as_mac_etype(ocelot, port, true);
|
||||
ocelot_match_all_as_mac_etype(ocelot, port,
|
||||
filter->lookup, true);
|
||||
} else if (ocelot_vcap_is_problematic_non_mac_etype(filter)) {
|
||||
/* Search for any MAC_ETYPE rules on the port */
|
||||
for (i = 0; i < block->count; i++) {
|
||||
tmp = ocelot_vcap_block_find_filter_by_index(block, i);
|
||||
if (tmp->ingress_port_mask & filter->ingress_port_mask &&
|
||||
tmp->lookup == filter->lookup &&
|
||||
ocelot_vcap_is_problematic_mac_etype(tmp))
|
||||
return false;
|
||||
}
|
||||
|
||||
for_each_set_bit(port, &filter->ingress_port_mask,
|
||||
ocelot->num_phys_ports)
|
||||
ocelot_match_all_as_mac_etype(ocelot, port, false);
|
||||
ocelot_match_all_as_mac_etype(ocelot, port,
|
||||
filter->lookup, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -907,12 +1121,12 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
|
|||
struct ocelot_vcap_filter *filter,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_block *block = &ocelot->block[filter->block_id];
|
||||
int i, index;
|
||||
|
||||
if (!ocelot_exclusive_mac_etype_filter_rules(ocelot, filter)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
|
||||
"Cannot mix MAC_ETYPE with non-MAC_ETYPE rules, use the other IS2 lookup");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -929,11 +1143,11 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
|
|||
struct ocelot_vcap_filter *tmp;
|
||||
|
||||
tmp = ocelot_vcap_block_find_filter_by_index(block, i);
|
||||
is2_entry_set(ocelot, i, tmp);
|
||||
vcap_entry_set(ocelot, i, tmp);
|
||||
}
|
||||
|
||||
/* Now insert the new filter */
|
||||
is2_entry_set(ocelot, index, filter);
|
||||
vcap_entry_set(ocelot, index, filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -947,9 +1161,10 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
|
|||
list_for_each_safe(pos, q, &block->rules) {
|
||||
tmp = list_entry(pos, struct ocelot_vcap_filter, list);
|
||||
if (tmp->id == filter->id) {
|
||||
if (tmp->action == OCELOT_VCAP_ACTION_POLICE)
|
||||
if (tmp->block_id == VCAP_IS2 &&
|
||||
tmp->action.police_ena)
|
||||
ocelot_vcap_policer_del(ocelot, block,
|
||||
tmp->pol_ix);
|
||||
tmp->action.pol_ix);
|
||||
|
||||
list_del(pos);
|
||||
kfree(tmp);
|
||||
|
@ -962,7 +1177,7 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
|
|||
int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_block *block = &ocelot->block[filter->block_id];
|
||||
struct ocelot_vcap_filter del_filter;
|
||||
int i, index;
|
||||
|
||||
|
@ -981,11 +1196,11 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
|||
struct ocelot_vcap_filter *tmp;
|
||||
|
||||
tmp = ocelot_vcap_block_find_filter_by_index(block, i);
|
||||
is2_entry_set(ocelot, i, tmp);
|
||||
vcap_entry_set(ocelot, i, tmp);
|
||||
}
|
||||
|
||||
/* Now delete the last filter, because it is duplicated */
|
||||
is2_entry_set(ocelot, block->count, &del_filter);
|
||||
vcap_entry_set(ocelot, block->count, &del_filter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -993,7 +1208,7 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
|
|||
int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
|
||||
struct ocelot_vcap_filter *filter)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
struct ocelot_vcap_block *block = &ocelot->block[filter->block_id];
|
||||
struct ocelot_vcap_filter tmp;
|
||||
int index;
|
||||
|
||||
|
@ -1001,12 +1216,12 @@ int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
|
|||
if (index < 0)
|
||||
return index;
|
||||
|
||||
vcap_entry_get(ocelot, filter, index);
|
||||
vcap_entry_get(ocelot, index, filter);
|
||||
|
||||
/* After we get the result we need to clear the counters */
|
||||
tmp = *filter;
|
||||
tmp.stats.pkts = 0;
|
||||
is2_entry_set(ocelot, index, &tmp);
|
||||
vcap_entry_set(ocelot, index, &tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1101,7 +1316,6 @@ static void ocelot_vcap_detect_constants(struct ocelot *ocelot,
|
|||
|
||||
int ocelot_vcap_init(struct ocelot *ocelot)
|
||||
{
|
||||
struct ocelot_vcap_block *block = &ocelot->block;
|
||||
int i;
|
||||
|
||||
/* Create a policer that will drop the frames for the cpu.
|
||||
|
@ -1120,15 +1334,17 @@ int ocelot_vcap_init(struct ocelot *ocelot)
|
|||
OCELOT_POLICER_DISCARD);
|
||||
|
||||
for (i = 0; i < OCELOT_NUM_VCAP_BLOCKS; i++) {
|
||||
struct ocelot_vcap_block *block = &ocelot->block[i];
|
||||
struct vcap_props *vcap = &ocelot->vcap[i];
|
||||
|
||||
INIT_LIST_HEAD(&block->rules);
|
||||
block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
|
||||
|
||||
ocelot_vcap_detect_constants(ocelot, vcap);
|
||||
ocelot_vcap_init_one(ocelot, vcap);
|
||||
}
|
||||
|
||||
block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
|
||||
|
||||
INIT_LIST_HEAD(&block->rules);
|
||||
INIT_LIST_HEAD(&ocelot->dummy_rules);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <net/sch_generic.h>
|
||||
#include <net/pkt_cls.h>
|
||||
|
||||
#define OCELOT_POLICER_DISCARD 0x17f
|
||||
|
||||
struct ocelot_ipv4 {
|
||||
u8 addr[4];
|
||||
};
|
||||
|
@ -76,6 +78,11 @@ struct ocelot_vcap_udp_tcp {
|
|||
u16 mask;
|
||||
};
|
||||
|
||||
struct ocelot_vcap_port {
|
||||
u8 value;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
enum ocelot_vcap_key_type {
|
||||
OCELOT_VCAP_KEY_ANY,
|
||||
OCELOT_VCAP_KEY_ETYPE,
|
||||
|
@ -158,6 +165,7 @@ struct ocelot_vcap_key_ipv4 {
|
|||
struct ocelot_vcap_key_ipv6 {
|
||||
struct ocelot_vcap_u8 proto; /* IPv6 protocol */
|
||||
struct ocelot_vcap_u128 sip; /* IPv6 source (byte 0-7 ignored) */
|
||||
struct ocelot_vcap_u128 dip; /* IPv6 destination (byte 0-7 ignored) */
|
||||
enum ocelot_vcap_bit ttl; /* TTL zero */
|
||||
struct ocelot_vcap_u8 ds;
|
||||
struct ocelot_vcap_u48 data; /* Not UDP/TCP: IP data */
|
||||
|
@ -174,10 +182,71 @@ struct ocelot_vcap_key_ipv6 {
|
|||
enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */
|
||||
};
|
||||
|
||||
enum ocelot_vcap_action {
|
||||
OCELOT_VCAP_ACTION_DROP,
|
||||
OCELOT_VCAP_ACTION_TRAP,
|
||||
OCELOT_VCAP_ACTION_POLICE,
|
||||
enum ocelot_mask_mode {
|
||||
OCELOT_MASK_MODE_NONE,
|
||||
OCELOT_MASK_MODE_PERMIT_DENY,
|
||||
OCELOT_MASK_MODE_POLICY,
|
||||
OCELOT_MASK_MODE_REDIRECT,
|
||||
};
|
||||
|
||||
enum ocelot_es0_tag {
|
||||
OCELOT_NO_ES0_TAG,
|
||||
OCELOT_ES0_TAG,
|
||||
OCELOT_FORCE_PORT_TAG,
|
||||
OCELOT_FORCE_UNTAG,
|
||||
};
|
||||
|
||||
enum ocelot_tag_tpid_sel {
|
||||
OCELOT_TAG_TPID_SEL_8021Q,
|
||||
OCELOT_TAG_TPID_SEL_8021AD,
|
||||
};
|
||||
|
||||
struct ocelot_vcap_action {
|
||||
union {
|
||||
/* VCAP ES0 */
|
||||
struct {
|
||||
enum ocelot_es0_tag push_outer_tag;
|
||||
enum ocelot_es0_tag push_inner_tag;
|
||||
enum ocelot_tag_tpid_sel tag_a_tpid_sel;
|
||||
int tag_a_vid_sel;
|
||||
int tag_a_pcp_sel;
|
||||
u16 vid_a_val;
|
||||
u8 pcp_a_val;
|
||||
u8 dei_a_val;
|
||||
enum ocelot_tag_tpid_sel tag_b_tpid_sel;
|
||||
int tag_b_vid_sel;
|
||||
int tag_b_pcp_sel;
|
||||
u16 vid_b_val;
|
||||
u8 pcp_b_val;
|
||||
u8 dei_b_val;
|
||||
};
|
||||
|
||||
/* VCAP IS1 */
|
||||
struct {
|
||||
bool vid_replace_ena;
|
||||
u16 vid;
|
||||
bool vlan_pop_cnt_ena;
|
||||
int vlan_pop_cnt;
|
||||
bool pcp_dei_ena;
|
||||
u8 pcp;
|
||||
u8 dei;
|
||||
bool qos_ena;
|
||||
u8 qos_val;
|
||||
u8 pag_override_mask;
|
||||
u8 pag_val;
|
||||
};
|
||||
|
||||
/* VCAP IS2 */
|
||||
struct {
|
||||
bool cpu_copy_ena;
|
||||
u8 cpu_qu_num;
|
||||
enum ocelot_mask_mode mask_mode;
|
||||
unsigned long port_mask;
|
||||
bool police_ena;
|
||||
struct ocelot_policer pol;
|
||||
u32 pol_ix;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct ocelot_vcap_stats {
|
||||
|
@ -186,15 +255,30 @@ struct ocelot_vcap_stats {
|
|||
u64 used;
|
||||
};
|
||||
|
||||
enum ocelot_vcap_filter_type {
|
||||
OCELOT_VCAP_FILTER_DUMMY,
|
||||
OCELOT_VCAP_FILTER_PAG,
|
||||
OCELOT_VCAP_FILTER_OFFLOAD,
|
||||
};
|
||||
|
||||
struct ocelot_vcap_filter {
|
||||
struct list_head list;
|
||||
|
||||
enum ocelot_vcap_filter_type type;
|
||||
int block_id;
|
||||
int goto_target;
|
||||
int lookup;
|
||||
u8 pag;
|
||||
u16 prio;
|
||||
u32 id;
|
||||
|
||||
enum ocelot_vcap_action action;
|
||||
struct ocelot_vcap_action action;
|
||||
struct ocelot_vcap_stats stats;
|
||||
/* For VCAP IS1 and IS2 */
|
||||
unsigned long ingress_port_mask;
|
||||
/* For VCAP ES0 */
|
||||
struct ocelot_vcap_port ingress_port;
|
||||
struct ocelot_vcap_port egress_port;
|
||||
|
||||
enum ocelot_vcap_bit dmac_mc;
|
||||
enum ocelot_vcap_bit dmac_bc;
|
||||
|
@ -210,8 +294,6 @@ struct ocelot_vcap_filter {
|
|||
struct ocelot_vcap_key_ipv4 ipv4;
|
||||
struct ocelot_vcap_key_ipv6 ipv6;
|
||||
} key;
|
||||
struct ocelot_policer pol;
|
||||
u32 pol_ix;
|
||||
};
|
||||
|
||||
int ocelot_vcap_filter_add(struct ocelot *ocelot,
|
||||
|
|
|
@ -763,6 +763,8 @@ static u16 ocelot_wm_enc(u16 value)
|
|||
static const struct ocelot_ops ocelot_ops = {
|
||||
.reset = ocelot_reset,
|
||||
.wm_enc = ocelot_wm_enc,
|
||||
.port_to_netdev = ocelot_port_to_netdev,
|
||||
.netdev_to_port = ocelot_netdev_to_port,
|
||||
};
|
||||
|
||||
static const struct vcap_field vsc7514_vcap_es0_keys[] = {
|
||||
|
|
|
@ -559,6 +559,8 @@ enum ocelot_tag_prefix {
|
|||
struct ocelot;
|
||||
|
||||
struct ocelot_ops {
|
||||
struct net_device *(*port_to_netdev)(struct ocelot *ocelot, int port);
|
||||
int (*netdev_to_port)(struct net_device *dev);
|
||||
int (*reset)(struct ocelot *ocelot);
|
||||
u16 (*wm_enc)(u16 value);
|
||||
};
|
||||
|
@ -631,7 +633,8 @@ struct ocelot {
|
|||
|
||||
struct list_head multicast;
|
||||
|
||||
struct ocelot_vcap_block block;
|
||||
struct list_head dummy_rules;
|
||||
struct ocelot_vcap_block block[3];
|
||||
struct vcap_props *vcap;
|
||||
|
||||
/* Workqueue to check statistics for overflow with its lock */
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright 2020 NXP Semiconductors
|
||||
|
||||
WAIT_TIME=1
|
||||
NUM_NETIFS=4
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
|
||||
require_command tcpdump
|
||||
|
||||
#
|
||||
# +---------------------------------------------+
|
||||
# | DUT ports Generator ports |
|
||||
# | +--------+ +--------+ +--------+ +--------+ |
|
||||
# | | | | | | | | | |
|
||||
# | | eth0 | | eth1 | | eth2 | | eth3 | |
|
||||
# | | | | | | | | | |
|
||||
# +-+--------+-+--------+-+--------+-+--------+-+
|
||||
# | | | |
|
||||
# | | | |
|
||||
# | +-----------+ |
|
||||
# | |
|
||||
# +--------------------------------+
|
||||
|
||||
eth0=${NETIFS[p1]}
|
||||
eth1=${NETIFS[p2]}
|
||||
eth2=${NETIFS[p3]}
|
||||
eth3=${NETIFS[p4]}
|
||||
|
||||
eth0_mac="de:ad:be:ef:00:00"
|
||||
eth1_mac="de:ad:be:ef:00:01"
|
||||
eth2_mac="de:ad:be:ef:00:02"
|
||||
eth3_mac="de:ad:be:ef:00:03"
|
||||
|
||||
# Helpers to map a VCAP IS1 and VCAP IS2 lookup and policy to a chain number
|
||||
# used by the kernel driver. The numbers are:
|
||||
# VCAP IS1 lookup 0: 10000
|
||||
# VCAP IS1 lookup 1: 11000
|
||||
# VCAP IS1 lookup 2: 12000
|
||||
# VCAP IS2 lookup 0 policy 0: 20000
|
||||
# VCAP IS2 lookup 0 policy 1: 20001
|
||||
# VCAP IS2 lookup 0 policy 255: 20255
|
||||
# VCAP IS2 lookup 1 policy 0: 21000
|
||||
# VCAP IS2 lookup 1 policy 1: 21001
|
||||
# VCAP IS2 lookup 1 policy 255: 21255
|
||||
IS1()
|
||||
{
|
||||
local lookup=$1
|
||||
|
||||
echo $((10000 + 1000 * lookup))
|
||||
}
|
||||
|
||||
IS2()
|
||||
{
|
||||
local lookup=$1
|
||||
local pag=$2
|
||||
|
||||
echo $((20000 + 1000 * lookup + pag))
|
||||
}
|
||||
|
||||
ES0()
|
||||
{
|
||||
echo 0
|
||||
}
|
||||
|
||||
# The Ocelot switches have a fixed ingress pipeline composed of:
|
||||
#
|
||||
# +----------------------------------------------+ +-----------------------------------------+
|
||||
# | VCAP IS1 | | VCAP IS2 |
|
||||
# | | | |
|
||||
# | +----------+ +----------+ +----------+ | | +----------+ +----------+ |
|
||||
# | | Lookup 0 | | Lookup 1 | | Lookup 2 | | --+------> PAG 0: | Lookup 0 | -> | Lookup 1 | |
|
||||
# | +----------+ -> +----------+ -> +----------+ | | | +----------+ +----------+ |
|
||||
# | |key&action| |key&action| |key&action| | | | |key&action| |key&action| |
|
||||
# | |key&action| |key&action| |key&action| | | | | .. | | .. | |
|
||||
# | | .. | | .. | | .. | | | | +----------+ +----------+ |
|
||||
# | +----------+ +----------+ +----------+ | | | |
|
||||
# | selects PAG | | | +----------+ +----------+ |
|
||||
# +----------------------------------------------+ +------> PAG 1: | Lookup 0 | -> | Lookup 1 | |
|
||||
# | | +----------+ +----------+ |
|
||||
# | | |key&action| |key&action| |
|
||||
# | | | .. | | .. | |
|
||||
# | | +----------+ +----------+ |
|
||||
# | | ... |
|
||||
# | | |
|
||||
# | | +----------+ +----------+ |
|
||||
# +----> PAG 254: | Lookup 0 | -> | Lookup 1 | |
|
||||
# | | +----------+ +----------+ |
|
||||
# | | |key&action| |key&action| |
|
||||
# | | | .. | | .. | |
|
||||
# | | +----------+ +----------+ |
|
||||
# | | |
|
||||
# | | +----------+ +----------+ |
|
||||
# +----> PAG 255: | Lookup 0 | -> | Lookup 1 | |
|
||||
# | +----------+ +----------+ |
|
||||
# | |key&action| |key&action| |
|
||||
# | | .. | | .. | |
|
||||
# | +----------+ +----------+ |
|
||||
# +-----------------------------------------+
|
||||
#
|
||||
# Both the VCAP IS1 (Ingress Stage 1) and IS2 (Ingress Stage 2) are indexed
|
||||
# (looked up) multiple times: IS1 3 times, and IS2 2 times. Each filter
|
||||
# (key and action pair) can be configured to only match during the first, or
|
||||
# second, etc, lookup.
|
||||
#
|
||||
# During one TCAM lookup, the filter processing stops at the first entry that
|
||||
# matches, then the pipeline jumps to the next lookup.
|
||||
# The driver maps each individual lookup of each individual ingress TCAM to a
|
||||
# separate chain number. For correct rule offloading, it is mandatory that each
|
||||
# filter installed in one TCAM is terminated by a non-optional GOTO action to
|
||||
# the next lookup from the fixed pipeline.
|
||||
#
|
||||
# A chain can only be used if there is a GOTO action correctly set up from the
|
||||
# prior lookup in the processing pipeline. Setting up all chains is not
|
||||
# mandatory.
|
||||
|
||||
# NOTE: VCAP IS1 currently uses only S1_NORMAL half keys and VCAP IS2
|
||||
# dynamically chooses between MAC_ETYPE, ARP, IP4_TCP_UDP, IP4_OTHER, which are
|
||||
# all half keys as well.
|
||||
|
||||
create_tcam_skeleton()
|
||||
{
|
||||
local eth=$1
|
||||
|
||||
tc qdisc add dev $eth clsact
|
||||
|
||||
# VCAP IS1 is the Ingress Classification TCAM and can offload the
|
||||
# following actions:
|
||||
# - skbedit priority
|
||||
# - vlan pop
|
||||
# - vlan modify
|
||||
# - goto (only in lookup 2, the last IS1 lookup)
|
||||
tc filter add dev $eth ingress chain 0 pref 49152 flower \
|
||||
skip_sw action goto chain $(IS1 0)
|
||||
tc filter add dev $eth ingress chain $(IS1 0) pref 49152 \
|
||||
flower skip_sw action goto chain $(IS1 1)
|
||||
tc filter add dev $eth ingress chain $(IS1 1) pref 49152 \
|
||||
flower skip_sw action goto chain $(IS1 2)
|
||||
tc filter add dev $eth ingress chain $(IS1 2) pref 49152 \
|
||||
flower skip_sw action goto chain $(IS2 0 0)
|
||||
|
||||
# VCAP IS2 is the Security Enforcement ingress TCAM and can offload the
|
||||
# following actions:
|
||||
# - trap
|
||||
# - drop
|
||||
# - police
|
||||
# The two VCAP IS2 lookups can be segmented into up to 256 groups of
|
||||
# rules, called Policies. A Policy is selected through the Policy
|
||||
# Association Group (PAG) action of VCAP IS1 (which is the
|
||||
# GOTO offload).
|
||||
tc filter add dev $eth ingress chain $(IS2 0 0) pref 49152 \
|
||||
flower skip_sw action goto chain $(IS2 1 0)
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
create_tcam_skeleton $eth0
|
||||
|
||||
ip link add br0 type bridge
|
||||
ip link set $eth0 master br0
|
||||
ip link set $eth1 master br0
|
||||
ip link set br0 up
|
||||
|
||||
ip link add link $eth3 name $eth3.100 type vlan id 100
|
||||
ip link set $eth3.100 up
|
||||
|
||||
tc filter add dev $eth0 ingress chain $(IS1 1) pref 1 \
|
||||
protocol 802.1Q flower skip_sw vlan_id 100 \
|
||||
action vlan pop \
|
||||
action goto chain $(IS1 2)
|
||||
|
||||
tc filter add dev $eth0 egress chain $(ES0) pref 1 \
|
||||
flower skip_sw indev $eth1 \
|
||||
action vlan push protocol 802.1Q id 100
|
||||
|
||||
tc filter add dev $eth0 ingress chain $(IS1 0) \
|
||||
protocol ipv4 flower skip_sw src_ip 10.1.1.2 \
|
||||
action skbedit priority 7 \
|
||||
action goto chain $(IS1 1)
|
||||
|
||||
tc filter add dev $eth0 ingress chain $(IS2 0 0) \
|
||||
protocol ipv4 flower skip_sw ip_proto udp dst_port 5201 \
|
||||
action police rate 50mbit burst 64k \
|
||||
action goto chain $(IS2 1 0)
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
ip link del $eth3.100
|
||||
tc qdisc del dev $eth0 clsact
|
||||
ip link del br0
|
||||
}
|
||||
|
||||
test_vlan_pop()
|
||||
{
|
||||
printf "Testing VLAN pop.. "
|
||||
|
||||
tcpdump_start $eth2
|
||||
|
||||
# Work around Mausezahn VLAN builder bug
|
||||
# (https://github.com/netsniff-ng/netsniff-ng/issues/225) by using
|
||||
# an 8021q upper
|
||||
$MZ $eth3.100 -q -c 1 -p 64 -a $eth3_mac -b $eth2_mac -t ip
|
||||
|
||||
sleep 1
|
||||
|
||||
tcpdump_stop
|
||||
|
||||
if tcpdump_show | grep -q "$eth3_mac > $eth2_mac, ethertype IPv4"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
|
||||
tcpdump_cleanup
|
||||
}
|
||||
|
||||
test_vlan_push()
|
||||
{
|
||||
printf "Testing VLAN push.. "
|
||||
|
||||
tcpdump_start $eth3.100
|
||||
|
||||
$MZ $eth2 -q -c 1 -p 64 -a $eth2_mac -b $eth3_mac -t ip
|
||||
|
||||
sleep 1
|
||||
|
||||
tcpdump_stop
|
||||
|
||||
if tcpdump_show | grep -q "$eth2_mac > $eth3_mac"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
|
||||
tcpdump_cleanup
|
||||
}
|
||||
|
||||
test_skbedit_priority()
|
||||
{
|
||||
local num_pkts=100
|
||||
|
||||
printf "Testing frame prioritization.. "
|
||||
|
||||
before=$(ethtool_stats_get $eth0 'rx_green_prio_7')
|
||||
|
||||
$MZ $eth3 -q -c $num_pkts -p 64 -a $eth3_mac -b $eth2_mac -t ip -A 10.1.1.2
|
||||
|
||||
after=$(ethtool_stats_get $eth0 'rx_green_prio_7')
|
||||
|
||||
if [ $((after - before)) = $num_pkts ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
ALL_TESTS="
|
||||
test_vlan_pop
|
||||
test_vlan_push
|
||||
test_skbedit_priority
|
||||
"
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
|
@ -1227,3 +1227,46 @@ stop_traffic()
|
|||
# Suppress noise from killing mausezahn.
|
||||
{ kill %% && wait %%; } 2>/dev/null
|
||||
}
|
||||
|
||||
tcpdump_start()
|
||||
{
|
||||
local if_name=$1; shift
|
||||
local ns=$1; shift
|
||||
|
||||
capfile=$(mktemp)
|
||||
capout=$(mktemp)
|
||||
|
||||
if [ -z $ns ]; then
|
||||
ns_cmd=""
|
||||
else
|
||||
ns_cmd="ip netns exec ${ns}"
|
||||
fi
|
||||
|
||||
if [ -z $SUDO_USER ] ; then
|
||||
capuser=""
|
||||
else
|
||||
capuser="-Z $SUDO_USER"
|
||||
fi
|
||||
|
||||
$ns_cmd tcpdump -e -n -Q in -i $if_name \
|
||||
-s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
|
||||
cappid=$!
|
||||
|
||||
sleep 1
|
||||
}
|
||||
|
||||
tcpdump_stop()
|
||||
{
|
||||
$ns_cmd kill $cappid
|
||||
sleep 1
|
||||
}
|
||||
|
||||
tcpdump_cleanup()
|
||||
{
|
||||
rm $capfile $capout
|
||||
}
|
||||
|
||||
tcpdump_show()
|
||||
{
|
||||
tcpdump -e -n -r $capfile 2>&1
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue