|
@@ -0,0 +1,266 @@
|
|
|
|
|
+From f0942e00a1abb6404ca4302c66497fc623676c11 Mon Sep 17 00:00:00 2001
|
|
|
|
|
+From: Iwan R Timmer <[email protected]>
|
|
|
|
|
+Date: Thu, 7 Nov 2019 22:11:14 +0100
|
|
|
|
|
+Subject: [PATCH] net: dsa: mv88e6xxx: Add support for port mirroring
|
|
|
|
|
+
|
|
|
|
|
+Add support for configuring port mirroring through the cls_matchall
|
|
|
|
|
+classifier. We do a full ingress and/or egress capture towards a
|
|
|
|
|
+capture port. It allows setting a different capture port for ingress
|
|
|
|
|
+and egress traffic.
|
|
|
|
|
+
|
|
|
|
|
+It keeps track of the mirrored ports and the destination ports to
|
|
|
|
|
+prevent changes to the capture port while other ports are being
|
|
|
|
|
+mirrored.
|
|
|
|
|
+
|
|
|
|
|
+Signed-off-by: Iwan R Timmer <[email protected]>
|
|
|
|
|
+Reviewed-by: Andrew Lunn <[email protected]>
|
|
|
|
|
+Signed-off-by: David S. Miller <[email protected]>
|
|
|
|
|
+---
|
|
|
|
|
+ drivers/net/dsa/mv88e6xxx/chip.c | 76 +++++++++++++++++++++++++++++
|
|
|
|
|
+ drivers/net/dsa/mv88e6xxx/chip.h | 6 +++
|
|
|
|
|
+ drivers/net/dsa/mv88e6xxx/global1.c | 18 +++++--
|
|
|
|
|
+ drivers/net/dsa/mv88e6xxx/port.c | 37 ++++++++++++++
|
|
|
|
|
+ drivers/net/dsa/mv88e6xxx/port.h | 3 ++
|
|
|
|
|
+ 5 files changed, 136 insertions(+), 4 deletions(-)
|
|
|
|
|
+
|
|
|
|
|
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
|
|
|
|
|
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
|
|
|
+@@ -4921,6 +4921,80 @@ static int mv88e6xxx_port_mdb_del(struct
|
|
|
|
|
+ return err;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port,
|
|
|
|
|
++ struct dsa_mall_mirror_tc_entry *mirror,
|
|
|
|
|
++ bool ingress)
|
|
|
|
|
++{
|
|
|
|
|
++ enum mv88e6xxx_egress_direction direction = ingress ?
|
|
|
|
|
++ MV88E6XXX_EGRESS_DIR_INGRESS :
|
|
|
|
|
++ MV88E6XXX_EGRESS_DIR_EGRESS;
|
|
|
|
|
++ struct mv88e6xxx_chip *chip = ds->priv;
|
|
|
|
|
++ bool other_mirrors = false;
|
|
|
|
|
++ int i;
|
|
|
|
|
++ int err;
|
|
|
|
|
++
|
|
|
|
|
++ if (!chip->info->ops->set_egress_port)
|
|
|
|
|
++ return -EOPNOTSUPP;
|
|
|
|
|
++
|
|
|
|
|
++ mutex_lock(&chip->reg_lock);
|
|
|
|
|
++ if ((ingress ? chip->ingress_dest_port : chip->egress_dest_port) !=
|
|
|
|
|
++ mirror->to_local_port) {
|
|
|
|
|
++ for (i = 0; i < mv88e6xxx_num_ports(chip); i++)
|
|
|
|
|
++ other_mirrors |= ingress ?
|
|
|
|
|
++ chip->ports[i].mirror_ingress :
|
|
|
|
|
++ chip->ports[i].mirror_egress;
|
|
|
|
|
++
|
|
|
|
|
++ /* Can't change egress port when other mirror is active */
|
|
|
|
|
++ if (other_mirrors) {
|
|
|
|
|
++ err = -EBUSY;
|
|
|
|
|
++ goto out;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ err = chip->info->ops->set_egress_port(chip,
|
|
|
|
|
++ direction,
|
|
|
|
|
++ mirror->to_local_port);
|
|
|
|
|
++ if (err)
|
|
|
|
|
++ goto out;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ err = mv88e6xxx_port_set_mirror(chip, port, direction, true);
|
|
|
|
|
++out:
|
|
|
|
|
++ mutex_unlock(&chip->reg_lock);
|
|
|
|
|
++
|
|
|
|
|
++ return err;
|
|
|
|
|
++}
|
|
|
|
|
++
|
|
|
|
|
++static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port,
|
|
|
|
|
++ struct dsa_mall_mirror_tc_entry *mirror)
|
|
|
|
|
++{
|
|
|
|
|
++ enum mv88e6xxx_egress_direction direction = mirror->ingress ?
|
|
|
|
|
++ MV88E6XXX_EGRESS_DIR_INGRESS :
|
|
|
|
|
++ MV88E6XXX_EGRESS_DIR_EGRESS;
|
|
|
|
|
++ struct mv88e6xxx_chip *chip = ds->priv;
|
|
|
|
|
++ bool other_mirrors = false;
|
|
|
|
|
++ int i;
|
|
|
|
|
++
|
|
|
|
|
++ mutex_lock(&chip->reg_lock);
|
|
|
|
|
++ if (mv88e6xxx_port_set_mirror(chip, port, direction, false))
|
|
|
|
|
++ dev_err(ds->dev, "p%d: failed to disable mirroring\n", port);
|
|
|
|
|
++
|
|
|
|
|
++ for (i = 0; i < mv88e6xxx_num_ports(chip); i++)
|
|
|
|
|
++ other_mirrors |= mirror->ingress ?
|
|
|
|
|
++ chip->ports[i].mirror_ingress :
|
|
|
|
|
++ chip->ports[i].mirror_egress;
|
|
|
|
|
++
|
|
|
|
|
++ /* Reset egress port when no other mirror is active */
|
|
|
|
|
++ if (!other_mirrors) {
|
|
|
|
|
++ if (chip->info->ops->set_egress_port(chip,
|
|
|
|
|
++ direction,
|
|
|
|
|
++ dsa_upstream_port(ds,
|
|
|
|
|
++ port)));
|
|
|
|
|
++ dev_err(ds->dev, "failed to set egress port\n");
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ mutex_unlock(&chip->reg_lock);
|
|
|
|
|
++}
|
|
|
|
|
++
|
|
|
|
|
+ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
|
|
|
|
|
+ bool unicast, bool multicast)
|
|
|
|
|
+ {
|
|
|
|
|
+@@ -4975,6 +5049,8 @@ static const struct dsa_switch_ops mv88e
|
|
|
|
|
+ .port_mdb_prepare = mv88e6xxx_port_mdb_prepare,
|
|
|
|
|
+ .port_mdb_add = mv88e6xxx_port_mdb_add,
|
|
|
|
|
+ .port_mdb_del = mv88e6xxx_port_mdb_del,
|
|
|
|
|
++ .port_mirror_add = mv88e6xxx_port_mirror_add,
|
|
|
|
|
++ .port_mirror_del = mv88e6xxx_port_mirror_del,
|
|
|
|
|
+ .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
|
|
|
|
|
+ .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
|
|
|
|
|
+ .port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set,
|
|
|
|
|
+--- a/drivers/net/dsa/mv88e6xxx/chip.h
|
|
|
|
|
++++ b/drivers/net/dsa/mv88e6xxx/chip.h
|
|
|
|
|
+@@ -232,6 +232,8 @@ struct mv88e6xxx_port {
|
|
|
|
|
+ u64 vtu_member_violation;
|
|
|
|
|
+ u64 vtu_miss_violation;
|
|
|
|
|
+ u8 cmode;
|
|
|
|
|
++ bool mirror_ingress;
|
|
|
|
|
++ bool mirror_egress;
|
|
|
|
|
+ unsigned int serdes_irq;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+@@ -315,6 +317,10 @@ struct mv88e6xxx_chip {
|
|
|
|
|
+ u16 evcap_config;
|
|
|
|
|
+ u16 enable_count;
|
|
|
|
|
+
|
|
|
|
|
++ /* Current ingress and egress monitor ports */
|
|
|
|
|
++ int egress_dest_port;
|
|
|
|
|
++ int ingress_dest_port;
|
|
|
|
|
++
|
|
|
|
|
+ /* Per-port timestamping resources. */
|
|
|
|
|
+ struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
|
|
|
|
|
+
|
|
|
|
|
+--- a/drivers/net/dsa/mv88e6xxx/global1.c
|
|
|
|
|
++++ b/drivers/net/dsa/mv88e6xxx/global1.c
|
|
|
|
|
+@@ -267,6 +267,7 @@ int mv88e6095_g1_set_egress_port(struct
|
|
|
|
|
+ enum mv88e6xxx_egress_direction direction,
|
|
|
|
|
+ int port)
|
|
|
|
|
+ {
|
|
|
|
|
++ int *dest_port_chip;
|
|
|
|
|
+ u16 reg;
|
|
|
|
|
+ int err;
|
|
|
|
|
+
|
|
|
|
|
+@@ -276,11 +277,13 @@ int mv88e6095_g1_set_egress_port(struct
|
|
|
|
|
+
|
|
|
|
|
+ switch (direction) {
|
|
|
|
|
+ case MV88E6XXX_EGRESS_DIR_INGRESS:
|
|
|
|
|
++ dest_port_chip = &chip->ingress_dest_port;
|
|
|
|
|
+ reg &= MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK;
|
|
|
|
|
+ reg |= port <<
|
|
|
|
|
+ __bf_shf(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MV88E6XXX_EGRESS_DIR_EGRESS:
|
|
|
|
|
++ dest_port_chip = &chip->egress_dest_port;
|
|
|
|
|
+ reg &= MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK;
|
|
|
|
|
+ reg |= port <<
|
|
|
|
|
+ __bf_shf(MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
|
|
|
|
|
+@@ -289,7 +292,11 @@ int mv88e6095_g1_set_egress_port(struct
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- return mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
|
|
|
|
|
++ err = mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
|
|
|
|
|
++ if (!err)
|
|
|
|
|
++ *dest_port_chip = port;
|
|
|
|
|
++
|
|
|
|
|
++ return err;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Older generations also call this the ARP destination. It has been
|
|
|
|
|
+@@ -325,14 +332,17 @@ int mv88e6390_g1_set_egress_port(struct
|
|
|
|
|
+ enum mv88e6xxx_egress_direction direction,
|
|
|
|
|
+ int port)
|
|
|
|
|
+ {
|
|
|
|
|
++ int *dest_port_chip;
|
|
|
|
|
+ u16 ptr;
|
|
|
|
|
+ int err;
|
|
|
|
|
+
|
|
|
|
|
+ switch (direction) {
|
|
|
|
|
+ case MV88E6XXX_EGRESS_DIR_INGRESS:
|
|
|
|
|
++ dest_port_chip = &chip->ingress_dest_port;
|
|
|
|
|
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MV88E6XXX_EGRESS_DIR_EGRESS:
|
|
|
|
|
++ dest_port_chip = &chip->egress_dest_port;
|
|
|
|
|
+ ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+@@ -340,10 +350,10 @@ int mv88e6390_g1_set_egress_port(struct
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ err = mv88e6390_g1_monitor_write(chip, ptr, port);
|
|
|
|
|
+- if (err)
|
|
|
|
|
+- return err;
|
|
|
|
|
++ if (!err)
|
|
|
|
|
++ *dest_port_chip = port;
|
|
|
|
|
+
|
|
|
|
|
+- return 0;
|
|
|
|
|
++ return err;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
|
+--- a/drivers/net/dsa/mv88e6xxx/port.c
|
|
|
|
|
++++ b/drivers/net/dsa/mv88e6xxx/port.c
|
|
|
|
|
+@@ -1181,6 +1181,43 @@ int mv88e6095_port_set_upstream_port(str
|
|
|
|
|
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
|
++ enum mv88e6xxx_egress_direction direction,
|
|
|
|
|
++ bool mirror)
|
|
|
|
|
++{
|
|
|
|
|
++ bool *mirror_port;
|
|
|
|
|
++ u16 reg;
|
|
|
|
|
++ u16 bit;
|
|
|
|
|
++ int err;
|
|
|
|
|
++
|
|
|
|
|
++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
|
|
|
|
++ if (err)
|
|
|
|
|
++ return err;
|
|
|
|
|
++
|
|
|
|
|
++ switch (direction) {
|
|
|
|
|
++ case MV88E6XXX_EGRESS_DIR_INGRESS:
|
|
|
|
|
++ bit = MV88E6XXX_PORT_CTL2_INGRESS_MONITOR;
|
|
|
|
|
++ mirror_port = &chip->ports[port].mirror_ingress;
|
|
|
|
|
++ break;
|
|
|
|
|
++ case MV88E6XXX_EGRESS_DIR_EGRESS:
|
|
|
|
|
++ bit = MV88E6XXX_PORT_CTL2_EGRESS_MONITOR;
|
|
|
|
|
++ mirror_port = &chip->ports[port].mirror_egress;
|
|
|
|
|
++ break;
|
|
|
|
|
++ default:
|
|
|
|
|
++ return -EINVAL;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ reg &= ~bit;
|
|
|
|
|
++ if (mirror)
|
|
|
|
|
++ reg |= bit;
|
|
|
|
|
++
|
|
|
|
|
++ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
|
|
|
|
++ if (!err)
|
|
|
|
|
++ *mirror_port = mirror;
|
|
|
|
|
++
|
|
|
|
|
++ return err;
|
|
|
|
|
++}
|
|
|
|
|
++
|
|
|
|
|
+ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
|
+ u16 mode)
|
|
|
|
|
+ {
|
|
|
|
|
+--- a/drivers/net/dsa/mv88e6xxx/port.h
|
|
|
|
|
++++ b/drivers/net/dsa/mv88e6xxx/port.h
|
|
|
|
|
+@@ -368,6 +368,9 @@ int mv88e6352_port_link_state(struct mv8
|
|
|
|
|
+ int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
|
|
|
|
|
+ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
|
+ int upstream_port);
|
|
|
|
|
++int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
|
++ enum mv88e6xxx_egress_direction direction,
|
|
|
|
|
++ bool mirror);
|
|
|
|
|
+
|
|
|
|
|
+ int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port);
|
|
|
|
|
+ int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port);
|