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: Horatiu Vultur <horatiu.vultur@microchip.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5390334b59
commit
b69e95397c
@ -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_tc.o lan966x_mqprio.o lan966x_taprio.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 {
|
||||
struct lan966x_rx rx;
|
||||
struct lan966x_tx tx;
|
||||
struct napi_struct napi;
|
||||
|
||||
/* Mirror */
|
||||
struct lan966x_port *mirror_monitor;
|
||||
u32 mirror_mask[2];
|
||||
u32 mirror_count;
|
||||
};
|
||||
|
||||
struct lan966x_port_config {
|
||||
@ -279,7 +284,10 @@ struct lan966x_port_config {
|
||||
struct lan966x_port_tc {
|
||||
bool ingress_shared_block;
|
||||
unsigned long police_id;
|
||||
unsigned long ingress_mirror_id;
|
||||
unsigned long egress_mirror_id;
|
||||
struct flow_stats police_stat;
|
||||
struct flow_stats mirror_stat;
|
||||
};
|
||||
|
||||
struct lan966x_port {
|
||||
@ -505,6 +513,18 @@ int lan966x_police_port_del(struct lan966x_port *port,
|
||||
void lan966x_police_port_stats(struct lan966x_port *port,
|
||||
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[],
|
||||
int id, int tinst, int tcnt,
|
||||
int gbase, int ginst,
|
||||
|
138
drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c
Normal file
138
drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c
Normal file
@ -0,0 +1,138 @@
|
||||
// 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 {
|
||||
#define ANA_AUTOAGE_AGE_PERIOD_GET(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 */
|
||||
#define ANA_FLOODING(r) __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 68, r, 8, 4)
|
||||
|
||||
@ -330,6 +348,12 @@ enum lan966x_target {
|
||||
/* 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_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_SET(x)\
|
||||
FIELD_PREP(ANA_PORT_CFG_LEARNAUTO, x)
|
||||
|
@ -20,6 +20,9 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port,
|
||||
return lan966x_police_port_add(port, &f->rule->action, act,
|
||||
f->cookie, ingress,
|
||||
f->common.extack);
|
||||
case FLOW_ACTION_MIRRED:
|
||||
return lan966x_mirror_port_add(port, act, f->cookie,
|
||||
ingress, f->common.extack);
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(f->common.extack,
|
||||
"Unsupported action");
|
||||
@ -36,6 +39,10 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port,
|
||||
if (f->cookie == port->tc.police_id) {
|
||||
return lan966x_police_port_del(port, f->cookie,
|
||||
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 {
|
||||
NL_SET_ERR_MSG_MOD(f->common.extack,
|
||||
"Unsupported action");
|
||||
@ -51,6 +58,9 @@ static int lan966x_tc_matchall_stats(struct lan966x_port *port,
|
||||
{
|
||||
if (f->cookie == port->tc.police_id) {
|
||||
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 {
|
||||
NL_SET_ERR_MSG_MOD(f->common.extack,
|
||||
"Unsupported action");
|
||||
|
Loading…
Reference in New Issue
Block a user