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

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

Merge branch 'dsa-core-vlan'

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: default avatarDavid S. Miller <davem@davemloft.net>
parents a658a3f2 314f76d7
Loading
Loading
Loading
Loading
+11 −23
Original line number Diff line number Diff line
@@ -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)
@@ -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,
@@ -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;
}

@@ -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;
	}
@@ -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;
}
+0 −1
Original line number Diff line number Diff line
@@ -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;
};
+7 −13
Original line number Diff line number Diff line
@@ -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;
		}
@@ -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)
@@ -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);
}

@@ -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
@@ -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;
@@ -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);
@@ -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);
+0 −1
Original line number Diff line number Diff line
@@ -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
+21 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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 {
Loading