Commit 3e66fd54 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mirroring-for-ocelot-switches'

Vladimir Oltean says:

====================
Mirroring for Ocelot switches

This series adds support for tc-matchall (port-based) and tc-flower
(flow-based) offloading of the tc-mirred action. Support has been added
for both the ocelot switchdev driver and felix DSA driver.
====================

Link: https://lore.kernel.org/r/20220316204144.2679277-1-vladimir.oltean@nxp.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2b341f75 5e497497
...@@ -2110,7 +2110,8 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, ...@@ -2110,7 +2110,8 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
EXPORT_SYMBOL(b53_get_tag_protocol); EXPORT_SYMBOL(b53_get_tag_protocol);
int b53_mirror_add(struct dsa_switch *ds, int port, int b53_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, bool ingress) struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
struct netlink_ext_ack *extack)
{ {
struct b53_device *dev = ds->priv; struct b53_device *dev = ds->priv;
u16 reg, loc; u16 reg, loc;
......
...@@ -373,7 +373,8 @@ int b53_mdb_del(struct dsa_switch *ds, int port, ...@@ -373,7 +373,8 @@ int b53_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db); struct dsa_db db);
int b53_mirror_add(struct dsa_switch *ds, int port, int b53_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, bool ingress); struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
struct netlink_ext_ack *extack);
enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
enum dsa_tag_protocol mprot); enum dsa_tag_protocol mprot);
void b53_mirror_del(struct dsa_switch *ds, int port, void b53_mirror_del(struct dsa_switch *ds, int port,
......
...@@ -1233,7 +1233,7 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, ...@@ -1233,7 +1233,7 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,
static int ksz8_port_mirror_add(struct dsa_switch *ds, int port, static int ksz8_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress, struct netlink_ext_ack *extack)
{ {
struct ksz_device *dev = ds->priv; struct ksz_device *dev = ds->priv;
......
...@@ -1018,7 +1018,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, ...@@ -1018,7 +1018,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port,
static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress, struct netlink_ext_ack *extack)
{ {
struct ksz_device *dev = ds->priv; struct ksz_device *dev = ds->priv;
......
...@@ -1714,7 +1714,7 @@ static int mt753x_mirror_port_set(unsigned int id, u32 val) ...@@ -1714,7 +1714,7 @@ static int mt753x_mirror_port_set(unsigned int id, u32 val)
static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, static int mt753x_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress, struct netlink_ext_ack *extack)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
int monitor_port; int monitor_port;
......
...@@ -6321,7 +6321,8 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, ...@@ -6321,7 +6321,8 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress,
struct netlink_ext_ack *extack)
{ {
enum mv88e6xxx_egress_direction direction = ingress ? enum mv88e6xxx_egress_direction direction = ingress ?
MV88E6XXX_EGRESS_DIR_INGRESS : MV88E6XXX_EGRESS_DIR_INGRESS :
......
...@@ -1650,6 +1650,24 @@ static void felix_port_policer_del(struct dsa_switch *ds, int port) ...@@ -1650,6 +1650,24 @@ static void felix_port_policer_del(struct dsa_switch *ds, int port)
ocelot_port_policer_del(ocelot, port); ocelot_port_policer_del(ocelot, port);
} }
static int felix_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress, struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;
return ocelot_port_mirror_add(ocelot, port, mirror->to_local_port,
ingress, extack);
}
static void felix_port_mirror_del(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_mirror_del(ocelot, port, mirror->ingress);
}
static int felix_port_setup_tc(struct dsa_switch *ds, int port, static int felix_port_setup_tc(struct dsa_switch *ds, int port,
enum tc_setup_type type, enum tc_setup_type type,
void *type_data) void *type_data)
...@@ -1880,6 +1898,8 @@ const struct dsa_switch_ops felix_switch_ops = { ...@@ -1880,6 +1898,8 @@ const struct dsa_switch_ops felix_switch_ops = {
.port_max_mtu = felix_get_max_mtu, .port_max_mtu = felix_get_max_mtu,
.port_policer_add = felix_port_policer_add, .port_policer_add = felix_port_policer_add,
.port_policer_del = felix_port_policer_del, .port_policer_del = felix_port_policer_del,
.port_mirror_add = felix_port_mirror_add,
.port_mirror_del = felix_port_mirror_del,
.cls_flower_add = felix_cls_flower_add, .cls_flower_add = felix_cls_flower_add,
.cls_flower_del = felix_cls_flower_del, .cls_flower_del = felix_cls_flower_del,
.cls_flower_stats = felix_cls_flower_stats, .cls_flower_stats = felix_cls_flower_stats,
......
...@@ -2473,7 +2473,7 @@ qca8k_port_mdb_del(struct dsa_switch *ds, int port, ...@@ -2473,7 +2473,7 @@ qca8k_port_mdb_del(struct dsa_switch *ds, int port,
static int static int
qca8k_port_mirror_add(struct dsa_switch *ds, int port, qca8k_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress, struct netlink_ext_ack *extack)
{ {
struct qca8k_priv *priv = ds->priv; struct qca8k_priv *priv = ds->priv;
int monitor_port, ret; int monitor_port, ret;
......
...@@ -2847,7 +2847,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, ...@@ -2847,7 +2847,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to,
static int sja1105_mirror_add(struct dsa_switch *ds, int port, static int sja1105_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress) bool ingress, struct netlink_ext_ack *extack)
{ {
return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port,
ingress, true); ingress, true);
......
...@@ -3023,6 +3023,82 @@ int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio) ...@@ -3023,6 +3023,82 @@ int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
} }
EXPORT_SYMBOL_GPL(ocelot_port_del_dscp_prio); EXPORT_SYMBOL_GPL(ocelot_port_del_dscp_prio);
struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to,
struct netlink_ext_ack *extack)
{
struct ocelot_mirror *m = ocelot->mirror;
if (m) {
if (m->to != to) {
NL_SET_ERR_MSG_MOD(extack,
"Mirroring already configured towards different egress port");
return ERR_PTR(-EBUSY);
}
refcount_inc(&m->refcount);
return m;
}
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
return ERR_PTR(-ENOMEM);
m->to = to;
refcount_set(&m->refcount, 1);
ocelot->mirror = m;
/* Program the mirror port to hardware */
ocelot_write(ocelot, BIT(to), ANA_MIRRORPORTS);
return m;
}
void ocelot_mirror_put(struct ocelot *ocelot)
{
struct ocelot_mirror *m = ocelot->mirror;
if (!refcount_dec_and_test(&m->refcount))
return;
ocelot_write(ocelot, 0, ANA_MIRRORPORTS);
ocelot->mirror = NULL;
kfree(m);
}
int ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to,
bool ingress, struct netlink_ext_ack *extack)
{
struct ocelot_mirror *m = ocelot_mirror_get(ocelot, to, extack);
if (IS_ERR(m))
return PTR_ERR(m);
if (ingress) {
ocelot_rmw_gix(ocelot, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
ANA_PORT_PORT_CFG, from);
} else {
ocelot_rmw(ocelot, BIT(from), BIT(from),
ANA_EMIRRORPORTS);
}
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_mirror_add);
void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress)
{
if (ingress) {
ocelot_rmw_gix(ocelot, 0, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
ANA_PORT_PORT_CFG, from);
} else {
ocelot_rmw(ocelot, 0, BIT(from), ANA_EMIRRORPORTS);
}
ocelot_mirror_put(ocelot);
}
EXPORT_SYMBOL_GPL(ocelot_port_mirror_del);
void ocelot_init_port(struct ocelot *ocelot, int port) void ocelot_init_port(struct ocelot *ocelot, int port)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
struct ocelot_port_tc { struct ocelot_port_tc {
bool block_shared; bool block_shared;
unsigned long offload_cnt; unsigned long offload_cnt;
unsigned long ingress_mirred_id;
unsigned long egress_mirred_id;
unsigned long police_id; unsigned long police_id;
}; };
...@@ -111,6 +112,10 @@ int ocelot_trap_add(struct ocelot *ocelot, int port, ...@@ -111,6 +112,10 @@ int ocelot_trap_add(struct ocelot *ocelot, int port,
void (*populate)(struct ocelot_vcap_filter *f)); void (*populate)(struct ocelot_vcap_filter *f));
int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie); int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie);
struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to,
struct netlink_ext_ack *extack);
void ocelot_mirror_put(struct ocelot *ocelot);
extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb; extern struct notifier_block ocelot_switchdev_blocking_nb;
......
...@@ -359,6 +359,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, ...@@ -359,6 +359,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
filter->action.port_mask = BIT(egress_port); filter->action.port_mask = BIT(egress_port);
filter->type = OCELOT_VCAP_FILTER_OFFLOAD; filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
break; break;
case FLOW_ACTION_MIRRED:
if (filter->block_id != VCAP_IS2) {
NL_SET_ERR_MSG_MOD(extack,
"Mirror 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->egress_port.value = egress_port;
filter->action.mirror_ena = true;
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
break;
case FLOW_ACTION_VLAN_POP: case FLOW_ACTION_VLAN_POP:
if (filter->block_id != VCAP_IS1) { if (filter->block_id != VCAP_IS1) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP
static bool ocelot_netdevice_dev_check(const struct net_device *dev);
static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp)
{ {
return devlink_priv(dlp->devlink); return devlink_priv(dlp->devlink);
...@@ -216,14 +218,14 @@ int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, ...@@ -216,14 +218,14 @@ int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
} }
} }
static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, static int ocelot_setup_tc_cls_matchall_police(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f, struct tc_cls_matchall_offload *f,
bool ingress) bool ingress,
struct netlink_ext_ack *extack)
{ {
struct netlink_ext_ack *extack = f->common.extack; struct flow_action_entry *action = &f->rule->action.entries[0];
struct ocelot *ocelot = priv->port.ocelot; struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_policer pol = { 0 }; struct ocelot_policer pol = { 0 };
struct flow_action_entry *action;
int port = priv->chip_port; int port = priv->chip_port;
int err; int err;
...@@ -232,6 +234,119 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, ...@@ -232,6 +234,119 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
NL_SET_ERR_MSG_MOD(extack,
"Only one policer per port is supported");
return -EEXIST;
}
err = ocelot_policer_validate(&f->rule->action, action, extack);
if (err)
return err;
pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
pol.burst = action->police.burst;
err = ocelot_port_policer_add(ocelot, port, &pol);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not add policer");
return err;
}
priv->tc.police_id = f->cookie;
priv->tc.offload_cnt++;
return 0;
}
static int ocelot_setup_tc_cls_matchall_mirred(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f,
bool ingress,
struct netlink_ext_ack *extack)
{
struct flow_action *action = &f->rule->action;
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_port_private *other_priv;
const struct flow_action_entry *a;
int err;
if (f->common.protocol != htons(ETH_P_ALL))
return -EOPNOTSUPP;
if (!flow_action_basic_hw_stats_check(action, extack))
return -EOPNOTSUPP;
a = &action->entries[0];
if (!a->dev)
return -EINVAL;
if (!ocelot_netdevice_dev_check(a->dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Destination not an ocelot port");
return -EOPNOTSUPP;
}
other_priv = netdev_priv(a->dev);
err = ocelot_port_mirror_add(ocelot, priv->chip_port,
other_priv->chip_port, ingress, extack);
if (err)
return err;
if (ingress)
priv->tc.ingress_mirred_id = f->cookie;
else
priv->tc.egress_mirred_id = f->cookie;
priv->tc.offload_cnt++;
return 0;
}
static int ocelot_del_tc_cls_matchall_police(struct ocelot_port_private *priv,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int err;
err = ocelot_port_policer_del(ocelot, port);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Could not delete policer");
return err;
}
priv->tc.police_id = 0;
priv->tc.offload_cnt--;
return 0;
}
static int ocelot_del_tc_cls_matchall_mirred(struct ocelot_port_private *priv,
bool ingress,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_port_mirror_del(ocelot, port, ingress);
if (ingress)
priv->tc.ingress_mirred_id = 0;
else
priv->tc.egress_mirred_id = 0;
priv->tc.offload_cnt--;
return 0;
}
static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f,
bool ingress)
{
struct netlink_ext_ack *extack = f->common.extack;
struct flow_action_entry *action;
switch (f->command) { switch (f->command) {
case TC_CLSMATCHALL_REPLACE: case TC_CLSMATCHALL_REPLACE:
if (!flow_offload_has_one_action(&f->rule->action)) { if (!flow_offload_has_one_action(&f->rule->action)) {
...@@ -242,53 +357,41 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, ...@@ -242,53 +357,41 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
if (priv->tc.block_shared) { if (priv->tc.block_shared) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Rate limit is not supported on shared blocks"); "Matchall offloads not supported on shared blocks");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
action = &f->rule->action.entries[0]; action = &f->rule->action.entries[0];
if (action->id != FLOW_ACTION_POLICE) { switch (action->id) {
case FLOW_ACTION_POLICE:
return ocelot_setup_tc_cls_matchall_police(priv, f,
ingress,
extack);
break;
case FLOW_ACTION_MIRRED:
return ocelot_setup_tc_cls_matchall_mirred(priv, f,
ingress,
extack);
default:
NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (priv->tc.police_id && priv->tc.police_id != f->cookie) { break;
NL_SET_ERR_MSG_MOD(extack,
"Only one policer per port is supported");
return -EEXIST;
}
err = ocelot_policer_validate(&f->rule->action, action,
extack);
if (err)
return err;
pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
pol.burst = action->police.burst;
err = ocelot_port_policer_add(ocelot, port, &pol);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not add policer");
return err;
}
priv->tc.police_id = f->cookie;
priv->tc.offload_cnt++;
return 0;
case TC_CLSMATCHALL_DESTROY: case TC_CLSMATCHALL_DESTROY:
if (priv->tc.police_id != f->cookie) action = &f->rule->action.entries[0];
if (f->cookie == priv->tc.police_id)
return ocelot_del_tc_cls_matchall_police(priv, extack);
else if (f->cookie == priv->tc.ingress_mirred_id ||
f->cookie == priv->tc.egress_mirred_id)
return ocelot_del_tc_cls_matchall_mirred(priv, ingress,
extack);
else
return -ENOENT; return -ENOENT;
err = ocelot_port_policer_del(ocelot, port); break;
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Could not delete policer");
return err;
}
priv->tc.police_id = 0;
priv->tc.offload_cnt--;
return 0;
case TC_CLSMATCHALL_STATS: case TC_CLSMATCHALL_STATS:
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -335,6 +335,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, ...@@ -335,6 +335,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
vcap_action_set(vcap, data, VCAP_IS2_ACT_MASK_MODE, a->mask_mode); 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_PORT_MASK, a->port_mask);
vcap_action_set(vcap, data, VCAP_IS2_ACT_MIRROR_ENA, a->mirror_ena);
vcap_action_set(vcap, data, VCAP_IS2_ACT_POLICE_ENA, a->police_ena); 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_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_QU_NUM, a->cpu_qu_num);
...@@ -955,14 +956,21 @@ int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix) ...@@ -955,14 +956,21 @@ int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix)
} }
EXPORT_SYMBOL(ocelot_vcap_policer_del); EXPORT_SYMBOL(ocelot_vcap_policer_del);
static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, static int
struct ocelot_vcap_block *block, ocelot_vcap_filter_add_aux_resources(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter) struct ocelot_vcap_filter *filter,
struct netlink_ext_ack *extack)
{ {
struct ocelot_vcap_filter *tmp; struct ocelot_mirror *m;
struct list_head *pos, *n;
int ret; int ret;
if (filter->block_id == VCAP_IS2 && filter->action.mirror_ena) {
m = ocelot_mirror_get(ocelot, filter->egress_port.value,
extack);
if (IS_ERR(m))
return PTR_ERR(m);
}
if (filter->block_id == VCAP_IS2 && filter->action.police_ena) { if (filter->block_id == VCAP_IS2 && filter->action.police_ena) {
ret = ocelot_vcap_policer_add(ocelot, filter->action.pol_ix, ret = ocelot_vcap_policer_add(ocelot, filter->action.pol_ix,
&filter->action.pol); &filter->action.pol);
...@@ -970,6 +978,33 @@ static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot, ...@@ -970,6 +978,33 @@ static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
return ret; return ret;
} }
return 0;
}
static void
ocelot_vcap_filter_del_aux_resources(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
if (filter->block_id == VCAP_IS2 && filter->action.police_ena)
ocelot_vcap_policer_del(ocelot, filter->action.pol_ix);
if (filter->block_id == VCAP_IS2 && filter->action.mirror_ena)
ocelot_mirror_put(ocelot);
}
static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
struct ocelot_vcap_block *block,
struct ocelot_vcap_filter *filter,
struct netlink_ext_ack *extack)
{
struct ocelot_vcap_filter *tmp;
struct list_head *pos, *n;
int ret;
ret = ocelot_vcap_filter_add_aux_resources(ocelot, filter, extack);
if (ret)
return ret;
block->count++; block->count++;
if (list_empty(&block->rules)) { if (list_empty(&block->rules)) {
...@@ -1168,7 +1203,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot, ...@@ -1168,7 +1203,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
} }
/* Add filter to the linked list */ /* Add filter to the linked list */
ret = ocelot_vcap_filter_add_to_block(ocelot, block, filter); ret = ocelot_vcap_filter_add_to_block(ocelot, block, filter, extack);
if (ret) if (ret)
return ret; return ret;
...@@ -1199,11 +1234,7 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot, ...@@ -1199,11 +1234,7 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
list_for_each_entry_safe(tmp, n, &block->rules, list) { list_for_each_entry_safe(tmp, n, &block->rules, list) {
if (ocelot_vcap_filter_equal(filter, tmp)) { if (ocelot_vcap_filter_equal(filter, tmp)) {
if (tmp->block_id == VCAP_IS2 && ocelot_vcap_filter_del_aux_resources(ocelot, tmp);
tmp->action.police_ena)
ocelot_vcap_policer_del(ocelot,
tmp->action.pol_ix);
list_del(&tmp->list); list_del(&tmp->list);
kfree(tmp); kfree(tmp);
} }
......
...@@ -1028,7 +1028,7 @@ struct dsa_switch_ops { ...@@ -1028,7 +1028,7 @@ struct dsa_switch_ops {
struct flow_cls_offload *cls, bool ingress); struct flow_cls_offload *cls, bool ingress);
int (*port_mirror_add)(struct dsa_switch *ds, int port, int (*port_mirror_add)(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, struct dsa_mall_mirror_tc_entry *mirror,
bool ingress); bool ingress, struct netlink_ext_ack *extack);
void (*port_mirror_del)(struct dsa_switch *ds, int port, void (*port_mirror_del)(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror); struct dsa_mall_mirror_tc_entry *mirror);
int (*port_policer_add)(struct dsa_switch *ds, int port, int (*port_policer_add)(struct dsa_switch *ds, int port,
......
...@@ -642,6 +642,11 @@ struct ocelot_lag_fdb { ...@@ -642,6 +642,11 @@ struct ocelot_lag_fdb {
struct list_head list; struct list_head list;
}; };
struct ocelot_mirror {
refcount_t refcount;
int to;
};
struct ocelot_port { struct ocelot_port {
struct ocelot *ocelot; struct ocelot *ocelot;
...@@ -723,6 +728,7 @@ struct ocelot { ...@@ -723,6 +728,7 @@ struct ocelot {
struct ocelot_vcap_block block[3]; struct ocelot_vcap_block block[3];
struct ocelot_vcap_policer vcap_pol; struct ocelot_vcap_policer vcap_pol;
struct vcap_props *vcap; struct vcap_props *vcap;
struct ocelot_mirror *mirror;
struct ocelot_psfp_list psfp; struct ocelot_psfp_list psfp;
...@@ -908,6 +914,9 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port); ...@@ -908,6 +914,9 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port);
int ocelot_port_policer_add(struct ocelot *ocelot, int port, int ocelot_port_policer_add(struct ocelot *ocelot, int port,
struct ocelot_policer *pol); struct ocelot_policer *pol);
int ocelot_port_policer_del(struct ocelot *ocelot, int port); int ocelot_port_policer_del(struct ocelot *ocelot, int port);
int ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to,
bool ingress, struct netlink_ext_ack *extack);
void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress);
int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress); struct flow_cls_offload *f, bool ingress);
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
......
...@@ -654,6 +654,7 @@ struct ocelot_vcap_action { ...@@ -654,6 +654,7 @@ struct ocelot_vcap_action {
enum ocelot_mask_mode mask_mode; enum ocelot_mask_mode mask_mode;
unsigned long port_mask; unsigned long port_mask;
bool police_ena; bool police_ena;
bool mirror_ena;
struct ocelot_policer pol; struct ocelot_policer pol;
u32 pol_ix; u32 pol_ix;
}; };
...@@ -697,6 +698,7 @@ struct ocelot_vcap_filter { ...@@ -697,6 +698,7 @@ struct ocelot_vcap_filter {
unsigned long ingress_port_mask; unsigned long ingress_port_mask;
/* For VCAP ES0 */ /* For VCAP ES0 */
struct ocelot_vcap_port ingress_port; struct ocelot_vcap_port ingress_port;
/* For VCAP IS2 mirrors and ES0 */
struct ocelot_vcap_port egress_port; struct ocelot_vcap_port egress_port;
enum ocelot_vcap_bit dmac_mc; enum ocelot_vcap_bit dmac_mc;
......
...@@ -1173,6 +1173,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, ...@@ -1173,6 +1173,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
struct tc_cls_matchall_offload *cls, struct tc_cls_matchall_offload *cls,
bool ingress) bool ingress)
{ {
struct netlink_ext_ack *extack = cls->common.extack;
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_mall_mirror_tc_entry *mirror; struct dsa_mall_mirror_tc_entry *mirror;
...@@ -1210,7 +1211,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, ...@@ -1210,7 +1211,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
mirror->to_local_port = to_dp->index; mirror->to_local_port = to_dp->index;
mirror->ingress = ingress; mirror->ingress = ingress;
err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress); err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress, extack);
if (err) { if (err) {
kfree(mall_tc_entry); kfree(mall_tc_entry);
return err; return err;
......
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