Commit c82488df authored by David S. Miller's avatar David S. Miller

Merge branch 'net-dsa-mv88e6xxx-Add-support-for-port-mirroring'

Iwan R Timmer says:

====================
net: dsa: mv88e6xxx: Add support for port mirroring

This patch series add support for port mirroring in the mv88e6xx switch driver.
The first patch changes the set_egress_port function to allow different egress
ports for egress and ingress traffic. The second patch adds the actual code for
port mirroring support.

Tested on a 88E6176 with:

tc qdisc add dev wan0 clsact
tc filter add dev wan0 ingress matchall skip_sw \
        action mirred egress mirror dev lan2
tc filter add dev wan0 egress matchall skip_sw \
        action mirred egress mirror dev lan3

Changes in v3

- Use enum for egress traffic direction
- Keep track of egress ports on mv88e6390
- Move booleans in struct for better structure packing

Changes in v2

- Support mirroring egress and ingress traffic to different ports
- Check for invalid configurations when multiple ports are mirrored
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 22820017 f0942e00
......@@ -2390,6 +2390,13 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
if (chip->info->ops->set_egress_port) {
err = chip->info->ops->set_egress_port(chip,
MV88E6XXX_EGRESS_DIR_INGRESS,
upstream_port);
if (err)
return err;
err = chip->info->ops->set_egress_port(chip,
MV88E6XXX_EGRESS_DIR_EGRESS,
upstream_port);
if (err)
return err;
......@@ -5243,6 +5250,80 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
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)
{
......@@ -5298,6 +5379,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.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,
......
......@@ -33,6 +33,11 @@ enum mv88e6xxx_egress_mode {
MV88E6XXX_EGRESS_MODE_ETHERTYPE,
};
enum mv88e6xxx_egress_direction {
MV88E6XXX_EGRESS_DIR_INGRESS,
MV88E6XXX_EGRESS_DIR_EGRESS,
};
enum mv88e6xxx_frame_mode {
MV88E6XXX_FRAME_MODE_NORMAL,
MV88E6XXX_FRAME_MODE_DSA,
......@@ -228,6 +233,8 @@ struct mv88e6xxx_port {
u64 vtu_member_violation;
u64 vtu_miss_violation;
u8 cmode;
bool mirror_ingress;
bool mirror_egress;
unsigned int serdes_irq;
};
......@@ -311,6 +318,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];
......@@ -465,7 +476,9 @@ struct mv88e6xxx_ops {
int (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
uint64_t *data);
int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port);
int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port);
int (*set_egress_port)(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port);
#define MV88E6XXX_CASCADE_PORT_NONE 0xe
#define MV88E6XXX_CASCADE_PORT_MULTIPLE 0xf
......
......@@ -263,8 +263,11 @@ int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip)
/* Offset 0x1a: Monitor Control */
/* Offset 0x1a: Monitor & MGMT Control on some devices */
int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port)
{
int *dest_port_chip;
u16 reg;
int err;
......@@ -272,13 +275,28 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
if (err)
return err;
reg &= ~(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK |
MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
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);
break;
default:
return -EINVAL;
}
reg |= port << __bf_shf(MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK) |
port << __bf_shf(MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK);
err = mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
if (!err)
*dest_port_chip = port;
return mv88e6xxx_g1_write(chip, MV88E6185_G1_MONITOR_CTL, reg);
return err;
}
/* Older generations also call this the ARP destination. It has been
......@@ -310,22 +328,32 @@ static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip,
return mv88e6xxx_g1_write(chip, MV88E6390_G1_MONITOR_MGMT_CTL, reg);
}
int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip,
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;
err = mv88e6390_g1_monitor_write(chip, ptr, port);
if (err)
return err;
break;
case MV88E6XXX_EGRESS_DIR_EGRESS:
dest_port_chip = &chip->egress_dest_port;
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST;
break;
default:
return -EINVAL;
}
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)
......
......@@ -288,8 +288,12 @@ int mv88e6095_g1_stats_set_histogram(struct mv88e6xxx_chip *chip);
int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip);
void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val);
int mv88e6xxx_g1_stats_clear(struct mv88e6xxx_chip *chip);
int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port);
int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port);
int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
......
......@@ -1181,6 +1181,43 @@ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
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, &reg);
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)
{
......
......@@ -368,6 +368,9 @@ int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
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);
......
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