Skip to content

Commit

Permalink
Merge branch 'dsa-core-vlan'
Browse files Browse the repository at this point in the history
Vladimir Oltean says:

====================
Improvements to DSA core VLAN manipulation

In preparation of submitting the NXP SJA1105 driver, the Broadcom b53
and Mediatek mt7530 drivers have been found to apply some VLAN
workarounds that are needed in the new driver as well.

Therefore this patchset is mostly simply promoting the DSA driver
workarounds for VLAN to the generic code.

The b53 driver was applying a few workarounds in order to convince DSA
that its vlan_filtering setting is not really per-port. This is now
simply set by the driver via a DSA variable at probe time. The sja1105
driver will be a second user of this.

The mt7530 was also keeping track of when the .port_vlan_filtering
callback was being called. Remove the kept state from this driver
and simplify dealing with vlan_filtering in the generic case.

TODO:

Find the best way to deal generically with the situation described below
(discussion at https://lkml.org/lkml/2019/4/16/1355):

> > +Segregating the switch ports in multiple bridges is supported (e.g. 2 + 2), but
> > +all bridges should have the same level of VLAN awareness (either both have
> > +``vlan_filtering`` 0, or both 1). Also an inevitable limitation of the fact
> > +that VLAN awareness is global at the switch level is that once a bridge with
> > +``vlan_filtering`` enslaves at least one switch port, the other un-bridged
> > +ports are no longer available for standalone traffic termination.
>
> That is quite a limitation that I don't think I had fully grasped until
> reading your different patches. Since enslaving ports into a bridge
> comes after the network device was already made available for use, maybe
> you should force the carrier down or something along those lines as soon
> as a port is enslaved into a bridge with vlan_filtering=1 to make this
> more predictable for the user?
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed May 1, 2019
2 parents a658a3f + 314f76d commit 4925930
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 63 deletions.
34 changes: 11 additions & 23 deletions drivers/net/dsa/b53/b53_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@ static void b53_enable_vlan(struct b53_device *dev, bool enable,
b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);

dev->vlan_enabled = enable;
dev->vlan_filtering_enabled = enable_filtering;
}

static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100)
Expand Down Expand Up @@ -665,7 +664,7 @@ int b53_configure_vlan(struct dsa_switch *ds)
b53_do_vlan_op(dev, VTA_CMD_CLEAR);
}

b53_enable_vlan(dev, false, dev->vlan_filtering_enabled);
b53_enable_vlan(dev, false, ds->vlan_filtering);

b53_for_each_port(dev, i)
b53_write16(dev, B53_VLAN_PAGE,
Expand Down Expand Up @@ -966,6 +965,13 @@ static int b53_setup(struct dsa_switch *ds)
b53_disable_port(ds, port);
}

/* Let DSA handle the case were multiple bridges span the same switch
* device and different VLAN awareness settings are requested, which
* would be breaking filtering semantics for any of the other bridge
* devices. (not hardware supported)
*/
ds->vlan_filtering_is_global = true;

return ret;
}

Expand Down Expand Up @@ -1275,35 +1281,17 @@ EXPORT_SYMBOL(b53_phylink_mac_link_up);
int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
{
struct b53_device *dev = ds->priv;
struct net_device *bridge_dev;
unsigned int i;
u16 pvid, new_pvid;

/* Handle the case were multiple bridges span the same switch device
* and one of them has a different setting than what is being requested
* which would be breaking filtering semantics for any of the other
* bridge devices.
*/
b53_for_each_port(dev, i) {
bridge_dev = dsa_to_port(ds, i)->bridge_dev;
if (bridge_dev &&
bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
br_vlan_enabled(bridge_dev) != vlan_filtering) {
netdev_err(bridge_dev,
"VLAN filtering is global to the switch!\n");
return -EINVAL;
}
}

b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid);
new_pvid = pvid;
if (dev->vlan_filtering_enabled && !vlan_filtering) {
if (!vlan_filtering) {
/* Filtering is currently enabled, use the default PVID since
* the bridge does not expect tagging anymore
*/
dev->ports[port].pvid = pvid;
new_pvid = b53_default_pvid(dev);
} else if (!dev->vlan_filtering_enabled && vlan_filtering) {
} else {
/* Filtering is currently disabled, restore the previous PVID */
new_pvid = dev->ports[port].pvid;
}
Expand All @@ -1329,7 +1317,7 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port,
if (vlan->vid_end > dev->num_vlans)
return -ERANGE;

b53_enable_vlan(dev, true, dev->vlan_filtering_enabled);
b53_enable_vlan(dev, true, ds->vlan_filtering);

return 0;
}
Expand Down
1 change: 0 additions & 1 deletion drivers/net/dsa/b53/b53_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ struct b53_device {
unsigned int num_vlans;
struct b53_vlan *vlans;
bool vlan_enabled;
bool vlan_filtering_enabled;
unsigned int num_ports;
struct b53_port *ports;
};
Expand Down
20 changes: 7 additions & 13 deletions drivers/net/dsa/mt7530.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,11 +828,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
VLAN_ATTR(MT7530_VLAN_TRANSPARENT));

priv->ports[port].vlan_filtering = false;

for (i = 0; i < MT7530_NUM_PORTS; i++) {
if (dsa_is_user_port(ds, i) &&
priv->ports[i].vlan_filtering) {
dsa_port_is_vlan_filtering(&ds->ports[i])) {
all_user_ports_removed = false;
break;
}
Expand Down Expand Up @@ -891,8 +889,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
* And the other port's port matrix cannot be broken when the
* other port is still a VLAN-aware port.
*/
if (!priv->ports[i].vlan_filtering &&
dsa_is_user_port(ds, i) && i != port) {
if (dsa_is_user_port(ds, i) && i != port &&
!dsa_port_is_vlan_filtering(&ds->ports[i])) {
if (dsa_to_port(ds, i)->bridge_dev != bridge)
continue;
if (priv->ports[i].enable)
Expand All @@ -910,8 +908,6 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
PCR_MATRIX(BIT(MT7530_CPU_PORT)));
priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));

mt7530_port_set_vlan_unaware(ds, port);

mutex_unlock(&priv->reg_mutex);
}

Expand Down Expand Up @@ -1013,10 +1009,6 @@ static int
mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering)
{
struct mt7530_priv *priv = ds->priv;

priv->ports[port].vlan_filtering = vlan_filtering;

if (vlan_filtering) {
/* The port is being kept as VLAN-unaware port when bridge is
* set up with vlan_filtering not being set, Otherwise, the
Expand All @@ -1025,6 +1017,8 @@ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
*/
mt7530_port_set_vlan_aware(ds, port);
mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT);
} else {
mt7530_port_set_vlan_unaware(ds, port);
}

return 0;
Expand Down Expand Up @@ -1139,7 +1133,7 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
/* The port is kept as VLAN-unaware if bridge with vlan_filtering not
* being set.
*/
if (!priv->ports[port].vlan_filtering)
if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
return;

mutex_lock(&priv->reg_mutex);
Expand Down Expand Up @@ -1170,7 +1164,7 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
/* The port is kept as VLAN-unaware if bridge with vlan_filtering not
* being set.
*/
if (!priv->ports[port].vlan_filtering)
if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
return 0;

mutex_lock(&priv->reg_mutex);
Expand Down
1 change: 0 additions & 1 deletion drivers/net/dsa/mt7530.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,6 @@ struct mt7530_port {
bool enable;
u32 pm;
u16 pvid;
bool vlan_filtering;
};

/* struct mt7530_priv - This is the main data structure for holding the state
Expand Down
21 changes: 21 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ struct dsa_port {
const char *mac;
struct device_node *dn;
unsigned int ageing_time;
bool vlan_filtering;
u8 stp_state;
struct net_device *bridge_dev;
struct devlink_port devlink_port;
Expand Down Expand Up @@ -227,6 +228,16 @@ struct dsa_switch {
/* Number of switch port queues */
unsigned int num_tx_queues;

/* Disallow bridge core from requesting different VLAN awareness
* settings on ports if not hardware-supported
*/
bool vlan_filtering_is_global;

/* In case vlan_filtering_is_global is set, the VLAN awareness state
* should be retrieved from here and not from the per-port settings.
*/
bool vlan_filtering;

unsigned long *bitmap;
unsigned long _bitmap;

Expand Down Expand Up @@ -294,6 +305,16 @@ static inline unsigned int dsa_upstream_port(struct dsa_switch *ds, int port)
return dsa_towards_port(ds, cpu_dp->ds->index, cpu_dp->index);
}

static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp)
{
const struct dsa_switch *ds = dp->ds;

if (ds->vlan_filtering_is_global)
return ds->vlan_filtering;
else
return dp->vlan_filtering;
}

typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
bool is_static, void *data);
struct dsa_switch_ops {
Expand Down
2 changes: 2 additions & 0 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ int dsa_port_vlan_add(struct dsa_port *dp,
struct switchdev_trans *trans);
int dsa_port_vlan_del(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan);
int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags);
int dsa_port_vid_del(struct dsa_port *dp, u16 vid);
int dsa_port_link_register_of(struct dsa_port *dp);
void dsa_port_link_unregister_of(struct dsa_port *dp);

Expand Down
85 changes: 82 additions & 3 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,67 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
}

static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
bool vlan_filtering)
{
struct dsa_switch *ds = dp->ds;
int i;

if (!ds->vlan_filtering_is_global)
return true;

/* For cases where enabling/disabling VLAN awareness is global to the
* switch, we need to handle the case where multiple bridges span
* different ports of the same switch device and one of them has a
* different setting than what is being requested.
*/
for (i = 0; i < ds->num_ports; i++) {
struct net_device *other_bridge;

other_bridge = dsa_to_port(ds, i)->bridge_dev;
if (!other_bridge)
continue;
/* If it's the same bridge, it also has same
* vlan_filtering setting => no need to check
*/
if (other_bridge == dp->bridge_dev)
continue;
if (br_vlan_enabled(other_bridge) != vlan_filtering) {
dev_err(ds->dev, "VLAN filtering is a global setting\n");
return false;
}
}
return true;
}

int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
struct switchdev_trans *trans)
{
struct dsa_switch *ds = dp->ds;
int err;

/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
if (switchdev_trans_ph_prepare(trans))
return 0;

if (ds->ops->port_vlan_filtering)
return ds->ops->port_vlan_filtering(ds, dp->index,
vlan_filtering);
if (!ds->ops->port_vlan_filtering)
return 0;

if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering))
return -EINVAL;

if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
return 0;

err = ds->ops->port_vlan_filtering(ds, dp->index,
vlan_filtering);
if (err)
return err;

if (ds->vlan_filtering_is_global)
ds->vlan_filtering = vlan_filtering;
else
dp->vlan_filtering = vlan_filtering;
return 0;
}

Expand Down Expand Up @@ -322,6 +370,37 @@ int dsa_port_vlan_del(struct dsa_port *dp,
return 0;
}

int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
struct switchdev_trans trans;
int err;

trans.ph_prepare = true;
err = dsa_port_vlan_add(dp, &vlan, &trans);
if (err == -EOPNOTSUPP)
return 0;

trans.ph_prepare = false;
return dsa_port_vlan_add(dp, &vlan, &trans);
}

int dsa_port_vid_del(struct dsa_port *dp, u16 vid)
{
struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
};

return dsa_port_vlan_del(dp, &vlan);
}

static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
{
struct device_node *phy_dn;
Expand Down
24 changes: 3 additions & 21 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -1001,13 +1001,6 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan vlan = {
.vid_begin = vid,
.vid_end = vid,
/* This API only allows programming tagged, non-PVID VIDs */
.flags = 0,
};
struct switchdev_trans trans;
struct bridge_vlan_info info;
int ret;

Expand All @@ -1024,25 +1017,14 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
return -EBUSY;
}

trans.ph_prepare = true;
ret = dsa_port_vlan_add(dp, &vlan, &trans);
if (ret == -EOPNOTSUPP)
return 0;

trans.ph_prepare = false;
return dsa_port_vlan_add(dp, &vlan, &trans);
/* This API only allows programming tagged, non-PVID VIDs */
return dsa_port_vid_add(dp, vid, 0);
}

static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan vlan = {
.vid_begin = vid,
.vid_end = vid,
/* This API only allows programming tagged, non-PVID VIDs */
.flags = 0,
};
struct bridge_vlan_info info;
int ret;

Expand All @@ -1059,7 +1041,7 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
return -EBUSY;
}

ret = dsa_port_vlan_del(dp, &vlan);
ret = dsa_port_vid_del(dp, vid);
if (ret == -EOPNOTSUPP)
ret = 0;

Expand Down
Loading

0 comments on commit 4925930

Please sign in to comment.