Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

Merge branch 'mv88e6xxx-broadcast-flooding-in-hardware'



Andrew Lunn says:

====================
mv88e6xxx broadcast flooding in hardware

This patchset makes the mv88e6xxx driver perform flooding in hardware,
rather than let the software bridge perform the flooding. This is a
prerequisite for IGMP snooping on the bridge interface.

In order to make hardware broadcasting work, a few other issues need
fixing or improving. SWITCHDEV_ATTR_ID_PORT_PARENT_ID is broken, which
is apparent when testing on the ZII devel board with multiple
switches.

Some of these patches are taken from a previous RFC patchset of IGMP
support.

Rebased onto net-next, with fixup for Vivien's refactoring.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8c5f9a8c 87fa886e
Loading
Loading
Loading
Loading
+79 −48
Original line number Diff line number Diff line
@@ -1137,7 +1137,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
			if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
				continue;

			if (!ds->ports[port].slave)
			if (!ds->ports[i].slave)
				continue;

			if (vlan.member[i] ==
@@ -1151,8 +1151,8 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
			if (!dsa_to_port(ds, i)->bridge_dev)
				continue;

			dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n",
				port, vlan.vid,
			dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n",
				port, vlan.vid, i,
				netdev_name(dsa_to_port(ds, i)->bridge_dev));
			err = -EOPNOTSUPP;
			goto unlock;
@@ -1208,6 +1208,73 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
	return 0;
}

static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
					const unsigned char *addr, u16 vid,
					u8 state)
{
	struct mv88e6xxx_vtu_entry vlan;
	struct mv88e6xxx_atu_entry entry;
	int err;

	/* Null VLAN ID corresponds to the port private database */
	if (vid == 0)
		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
	else
		err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
	if (err)
		return err;

	entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	ether_addr_copy(entry.mac, addr);
	eth_addr_dec(entry.mac);

	err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry);
	if (err)
		return err;

	/* Initialize a fresh ATU entry if it isn't found */
	if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED ||
	    !ether_addr_equal(entry.mac, addr)) {
		memset(&entry, 0, sizeof(entry));
		ether_addr_copy(entry.mac, addr);
	}

	/* Purge the ATU entry only if no port is using it anymore */
	if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
		entry.portvec &= ~BIT(port);
		if (!entry.portvec)
			entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	} else {
		entry.portvec |= BIT(port);
		entry.state = state;
	}

	return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
}

static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port,
					u16 vid)
{
	const char broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
	u8 state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC;

	return mv88e6xxx_port_db_load_purge(chip, port, broadcast, vid, state);
}

static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid)
{
	int port;
	int err;

	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
		err = mv88e6xxx_port_add_broadcast(chip, port, vid);
		if (err)
			return err;
	}

	return 0;
}

static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
				    u16 vid, u8 member)
{
@@ -1220,7 +1287,11 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,

	vlan.member[port] = member;

	return mv88e6xxx_vtu_loadpurge(chip, &vlan);
	err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
	if (err)
		return err;

	return mv88e6xxx_broadcast_setup(chip, vid);
}

static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
@@ -1324,50 +1395,6 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
	return err;
}

static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
					const unsigned char *addr, u16 vid,
					u8 state)
{
	struct mv88e6xxx_vtu_entry vlan;
	struct mv88e6xxx_atu_entry entry;
	int err;

	/* Null VLAN ID corresponds to the port private database */
	if (vid == 0)
		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
	else
		err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
	if (err)
		return err;

	entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	ether_addr_copy(entry.mac, addr);
	eth_addr_dec(entry.mac);

	err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry);
	if (err)
		return err;

	/* Initialize a fresh ATU entry if it isn't found */
	if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED ||
	    !ether_addr_equal(entry.mac, addr)) {
		memset(&entry, 0, sizeof(entry));
		ether_addr_copy(entry.mac, addr);
	}

	/* Purge the ATU entry only if no port is using it anymore */
	if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
		entry.portvec &= ~BIT(port);
		if (!entry.portvec)
			entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
	} else {
		entry.portvec |= BIT(port);
		entry.state = state;
	}

	return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
}

static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
				  const unsigned char *addr, u16 vid)
{
@@ -2049,6 +2076,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
	if (err)
		goto unlock;

	err = mv88e6xxx_broadcast_setup(chip, 0);
	if (err)
		goto unlock;

	err = mv88e6xxx_pot_setup(chip);
	if (err)
		goto unlock;
+3 −2
Original line number Diff line number Diff line
@@ -355,11 +355,12 @@ static int dsa_slave_port_attr_get(struct net_device *dev,
{
	struct dsa_port *dp = dsa_slave_to_port(dev);
	struct dsa_switch *ds = dp->ds;
	struct dsa_switch_tree *dst = ds->dst;

	switch (attr->id) {
	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
		attr->u.ppid.id_len = sizeof(ds->index);
		memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len);
		attr->u.ppid.id_len = sizeof(dst->index);
		memcpy(&attr->u.ppid.id, &dst->index, attr->u.ppid.id_len);
		break;
	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
		attr->u.brport_flags_support = 0;
+2 −0
Original line number Diff line number Diff line
@@ -141,6 +141,8 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
			2 * ETH_ALEN);
	}

	skb->offload_fwd_mark = 1;

	return skb;
}

+2 −0
Original line number Diff line number Diff line
@@ -160,6 +160,8 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
			2 * ETH_ALEN);
	}

	skb->offload_fwd_mark = 1;

	return skb;
}