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,
EXPORT_SYMBOL(b53_get_tag_protocol);
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;
u16 reg, loc;
......
......@@ -373,7 +373,8 @@ int b53_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db);
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 mprot);
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,
static int ksz8_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
bool ingress, struct netlink_ext_ack *extack)
{
struct ksz_device *dev = ds->priv;
......
......@@ -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,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
bool ingress, struct netlink_ext_ack *extack)
{
struct ksz_device *dev = ds->priv;
......
......@@ -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,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
bool ingress, struct netlink_ext_ack *extack)
{
struct mt7530_priv *priv = ds->priv;
int monitor_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,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
bool ingress,
struct netlink_ext_ack *extack)
{
enum mv88e6xxx_egress_direction direction = ingress ?
MV88E6XXX_EGRESS_DIR_INGRESS :
......
......@@ -1650,6 +1650,24 @@ static void felix_port_policer_del(struct dsa_switch *ds, int 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,
enum tc_setup_type type,
void *type_data)
......@@ -1880,6 +1898,8 @@ const struct dsa_switch_ops felix_switch_ops = {
.port_max_mtu = felix_get_max_mtu,
.port_policer_add = felix_port_policer_add,
.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_del = felix_cls_flower_del,
.cls_flower_stats = felix_cls_flower_stats,
......
......@@ -2473,7 +2473,7 @@ qca8k_port_mdb_del(struct dsa_switch *ds, int port,
static int
qca8k_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress)
bool ingress, struct netlink_ext_ack *extack)
{
struct qca8k_priv *priv = ds->priv;
int monitor_port, ret;
......
......@@ -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,
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,
ingress, true);
......
......@@ -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);
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)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
......
......@@ -38,7 +38,8 @@
struct ocelot_port_tc {
bool block_shared;
unsigned long offload_cnt;
unsigned long ingress_mirred_id;
unsigned long egress_mirred_id;
unsigned long police_id;
};
......@@ -111,6 +112,10 @@ int ocelot_trap_add(struct ocelot *ocelot, int port,
void (*populate)(struct ocelot_vcap_filter *f));
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_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb;
......
......@@ -359,6 +359,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
filter->action.port_mask = BIT(egress_port);
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
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:
if (filter->block_id != VCAP_IS1) {
NL_SET_ERR_MSG_MOD(extack,
......
......@@ -20,6 +20,8 @@
#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)
{
return devlink_priv(dlp->devlink);
......@@ -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,
struct tc_cls_matchall_offload *f,
bool ingress)
static int ocelot_setup_tc_cls_matchall_police(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f,
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_policer pol = { 0 };
struct flow_action_entry *action;
int port = priv->chip_port;
int err;
......@@ -232,6 +234,119 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
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) {
case TC_CLSMATCHALL_REPLACE:
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,
if (priv->tc.block_shared) {
NL_SET_ERR_MSG_MOD(extack,
"Rate limit is not supported on shared blocks");
"Matchall offloads not supported on shared blocks");
return -EOPNOTSUPP;
}
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");
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;
break;
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;
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;
break;
case TC_CLSMATCHALL_STATS:
default:
return -EOPNOTSUPP;
......
......@@ -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_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_IDX, a->pol_ix);
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)
}
EXPORT_SYMBOL(ocelot_vcap_policer_del);
static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
struct ocelot_vcap_block *block,
struct ocelot_vcap_filter *filter)
static int
ocelot_vcap_filter_add_aux_resources(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter,
struct netlink_ext_ack *extack)
{
struct ocelot_vcap_filter *tmp;
struct list_head *pos, *n;
struct ocelot_mirror *m;
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) {
ret = ocelot_vcap_policer_add(ocelot, filter->action.pol_ix,
&filter->action.pol);
......@@ -970,6 +978,33 @@ static int ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
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++;
if (list_empty(&block->rules)) {
......@@ -1168,7 +1203,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
}
/* 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)
return ret;
......@@ -1199,11 +1234,7 @@ static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
list_for_each_entry_safe(tmp, n, &block->rules, list) {
if (ocelot_vcap_filter_equal(filter, tmp)) {
if (tmp->block_id == VCAP_IS2 &&
tmp->action.police_ena)
ocelot_vcap_policer_del(ocelot,
tmp->action.pol_ix);
ocelot_vcap_filter_del_aux_resources(ocelot, tmp);
list_del(&tmp->list);
kfree(tmp);
}
......
......@@ -1028,7 +1028,7 @@ struct dsa_switch_ops {
struct flow_cls_offload *cls, bool ingress);
int (*port_mirror_add)(struct dsa_switch *ds, int port,
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,
struct dsa_mall_mirror_tc_entry *mirror);
int (*port_policer_add)(struct dsa_switch *ds, int port,
......
......@@ -642,6 +642,11 @@ struct ocelot_lag_fdb {
struct list_head list;
};
struct ocelot_mirror {
refcount_t refcount;
int to;
};
struct ocelot_port {
struct ocelot *ocelot;
......@@ -723,6 +728,7 @@ struct ocelot {
struct ocelot_vcap_block block[3];
struct ocelot_vcap_policer vcap_pol;
struct vcap_props *vcap;
struct ocelot_mirror *mirror;
struct ocelot_psfp_list psfp;
......@@ -908,6 +914,9 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port);
int ocelot_port_policer_add(struct ocelot *ocelot, int port,
struct ocelot_policer *pol);
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,
struct flow_cls_offload *f, bool ingress);
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
......
......@@ -654,6 +654,7 @@ struct ocelot_vcap_action {
enum ocelot_mask_mode mask_mode;
unsigned long port_mask;
bool police_ena;
bool mirror_ena;
struct ocelot_policer pol;
u32 pol_ix;
};
......@@ -697,6 +698,7 @@ struct ocelot_vcap_filter {
unsigned long ingress_port_mask;
/* For VCAP ES0 */
struct ocelot_vcap_port ingress_port;
/* For VCAP IS2 mirrors and ES0 */
struct ocelot_vcap_port egress_port;
enum ocelot_vcap_bit dmac_mc;
......
......@@ -1173,6 +1173,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
struct tc_cls_matchall_offload *cls,
bool ingress)
{
struct netlink_ext_ack *extack = cls->common.extack;
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_mall_mirror_tc_entry *mirror;
......@@ -1210,7 +1211,7 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
mirror->to_local_port = to_dp->index;
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) {
kfree(mall_tc_entry);
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