Commit 0c2a01dc authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 26d0a8ed 8cd6b020
......@@ -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];
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;
}
struct ocelot_vcap_action *a = &filter->action;
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 */
......
#!/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
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment