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

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
Loading
Loading
Loading
Loading
+84 −1
Original line number Diff line number Diff line
@@ -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,
+14 −1
Original line number Diff line number Diff line
@@ -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
+44 −16
Original line number Diff line number Diff line
@@ -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)
+6 −2
Original line number Diff line number Diff line
@@ -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);
+37 −0
Original line number Diff line number Diff line
@@ -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)
{
Loading