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

Merge branch 'DSA-tag_8021q-cleanup'



Vladimir Oltean says:

====================
DSA tag_8021q cleanup

This small series tries to consolidate the VLAN handling in DSA a little
bit. It reworks tag_8021q to be minimally invasive to the dsa_switch_ops
structure. This makes the rest of the code a bit easier to follow.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a8c16e8e 88236591
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -210,14 +210,13 @@ struct sja1105_private {
	struct dsa_switch *ds;
	struct list_head dsa_8021q_vlans;
	struct list_head bridge_vlans;
	struct list_head crosschip_links;
	struct sja1105_flow_block flow_block;
	struct sja1105_port ports[SJA1105_NUM_PORTS];
	/* Serializes transmission of management frames so that
	 * the switch doesn't confuse them with one another.
	 */
	struct mutex mgmt_lock;
	bool expect_dsa_8021q;
	struct dsa_8021q_context *dsa_8021q_ctx;
	enum sja1105_vlan_state vlan_state;
	struct sja1105_cbs_entry *cbs;
	struct sja1105_tagger_data tagger_data;
+128 −98
Original line number Diff line number Diff line
@@ -1880,19 +1880,17 @@ static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		other_priv->expect_dsa_8021q = true;
		rc = dsa_8021q_crosschip_bridge_join(ds, port, other_ds,
						     other_port,
						     &priv->crosschip_links);
		other_priv->expect_dsa_8021q = false;
		rc = dsa_8021q_crosschip_bridge_join(priv->dsa_8021q_ctx,
						     port,
						     other_priv->dsa_8021q_ctx,
						     other_port);
		if (rc)
			return rc;

		priv->expect_dsa_8021q = true;
		rc = dsa_8021q_crosschip_bridge_join(other_ds, other_port, ds,
						     port,
						     &other_priv->crosschip_links);
		priv->expect_dsa_8021q = false;
		rc = dsa_8021q_crosschip_bridge_join(other_priv->dsa_8021q_ctx,
						     other_port,
						     priv->dsa_8021q_ctx,
						     port);
		if (rc)
			return rc;
	}
@@ -1919,33 +1917,24 @@ static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		other_priv->expect_dsa_8021q = true;
		dsa_8021q_crosschip_bridge_leave(ds, port, other_ds, other_port,
						 &priv->crosschip_links);
		other_priv->expect_dsa_8021q = false;
		dsa_8021q_crosschip_bridge_leave(priv->dsa_8021q_ctx, port,
						 other_priv->dsa_8021q_ctx,
						 other_port);

		priv->expect_dsa_8021q = true;
		dsa_8021q_crosschip_bridge_leave(other_ds, other_port, ds, port,
						 &other_priv->crosschip_links);
		priv->expect_dsa_8021q = false;
		dsa_8021q_crosschip_bridge_leave(other_priv->dsa_8021q_ctx,
						 other_port,
						 priv->dsa_8021q_ctx, port);
	}
}

static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
{
	struct sja1105_private *priv = ds->priv;
	int rc, i;
	int rc;

	for (i = 0; i < SJA1105_NUM_PORTS; i++) {
		priv->expect_dsa_8021q = true;
		rc = dsa_port_setup_8021q_tagging(ds, i, enabled);
		priv->expect_dsa_8021q = false;
		if (rc < 0) {
			dev_err(ds->dev, "Failed to setup VLAN tagging for port %d: %d\n",
				i, rc);
	rc = dsa_8021q_setup(priv->dsa_8021q_ctx, enabled);
	if (rc)
		return rc;
		}
	}

	dev_info(ds->dev, "%s switch tagging\n",
		 enabled ? "Enabled" : "Disabled");
@@ -2149,12 +2138,12 @@ struct sja1105_crosschip_vlan {
	bool untagged;
	int port;
	int other_port;
	struct dsa_switch *other_ds;
	struct dsa_8021q_context *other_ctx;
};

struct sja1105_crosschip_switch {
	struct list_head list;
	struct dsa_switch *other_ds;
	struct dsa_8021q_context *other_ctx;
};

static int sja1105_commit_pvid(struct sja1105_private *priv)
@@ -2330,8 +2319,8 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,

	INIT_LIST_HEAD(&crosschip_vlans);

	list_for_each_entry(c, &priv->crosschip_links, list) {
		struct sja1105_private *other_priv = c->other_ds->priv;
	list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) {
		struct sja1105_private *other_priv = c->other_ctx->ds->priv;

		if (other_priv->vlan_state == SJA1105_VLAN_FILTERING_FULL)
			continue;
@@ -2341,7 +2330,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,
		 */
		if (!dsa_is_user_port(priv->ds, c->port))
			continue;
		if (!dsa_is_user_port(c->other_ds, c->other_port))
		if (!dsa_is_user_port(c->other_ctx->ds, c->other_port))
			continue;

		/* Search for VLANs on the remote port */
@@ -2376,7 +2365,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,
				    tmp->untagged == v->untagged &&
				    tmp->port == c->port &&
				    tmp->other_port == v->port &&
				    tmp->other_ds == c->other_ds) {
				    tmp->other_ctx == c->other_ctx) {
					already_added = true;
					break;
				}
@@ -2394,14 +2383,14 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,
			tmp->vid = v->vid;
			tmp->port = c->port;
			tmp->other_port = v->port;
			tmp->other_ds = c->other_ds;
			tmp->other_ctx = c->other_ctx;
			tmp->untagged = v->untagged;
			list_add(&tmp->list, &crosschip_vlans);
		}
	}

	list_for_each_entry(tmp, &crosschip_vlans, list) {
		struct sja1105_private *other_priv = tmp->other_ds->priv;
		struct sja1105_private *other_priv = tmp->other_ctx->ds->priv;
		int upstream = dsa_upstream_port(priv->ds, tmp->port);
		int match, subvlan;
		u16 rx_vid;
@@ -2418,7 +2407,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,
			goto out;
		}

		rx_vid = dsa_8021q_rx_vid_subvlan(tmp->other_ds,
		rx_vid = dsa_8021q_rx_vid_subvlan(tmp->other_ctx->ds,
						  tmp->other_port,
						  subvlan);

@@ -2493,11 +2482,11 @@ static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)

	INIT_LIST_HEAD(&crosschip_switches);

	list_for_each_entry(c, &priv->crosschip_links, list) {
	list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) {
		bool already_added = false;

		list_for_each_entry(s, &crosschip_switches, list) {
			if (s->other_ds == c->other_ds) {
			if (s->other_ctx == c->other_ctx) {
				already_added = true;
				break;
			}
@@ -2512,12 +2501,12 @@ static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)
			rc = -ENOMEM;
			goto out;
		}
		s->other_ds = c->other_ds;
		s->other_ctx = c->other_ctx;
		list_add(&s->list, &crosschip_switches);
	}

	list_for_each_entry(s, &crosschip_switches, list) {
		struct sja1105_private *other_priv = s->other_ds->priv;
		struct sja1105_private *other_priv = s->other_ctx->ds->priv;

		rc = sja1105_build_vlan_table(other_priv, false);
		if (rc)
@@ -2618,16 +2607,6 @@ static int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify)
	return rc;
}

/* Select the list to which we should add this VLAN. */
static struct list_head *sja1105_classify_vlan(struct sja1105_private *priv,
					       u16 vid)
{
	if (priv->expect_dsa_8021q)
		return &priv->dsa_8021q_vlans;

	return &priv->bridge_vlans;
}

static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
				const struct switchdev_obj_port_vlan *vlan)
{
@@ -2642,7 +2621,7 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
	 * configuration done by dsa_8021q.
	 */
	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
		if (!priv->expect_dsa_8021q && vid_is_dsa_8021q(vid)) {
		if (vid_is_dsa_8021q(vid)) {
			dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n");
			return -EBUSY;
		}
@@ -2762,38 +2741,26 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
	return sja1105_setup_8021q_tagging(ds, want_tagging);
}

static void sja1105_vlan_add(struct dsa_switch *ds, int port,
			     const struct switchdev_obj_port_vlan *vlan)
/* Returns number of VLANs added (0 or 1) on success,
 * or a negative error code.
 */
static int sja1105_vlan_add_one(struct dsa_switch *ds, int port, u16 vid,
				u16 flags, struct list_head *vlan_list)
{
	struct sja1105_private *priv = ds->priv;
	bool vlan_table_changed = false;
	u16 vid;
	int rc;

	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
		bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
		bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
	bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED;
	bool pvid = flags & BRIDGE_VLAN_INFO_PVID;
	struct sja1105_bridge_vlan *v;
		struct list_head *vlan_list;
		bool already_added = false;

		vlan_list = sja1105_classify_vlan(priv, vid);

		list_for_each_entry(v, vlan_list, list) {
	list_for_each_entry(v, vlan_list, list)
		if (v->port == port && v->vid == vid &&
			    v->untagged == untagged && v->pvid == pvid) {
				already_added = true;
				break;
			}
		}

		if (already_added)
			continue;
		    v->untagged == untagged && v->pvid == pvid)
			/* Already added */
			return 0;

	v = kzalloc(sizeof(*v), GFP_KERNEL);
	if (!v) {
		dev_err(ds->dev, "Out of memory while storing VLAN\n");
			return;
		return -ENOMEM;
	}

	v->port = port;
@@ -2802,6 +2769,40 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,
	v->pvid = pvid;
	list_add(&v->list, vlan_list);

	return 1;
}

/* Returns number of VLANs deleted (0 or 1) */
static int sja1105_vlan_del_one(struct dsa_switch *ds, int port, u16 vid,
				struct list_head *vlan_list)
{
	struct sja1105_bridge_vlan *v, *n;

	list_for_each_entry_safe(v, n, vlan_list, list) {
		if (v->port == port && v->vid == vid) {
			list_del(&v->list);
			kfree(v);
			return 1;
		}
	}

	return 0;
}

static void sja1105_vlan_add(struct dsa_switch *ds, int port,
			     const struct switchdev_obj_port_vlan *vlan)
{
	struct sja1105_private *priv = ds->priv;
	bool vlan_table_changed = false;
	u16 vid;
	int rc;

	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
		rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags,
					  &priv->bridge_vlans);
		if (rc < 0)
			return;
		if (rc > 0)
			vlan_table_changed = true;
	}

@@ -2819,29 +2820,50 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
	struct sja1105_private *priv = ds->priv;
	bool vlan_table_changed = false;
	u16 vid;
	int rc;

	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
		struct sja1105_bridge_vlan *v, *n;
		struct list_head *vlan_list;

		vlan_list = sja1105_classify_vlan(priv, vid);

		list_for_each_entry_safe(v, n, vlan_list, list) {
			if (v->port == port && v->vid == vid) {
				list_del(&v->list);
				kfree(v);
		rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans);
		if (rc > 0)
			vlan_table_changed = true;
				break;
	}

	if (!vlan_table_changed)
		return 0;

	return sja1105_build_vlan_table(priv, true);
}

static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
				      u16 flags)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	rc = sja1105_vlan_add_one(ds, port, vid, flags, &priv->dsa_8021q_vlans);
	if (rc <= 0)
		return rc;

	return sja1105_build_vlan_table(priv, true);
}

	if (!vlan_table_changed)
static int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	rc = sja1105_vlan_del_one(ds, port, vid, &priv->dsa_8021q_vlans);
	if (!rc)
		return 0;

	return sja1105_build_vlan_table(priv, true);
}

static const struct dsa_8021q_ops sja1105_dsa_8021q_ops = {
	.vlan_add	= sja1105_dsa_8021q_vlan_add,
	.vlan_del	= sja1105_dsa_8021q_vlan_del,
};

static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv,
						  bool *be_vlan)
{
@@ -3504,7 +3526,15 @@ static int sja1105_probe(struct spi_device *spi)
	mutex_init(&priv->ptp_data.lock);
	mutex_init(&priv->mgmt_lock);

	INIT_LIST_HEAD(&priv->crosschip_links);
	priv->dsa_8021q_ctx = devm_kzalloc(dev, sizeof(*priv->dsa_8021q_ctx),
					   GFP_KERNEL);
	if (!priv->dsa_8021q_ctx)
		return -ENOMEM;

	priv->dsa_8021q_ctx->ops = &sja1105_dsa_8021q_ops;
	priv->dsa_8021q_ctx->ds = ds;

	INIT_LIST_HEAD(&priv->dsa_8021q_ctx->crosschip_links);
	INIT_LIST_HEAD(&priv->bridge_vlans);
	INIT_LIST_HEAD(&priv->dsa_8021q_vlans);

+28 −21
Original line number Diff line number Diff line
@@ -5,37 +5,47 @@
#ifndef _NET_DSA_8021Q_H
#define _NET_DSA_8021Q_H

#include <linux/refcount.h>
#include <linux/types.h>

struct dsa_switch;
struct sk_buff;
struct net_device;
struct packet_type;
struct dsa_8021q_context;

struct dsa_8021q_crosschip_link {
	struct list_head list;
	int port;
	struct dsa_switch *other_ds;
	struct dsa_8021q_context *other_ctx;
	int other_port;
	refcount_t refcount;
};

struct dsa_8021q_ops {
	int (*vlan_add)(struct dsa_switch *ds, int port, u16 vid, u16 flags);
	int (*vlan_del)(struct dsa_switch *ds, int port, u16 vid);
};

struct dsa_8021q_context {
	const struct dsa_8021q_ops *ops;
	struct dsa_switch *ds;
	struct list_head crosschip_links;
};

#define DSA_8021Q_N_SUBVLAN			8

#if IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q)

int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
				 bool enabled);
int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled);

int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port,
				    struct dsa_switch *other_ds,
				    int other_port,
				    struct list_head *crosschip_links);
int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port,
				    struct dsa_8021q_context *other_ctx,
				    int other_port);

int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port,
				     struct dsa_switch *other_ds,
				     int other_port,
				     struct list_head *crosschip_links);
int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port,
				     struct dsa_8021q_context *other_ctx,
				     int other_port);

struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
			       u16 tpid, u16 tci);
@@ -56,24 +66,21 @@ bool vid_is_dsa_8021q(u16 vid);

#else

int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
				 bool enabled)
int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled)
{
	return 0;
}

int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port,
				    struct dsa_switch *other_ds,
				    int other_port,
				    struct list_head *crosschip_links)
int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port,
				    struct dsa_8021q_context *other_ctx,
				    int other_port)
{
	return 0;
}

int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port,
				     struct dsa_switch *other_ds,
				     int other_port,
				     struct list_head *crosschip_links)
int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port,
				     struct dsa_8021q_context *other_ctx,
				     int other_port)
{
	return 0;
}
+0 −2
Original line number Diff line number Diff line
@@ -164,8 +164,6 @@ 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);
extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
+0 −33
Original line number Diff line number Diff line
@@ -433,39 +433,6 @@ int dsa_port_vlan_del(struct dsa_port *dp,
	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
}

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)
		return err;

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

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);
}
EXPORT_SYMBOL(dsa_port_vid_del);

static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
{
	struct device_node *phy_dn;
Loading