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