Skip to content

Commit

Permalink
net: dsa: tag_8021q: add a context structure
Browse files Browse the repository at this point in the history
While working on another tag_8021q driver implementation, some things
became apparent:

- It is not mandatory for a DSA driver to offload the tag_8021q VLANs by
  using the VLAN table per se. For example, it can add custom TCAM rules
  that simply encapsulate RX traffic, and redirect & decapsulate rules
  for TX traffic. For such a driver, it makes no sense to receive the
  tag_8021q configuration through the same callback as it receives the
  VLAN configuration from the bridge and the 8021q modules.

- Currently, sja1105 (the only tag_8021q user) sets a
  priv->expect_dsa_8021q variable to distinguish between the bridge
  calling, and tag_8021q calling. That can be improved, to say the
  least.

- The crosschip bridging operations are, in fact, stateful already. The
  list of crosschip_links must be kept by the caller and passed to the
  relevant tag_8021q functions.

So it would be nice if the tag_8021q configuration was more
self-contained. This patch attempts to do that.

Create a struct dsa_8021q_context which encapsulates a struct
dsa_switch, and has 2 function pointers for adding and deleting a VLAN.
These will replace the previous channel to the driver, which was through
the .port_vlan_add and .port_vlan_del callbacks of dsa_switch_ops.

Also put the list of crosschip_links into this dsa_8021q_context.
Drivers that don't support cross-chip bridging can simply omit to
initialize this list, as long as they dont call any cross-chip function.

The sja1105_vlan_add and sja1105_vlan_del functions are refactored into
a smaller sja1105_vlan_add_one, which now has 2 entry points:
- sja1105_vlan_add, from struct dsa_switch_ops
- sja1105_dsa_8021q_vlan_add, from the tag_8021q ops
But even this change is fairly trivial. It just reflects the fact that
for sja1105, the VLANs from these 2 channels end up in the same hardware
table. However that is not necessarily true in the general sense (and
that's the reason for making this change).

The rest of the patch is mostly plain refactoring of "ds" -> "ctx". The
dsa_8021q_context structure needs to be propagated because adding a VLAN
is now done through the ops function pointers inside of it.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Sep 12, 2020
1 parent 7e092af commit 5899ee3
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 173 deletions.
3 changes: 1 addition & 2 deletions drivers/net/dsa/sja1105/sja1105.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
213 changes: 125 additions & 88 deletions drivers/net/dsa/sja1105/sja1105_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -1919,15 +1917,13 @@ 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);
}
}

Expand All @@ -1936,7 +1932,7 @@ static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
struct sja1105_private *priv = ds->priv;
int rc;

rc = dsa_8021q_setup(priv->ds, enabled);
rc = dsa_8021q_setup(priv->dsa_8021q_ctx, enabled);
if (rc)
return rc;

Expand Down Expand Up @@ -2142,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)
Expand Down Expand Up @@ -2323,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;
Expand All @@ -2334,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 */
Expand Down Expand Up @@ -2369,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;
}
Expand All @@ -2387,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;
Expand All @@ -2411,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);

Expand Down Expand Up @@ -2486,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;
}
Expand All @@ -2505,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)
Expand Down Expand Up @@ -2611,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)
{
Expand All @@ -2635,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;
}
Expand Down Expand Up @@ -2755,6 +2741,54 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
return sja1105_setup_8021q_tagging(ds, want_tagging);
}

/* 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)
{
bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = flags & BRIDGE_VLAN_INFO_PVID;
struct sja1105_bridge_vlan *v;

list_for_each_entry(v, vlan_list, list)
if (v->port == port && v->vid == vid &&
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 -ENOMEM;
}

v->port = port;
v->vid = vid;
v->untagged = untagged;
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)
{
Expand All @@ -2764,38 +2798,12 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,
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;
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) {
if (v->port == port && v->vid == vid &&
v->untagged == untagged && v->pvid == pvid) {
already_added = true;
break;
}
}

if (already_added)
continue;

v = kzalloc(sizeof(*v), GFP_KERNEL);
if (!v) {
dev_err(ds->dev, "Out of memory while storing VLAN\n");
rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags,
&priv->bridge_vlans);
if (rc < 0)
return;
}

v->port = port;
v->vid = vid;
v->untagged = untagged;
v->pvid = pvid;
list_add(&v->list, vlan_list);

vlan_table_changed = true;
if (rc > 0)
vlan_table_changed = true;
}

if (!vlan_table_changed)
Expand All @@ -2812,21 +2820,12 @@ 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);
vlan_table_changed = true;
break;
}
}
rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans);
if (rc > 0)
vlan_table_changed = true;
}

if (!vlan_table_changed)
Expand All @@ -2835,6 +2834,36 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
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);
}

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)
{
Expand Down Expand Up @@ -3497,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);

Expand Down
Loading

0 comments on commit 5899ee3

Please sign in to comment.