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

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
...@@ -1137,7 +1137,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, ...@@ -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)) if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
continue; continue;
if (!ds->ports[port].slave) if (!ds->ports[i].slave)
continue; continue;
if (vlan.member[i] == if (vlan.member[i] ==
...@@ -1151,8 +1151,8 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, ...@@ -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) if (!dsa_to_port(ds, i)->bridge_dev)
continue; continue;
dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n", dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n",
port, vlan.vid, port, vlan.vid, i,
netdev_name(dsa_to_port(ds, i)->bridge_dev)); netdev_name(dsa_to_port(ds, i)->bridge_dev));
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto unlock; goto unlock;
...@@ -1208,6 +1208,73 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, ...@@ -1208,6 +1208,73 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
return 0; 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, static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
u16 vid, u8 member) u16 vid, u8 member)
{ {
...@@ -1220,7 +1287,11 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, ...@@ -1220,7 +1287,11 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
vlan.member[port] = member; 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, 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, ...@@ -1324,50 +1395,6 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
return err; 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, static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid)
{ {
...@@ -2049,6 +2076,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) ...@@ -2049,6 +2076,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err) if (err)
goto unlock; goto unlock;
err = mv88e6xxx_broadcast_setup(chip, 0);
if (err)
goto unlock;
err = mv88e6xxx_pot_setup(chip); err = mv88e6xxx_pot_setup(chip);
if (err) if (err)
goto unlock; goto unlock;
......
...@@ -355,11 +355,12 @@ static int dsa_slave_port_attr_get(struct net_device *dev, ...@@ -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_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
struct dsa_switch_tree *dst = ds->dst;
switch (attr->id) { switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
attr->u.ppid.id_len = sizeof(ds->index); attr->u.ppid.id_len = sizeof(dst->index);
memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len); memcpy(&attr->u.ppid.id, &dst->index, attr->u.ppid.id_len);
break; break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
attr->u.brport_flags_support = 0; attr->u.brport_flags_support = 0;
......
...@@ -141,6 +141,8 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -141,6 +141,8 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
2 * ETH_ALEN); 2 * ETH_ALEN);
} }
skb->offload_fwd_mark = 1;
return skb; return skb;
} }
......
...@@ -160,6 +160,8 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -160,6 +160,8 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
2 * ETH_ALEN); 2 * ETH_ALEN);
} }
skb->offload_fwd_mark = 1;
return skb; return skb;
} }
......
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