Commit b69e9539 authored by Horatiu Vultur's avatar Horatiu Vultur Committed by David S. Miller

net: lan966x: Add port mirroring support using tc-matchall

Add support for port mirroring. It is possible to mirror only one port
at a time and it is possible to have both ingress and egress mirroring.
Frames injected by the CPU don't get egress mirrored because they are
bypassing the analyzer module.
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5390334b
...@@ -11,4 +11,4 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ ...@@ -11,4 +11,4 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \
lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \ lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \
lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \
lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \
lan966x_tc_matchall.o lan966x_police.o lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o
...@@ -264,6 +264,11 @@ struct lan966x { ...@@ -264,6 +264,11 @@ struct lan966x {
struct lan966x_rx rx; struct lan966x_rx rx;
struct lan966x_tx tx; struct lan966x_tx tx;
struct napi_struct napi; struct napi_struct napi;
/* Mirror */
struct lan966x_port *mirror_monitor;
u32 mirror_mask[2];
u32 mirror_count;
}; };
struct lan966x_port_config { struct lan966x_port_config {
...@@ -279,7 +284,10 @@ struct lan966x_port_config { ...@@ -279,7 +284,10 @@ struct lan966x_port_config {
struct lan966x_port_tc { struct lan966x_port_tc {
bool ingress_shared_block; bool ingress_shared_block;
unsigned long police_id; unsigned long police_id;
unsigned long ingress_mirror_id;
unsigned long egress_mirror_id;
struct flow_stats police_stat; struct flow_stats police_stat;
struct flow_stats mirror_stat;
}; };
struct lan966x_port { struct lan966x_port {
...@@ -505,6 +513,18 @@ int lan966x_police_port_del(struct lan966x_port *port, ...@@ -505,6 +513,18 @@ int lan966x_police_port_del(struct lan966x_port *port,
void lan966x_police_port_stats(struct lan966x_port *port, void lan966x_police_port_stats(struct lan966x_port *port,
struct flow_stats *stats); struct flow_stats *stats);
int lan966x_mirror_port_add(struct lan966x_port *port,
struct flow_action_entry *action,
unsigned long mirror_id,
bool ingress,
struct netlink_ext_ack *extack);
int lan966x_mirror_port_del(struct lan966x_port *port,
bool ingress,
struct netlink_ext_ack *extack);
void lan966x_mirror_port_stats(struct lan966x_port *port,
struct flow_stats *stats,
bool ingress);
static inline void __iomem *lan_addr(void __iomem *base[], static inline void __iomem *lan_addr(void __iomem *base[],
int id, int tinst, int tcnt, int id, int tinst, int tcnt,
int gbase, int ginst, int gbase, int ginst,
......
// SPDX-License-Identifier: GPL-2.0+
#include "lan966x_main.h"
int lan966x_mirror_port_add(struct lan966x_port *port,
struct flow_action_entry *action,
unsigned long mirror_id,
bool ingress,
struct netlink_ext_ack *extack)
{
struct lan966x *lan966x = port->lan966x;
struct lan966x_port *monitor_port;
if (!lan966x_netdevice_check(action->dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Destination not an lan966x port");
return -EOPNOTSUPP;
}
monitor_port = netdev_priv(action->dev);
if (lan966x->mirror_mask[ingress] & BIT(port->chip_port)) {
NL_SET_ERR_MSG_MOD(extack,
"Mirror already exists");
return -EEXIST;
}
if (lan966x->mirror_monitor &&
lan966x->mirror_monitor != monitor_port) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot change mirror port while in use");
return -EBUSY;
}
if (port == monitor_port) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot mirror the monitor port");
return -EINVAL;
}
lan966x->mirror_mask[ingress] |= BIT(port->chip_port);
lan966x->mirror_monitor = monitor_port;
lan_wr(BIT(monitor_port->chip_port), lan966x, ANA_MIRRORPORTS);
if (ingress) {
lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(1),
ANA_PORT_CFG_SRC_MIRROR_ENA,
lan966x, ANA_PORT_CFG(port->chip_port));
} else {
lan_wr(lan966x->mirror_mask[0], lan966x,
ANA_EMIRRORPORTS);
}
lan966x->mirror_count++;
if (ingress)
port->tc.ingress_mirror_id = mirror_id;
else
port->tc.egress_mirror_id = mirror_id;
return 0;
}
int lan966x_mirror_port_del(struct lan966x_port *port,
bool ingress,
struct netlink_ext_ack *extack)
{
struct lan966x *lan966x = port->lan966x;
if (!(lan966x->mirror_mask[ingress] & BIT(port->chip_port))) {
NL_SET_ERR_MSG_MOD(extack,
"There is no mirroring for this port");
return -ENOENT;
}
lan966x->mirror_mask[ingress] &= ~BIT(port->chip_port);
if (ingress) {
lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(0),
ANA_PORT_CFG_SRC_MIRROR_ENA,
lan966x, ANA_PORT_CFG(port->chip_port));
} else {
lan_wr(lan966x->mirror_mask[0], lan966x,
ANA_EMIRRORPORTS);
}
lan966x->mirror_count--;
if (lan966x->mirror_count == 0) {
lan966x->mirror_monitor = NULL;
lan_wr(0, lan966x, ANA_MIRRORPORTS);
}
if (ingress)
port->tc.ingress_mirror_id = 0;
else
port->tc.egress_mirror_id = 0;
return 0;
}
void lan966x_mirror_port_stats(struct lan966x_port *port,
struct flow_stats *stats,
bool ingress)
{
struct rtnl_link_stats64 new_stats;
struct flow_stats *old_stats;
old_stats = &port->tc.mirror_stat;
lan966x_stats_get(port->dev, &new_stats);
if (ingress) {
flow_stats_update(stats,
new_stats.rx_bytes - old_stats->bytes,
new_stats.rx_packets - old_stats->pkts,
new_stats.rx_dropped - old_stats->drops,
old_stats->lastused,
FLOW_ACTION_HW_STATS_IMMEDIATE);
old_stats->bytes = new_stats.rx_bytes;
old_stats->pkts = new_stats.rx_packets;
old_stats->drops = new_stats.rx_dropped;
old_stats->lastused = jiffies;
} else {
flow_stats_update(stats,
new_stats.tx_bytes - old_stats->bytes,
new_stats.tx_packets - old_stats->pkts,
new_stats.tx_dropped - old_stats->drops,
old_stats->lastused,
FLOW_ACTION_HW_STATS_IMMEDIATE);
old_stats->bytes = new_stats.tx_bytes;
old_stats->pkts = new_stats.tx_packets;
old_stats->drops = new_stats.tx_dropped;
old_stats->lastused = jiffies;
}
}
...@@ -90,6 +90,24 @@ enum lan966x_target { ...@@ -90,6 +90,24 @@ enum lan966x_target {
#define ANA_AUTOAGE_AGE_PERIOD_GET(x)\ #define ANA_AUTOAGE_AGE_PERIOD_GET(x)\
FIELD_GET(ANA_AUTOAGE_AGE_PERIOD, x) FIELD_GET(ANA_AUTOAGE_AGE_PERIOD, x)
/* ANA:ANA:MIRRORPORTS */
#define ANA_MIRRORPORTS __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 60, 0, 1, 4)
#define ANA_MIRRORPORTS_MIRRORPORTS GENMASK(8, 0)
#define ANA_MIRRORPORTS_MIRRORPORTS_SET(x)\
FIELD_PREP(ANA_MIRRORPORTS_MIRRORPORTS, x)
#define ANA_MIRRORPORTS_MIRRORPORTS_GET(x)\
FIELD_GET(ANA_MIRRORPORTS_MIRRORPORTS, x)
/* ANA:ANA:EMIRRORPORTS */
#define ANA_EMIRRORPORTS __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 64, 0, 1, 4)
#define ANA_EMIRRORPORTS_EMIRRORPORTS GENMASK(8, 0)
#define ANA_EMIRRORPORTS_EMIRRORPORTS_SET(x)\
FIELD_PREP(ANA_EMIRRORPORTS_EMIRRORPORTS, x)
#define ANA_EMIRRORPORTS_EMIRRORPORTS_GET(x)\
FIELD_GET(ANA_EMIRRORPORTS_EMIRRORPORTS, x)
/* ANA:ANA:FLOODING */ /* ANA:ANA:FLOODING */
#define ANA_FLOODING(r) __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 68, r, 8, 4) #define ANA_FLOODING(r) __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 68, r, 8, 4)
...@@ -330,6 +348,12 @@ enum lan966x_target { ...@@ -330,6 +348,12 @@ enum lan966x_target {
/* ANA:PORT:PORT_CFG */ /* ANA:PORT:PORT_CFG */
#define ANA_PORT_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 112, 0, 1, 4) #define ANA_PORT_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 112, 0, 1, 4)
#define ANA_PORT_CFG_SRC_MIRROR_ENA BIT(13)
#define ANA_PORT_CFG_SRC_MIRROR_ENA_SET(x)\
FIELD_PREP(ANA_PORT_CFG_SRC_MIRROR_ENA, x)
#define ANA_PORT_CFG_SRC_MIRROR_ENA_GET(x)\
FIELD_GET(ANA_PORT_CFG_SRC_MIRROR_ENA, x)
#define ANA_PORT_CFG_LEARNAUTO BIT(6) #define ANA_PORT_CFG_LEARNAUTO BIT(6)
#define ANA_PORT_CFG_LEARNAUTO_SET(x)\ #define ANA_PORT_CFG_LEARNAUTO_SET(x)\
FIELD_PREP(ANA_PORT_CFG_LEARNAUTO, x) FIELD_PREP(ANA_PORT_CFG_LEARNAUTO, x)
......
...@@ -20,6 +20,9 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port, ...@@ -20,6 +20,9 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port,
return lan966x_police_port_add(port, &f->rule->action, act, return lan966x_police_port_add(port, &f->rule->action, act,
f->cookie, ingress, f->cookie, ingress,
f->common.extack); f->common.extack);
case FLOW_ACTION_MIRRED:
return lan966x_mirror_port_add(port, act, f->cookie,
ingress, f->common.extack);
default: default:
NL_SET_ERR_MSG_MOD(f->common.extack, NL_SET_ERR_MSG_MOD(f->common.extack,
"Unsupported action"); "Unsupported action");
...@@ -36,6 +39,10 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port, ...@@ -36,6 +39,10 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port,
if (f->cookie == port->tc.police_id) { if (f->cookie == port->tc.police_id) {
return lan966x_police_port_del(port, f->cookie, return lan966x_police_port_del(port, f->cookie,
f->common.extack); f->common.extack);
} else if (f->cookie == port->tc.ingress_mirror_id ||
f->cookie == port->tc.egress_mirror_id) {
return lan966x_mirror_port_del(port, ingress,
f->common.extack);
} else { } else {
NL_SET_ERR_MSG_MOD(f->common.extack, NL_SET_ERR_MSG_MOD(f->common.extack,
"Unsupported action"); "Unsupported action");
...@@ -51,6 +58,9 @@ static int lan966x_tc_matchall_stats(struct lan966x_port *port, ...@@ -51,6 +58,9 @@ static int lan966x_tc_matchall_stats(struct lan966x_port *port,
{ {
if (f->cookie == port->tc.police_id) { if (f->cookie == port->tc.police_id) {
lan966x_police_port_stats(port, &f->stats); lan966x_police_port_stats(port, &f->stats);
} else if (f->cookie == port->tc.ingress_mirror_id ||
f->cookie == port->tc.egress_mirror_id) {
lan966x_mirror_port_stats(port, &f->stats, ingress);
} else { } else {
NL_SET_ERR_MSG_MOD(f->common.extack, NL_SET_ERR_MSG_MOD(f->common.extack,
"Unsupported action"); "Unsupported action");
......
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