// 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;
}
}