Merge branch 'dsa-pass-bridge-to-drivers'

Vivien Didelot says:

====================
net: dsa: pass bridge device to drivers

This patchset simplifies the DSA layer.

A switch may support multiple bridges with the same hardware VLAN. Thus a check
such as dsa_bridge_check_vlan_range must be moved from the DSA layer to the
concerned driver.

The first purpose of this patchset is to help moving this check to the
mv88e6xxx driver, which is the only one affected at the moment.

To do that, pass directly the bridge net_device structure down to the DSA
drivers, instead of calculating a bitmask of bridge members.

The second purpose is to prepare the replacement of the complex
port_vlan_getnext approach. A second patchset is ready to follow, implementing
port_vlan_dump and thus simplifying the DSA slave code one more time.

Note that this patchset applies on top of https://lkml.org/lkml/2016/2/5/532.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-02-23 14:52:46 -05:00
commit f5461c2763
7 changed files with 102 additions and 99 deletions

View File

@ -524,17 +524,14 @@ Bridge layer
- port_join_bridge: bridge layer function invoked when a given switch port is
added to a bridge, this function should be doing the necessary at the switch
level to permit the joining port from being added to the relevant logical
domain for it to ingress/egress traffic with other members of the bridge. DSA
does nothing but calculate a bitmask of switch ports currently members of the
specified bridge being requested the join
domain for it to ingress/egress traffic with other members of the bridge.
- port_leave_bridge: bridge layer function invoked when a given switch port is
removed from a bridge, this function should be doing the necessary at the
switch level to deny the leaving port from ingress/egress traffic from the
remaining bridge members. When the port leaves the bridge, it should be aged
out at the switch hardware for the switch to (re) learn MAC addresses behind
this port. DSA calculates the bitmask of ports still members of the bridge
being left
this port.
- port_stp_update: bridge layer function invoked when a given switch port STP
state is computed by the bridge layer and should be propagated to switch

View File

@ -483,16 +483,17 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port)
}
static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port,
u32 br_port_mask)
struct net_device *bridge)
{
struct bcm_sf2_priv *priv = ds_to_priv(ds);
unsigned int i;
u32 reg, p_ctl;
priv->port_sts[port].bridge_dev = bridge;
p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port));
for (i = 0; i < priv->hw_params.num_ports; i++) {
if (!((1 << i) & br_port_mask))
if (priv->port_sts[i].bridge_dev != bridge)
continue;
/* Add this local port to the remote port VLAN control
@ -515,10 +516,10 @@ static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port,
return 0;
}
static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port,
u32 br_port_mask)
static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port)
{
struct bcm_sf2_priv *priv = ds_to_priv(ds);
struct net_device *bridge = priv->port_sts[port].bridge_dev;
unsigned int i;
u32 reg, p_ctl;
@ -526,7 +527,7 @@ static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port,
for (i = 0; i < priv->hw_params.num_ports; i++) {
/* Don't touch the remaining ports */
if (!((1 << i) & br_port_mask))
if (priv->port_sts[i].bridge_dev != bridge)
continue;
reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i));
@ -541,6 +542,7 @@ static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port,
core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port));
priv->port_sts[port].vlan_ctl_mask = p_ctl;
priv->port_sts[port].bridge_dev = NULL;
return 0;
}

View File

@ -50,6 +50,8 @@ struct bcm_sf2_port_status {
struct ethtool_eee eee;
u32 vlan_ctl_mask;
struct net_device *bridge_dev;
};
struct bcm_sf2_arl_entry {

View File

@ -1131,7 +1131,7 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
/* mv88e6xxx_port_stp_update may be called with softirqs disabled,
* so we can not update the port state directly but need to schedule it.
*/
ps->port_state[port] = stp_state;
ps->ports[port].state = stp_state;
set_bit(port, &ps->port_state_update_mask);
schedule_work(&ps->bridge_work);
@ -1471,14 +1471,78 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
return 0;
}
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
u16 vid_begin, u16 vid_end)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry vlan;
int i, err;
if (!vid_begin)
return -EOPNOTSUPP;
mutex_lock(&ps->smi_mutex);
err = _mv88e6xxx_vtu_vid_write(ds, vid_begin - 1);
if (err)
goto unlock;
do {
err = _mv88e6xxx_vtu_getnext(ds, &vlan);
if (err)
goto unlock;
if (!vlan.valid)
break;
if (vlan.vid > vid_end)
break;
for (i = 0; i < ps->num_ports; ++i) {
if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
continue;
if (vlan.data[i] ==
GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
continue;
if (ps->ports[i].bridge_dev ==
ps->ports[port].bridge_dev)
break; /* same bridge, check next VLAN */
netdev_warn(ds->ports[port],
"hardware VLAN %d already used by %s\n",
vlan.vid,
netdev_name(ps->ports[i].bridge_dev));
err = -EOPNOTSUPP;
goto unlock;
}
} while (vlan.vid < vid_end);
unlock:
mutex_unlock(&ps->smi_mutex);
return err;
}
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
{
int err;
/* We reserve a few VLANs to isolate unbridged ports */
if (vlan->vid_end >= 4000)
return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as the VLAN
* members, do not support it (yet) and fallback to software VLAN.
*/
err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
vlan->vid_end);
if (err)
return err;
/* We don't need any dynamic resource from the kernel (yet),
* so skip the prepare phase.
*/
@ -1889,13 +1953,22 @@ unlock:
return err;
}
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, u32 members)
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
struct net_device *bridge)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
ps->ports[port].bridge_dev = bridge;
return 0;
}
int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, u32 members)
int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
ps->ports[port].bridge_dev = NULL;
return 0;
}
@ -1925,7 +1998,7 @@ static void mv88e6xxx_bridge_work(struct work_struct *work)
while (ps->port_state_update_mask) {
port = __ffs(ps->port_state_update_mask);
clear_bit(port, &ps->port_state_update_mask);
mv88e6xxx_set_port_state(ds, port, ps->port_state[port]);
mv88e6xxx_set_port_state(ds, port, ps->ports[port].state);
}
}

View File

@ -379,6 +379,11 @@ struct mv88e6xxx_vtu_stu_entry {
u8 data[DSA_MAX_PORTS];
};
struct mv88e6xxx_priv_port {
struct net_device *bridge_dev;
u8 state;
};
struct mv88e6xxx_priv_state {
/* When using multi-chip addressing, this mutex protects
* access to the indirect access registers. (In single-chip
@ -415,8 +420,9 @@ struct mv88e6xxx_priv_state {
int id; /* switch product id */
int num_ports; /* number of switch ports */
struct mv88e6xxx_priv_port ports[DSA_MAX_PORTS];
unsigned long port_state_update_mask;
u8 port_state[DSA_MAX_PORTS];
struct work_struct bridge_work;
};
@ -476,8 +482,9 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e);
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, u32 members);
int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, u32 members);
int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
struct net_device *bridge);
int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port);
int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,

View File

@ -297,9 +297,8 @@ struct dsa_switch_driver {
* Bridge integration
*/
int (*port_join_bridge)(struct dsa_switch *ds, int port,
u32 br_port_mask);
int (*port_leave_bridge)(struct dsa_switch *ds, int port,
u32 br_port_mask);
struct net_device *bridge);
int (*port_leave_bridge)(struct dsa_switch *ds, int port);
int (*port_stp_update)(struct dsa_switch *ds, int port,
u8 state);

View File

@ -201,47 +201,6 @@ out:
return 0;
}
static int dsa_bridge_check_vlan_range(struct dsa_switch *ds,
const struct net_device *bridge,
u16 vid_begin, u16 vid_end)
{
struct dsa_slave_priv *p;
struct net_device *dev, *vlan_br;
DECLARE_BITMAP(members, DSA_MAX_PORTS);
DECLARE_BITMAP(untagged, DSA_MAX_PORTS);
u16 vid;
int member, err;
if (!ds->drv->vlan_getnext || !vid_begin)
return -EOPNOTSUPP;
vid = vid_begin - 1;
do {
err = ds->drv->vlan_getnext(ds, &vid, members, untagged);
if (err)
break;
if (vid > vid_end)
break;
member = find_first_bit(members, DSA_MAX_PORTS);
if (member == DSA_MAX_PORTS)
continue;
dev = ds->ports[member];
p = netdev_priv(dev);
vlan_br = p->bridge_dev;
if (vlan_br == bridge)
continue;
netdev_dbg(vlan_br, "hardware VLAN %d already in use\n", vid);
return -EOPNOTSUPP;
} while (vid < vid_end);
return err == -ENOENT ? 0 : err;
}
static int dsa_slave_port_vlan_add(struct net_device *dev,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
@ -254,15 +213,6 @@ static int dsa_slave_port_vlan_add(struct net_device *dev,
if (!ds->drv->port_vlan_prepare || !ds->drv->port_vlan_add)
return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as
* the VLAN members, fallback to software VLAN (hopefully).
*/
err = dsa_bridge_check_vlan_range(ds, p->bridge_dev,
vlan->vid_begin,
vlan->vid_end);
if (err)
return err;
err = ds->drv->port_vlan_prepare(ds, p->port, vlan, trans);
if (err)
return err;
@ -385,31 +335,6 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EOPNOTSUPP;
}
/* Return a bitmask of all ports being currently bridged within a given bridge
* device. Note that on leave, the mask will still return the bitmask of ports
* currently bridged, prior to port removal, and this is exactly what we want.
*/
static u32 dsa_slave_br_port_mask(struct dsa_switch *ds,
struct net_device *bridge)
{
struct dsa_slave_priv *p;
unsigned int port;
u32 mask = 0;
for (port = 0; port < DSA_MAX_PORTS; port++) {
if (!dsa_is_port_initialized(ds, port))
continue;
p = netdev_priv(ds->ports[port]);
if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT &&
p->bridge_dev == bridge)
mask |= 1 << port;
}
return mask;
}
static int dsa_slave_stp_update(struct net_device *dev, u8 state)
{
struct dsa_slave_priv *p = netdev_priv(dev);
@ -533,8 +458,7 @@ static int dsa_slave_bridge_port_join(struct net_device *dev,
p->bridge_dev = br;
if (ds->drv->port_join_bridge)
ret = ds->drv->port_join_bridge(ds, p->port,
dsa_slave_br_port_mask(ds, br));
ret = ds->drv->port_join_bridge(ds, p->port, br);
return ret;
}
@ -547,8 +471,7 @@ static int dsa_slave_bridge_port_leave(struct net_device *dev)
if (ds->drv->port_leave_bridge)
ret = ds->drv->port_leave_bridge(ds, p->port,
dsa_slave_br_port_mask(ds, p->bridge_dev));
ret = ds->drv->port_leave_bridge(ds, p->port);
p->bridge_dev = NULL;